As an example, I'm using the following preprocessing directive
#if COMPILED
let context = Sql.GetDataContext(ConfigurationManager.ConnectionStrings.[AppDB].ConnectionString)
#else
let context = Sql.GetDataContext()
#endif
so that I'm able to test a dll library from F# interactive, to give you an idea
#I #"bin\Debug"
#r #"import.dll"
#r #"FSharp.Data.SqlProvider.dll"
#load "Library1.fs"
open SqlLib
open SqlDB
// Define your library scripting code here
let book = "My Company"
let db = DB()
db.analysts book |> Array.iter (printfn "%A")
because, of course, in the example above ConfigurationManager would not be usable from the scripting engine, so I need to implement a change at preprocessing time.
More generally, where can I find the documentation or a at least a list of all the available, standard symbols, that are already automatically defined, including COMPILED and so on...?
From Compiler directives F#:
Symbols that you use in the if directive must be defined by the command line or in the project settings; there is no define preprocessor directive...
When VERSION1 is defined by using the -define compiler option, the code between the #if directive and the #else directive is activated. Otherwise, the code between #else and #endif is activated.
So you can defined your own preprocessor directives when compile code. If you work with .NET Core, inside fsproj or csproj you can define these symbols as:
<PropertyGroup Condition="'$(TargetFramework)' != 'net40'">
<DefineConstants>NET45</DefineConstants>
</PropertyGroup>
dotnet sends them to fsc. If you curious what symbols are defined by default, you can investigate fsc source code
I found COMPILED and INTERACTIVE there.
Related
When using FSharp.Compiler.SourceCodeServices.FSharpChecker.ParseAndCheckFileInProject from FSharp Compiler Service for whole-project analysis, how can NuGet dependencies be included for symbolic resolution?
In the project under analysis, some project file (*.fsproj) contains some <PackageReference> element, e.g. <PackageReference Include="NodaTime" Version="3.0.3" />, and some source file contains an open statement open NodaTime and somewhere some symbol DateInterval refering to NodaTime.DateInterval. For me, the FSharp Compiler Service seems to fail to resolve DateInterval to NodaTime.DateInterval in that source file.
What I am currently doing is, summarized:
let checker = FSharpChecker.Create()
let options: FSharpProjectOptions = { ... }
checker.ParseAndCheckFileInProject (...)
Given that I use ParseAndCheckFileInProject, what is necessary for checker and/or options to consider symbols coming from NuGet dependencies?
I think you have to extract the FSharpProjectOptions from the .fsproj project file. For old-style .NET Framework projects, there used to be a method called ProjectCracker.GetProjectOptionsFromProjectFile that would do this, but it has been replaced with Dotnet.ProjInfo, which also supports .NET Core. See this SO question for details.
We're multiple persons working on the same F# project. Some use MacOS and Visual Studio Code together with Ionide while others use Windows with Visual Studio. In the F#-code, we need to access some files, but MacOS uses / to specify paths while Windows uses \. In F#, how can we make something like:
#if OS_WINDOWS
let path = "path\to\file.txt"
#elif OS_MAC
let path = "path/to/file.txt"
#endif
There is no built-in pre-defined symbol to indicate what operating system you are compiling for. When you use .NET, you generally use the same compiled assembly on all operating systems, so this is not something that you can reasonably do in a pre-processor anyway.
You can check what OS are you running on at runtime using System.Environment:
open System
let path =
if Environment.OSVersion.Platform = PlatformID.Win32NT then #"path\to\file.txt"
else #"path/to/file.txt"
That said, if your only concern is slashes and backslashes in a path, you can just use:
let path = System.IO.Path.Combine("path", "to", "file.txt")
I have a fsx file where I try to do this
#if DEV
#load "MyFile.fs"
#endif
// Later in the file
#if DEV
callSomethingFromMyFile()
#endif
The callSomethingFromMyFile() works if I remove the #if DEV ... #endif around the #load directive.
I realize that this might be a weird thing, but it is because I'm using fable to compile to F# to js, and if I want to exclude a file when production "build" to reduce js file size.
In regular F# scripts it is possible, it seems like fable doesn't handle it.
To verify that it works in regular F# I created the following to files:
MyModule.fs:
module MyModule
type A = {b: string}
script.fsx:
#if DEV
#load "./MyModule.fs"
#endif
#if DEV
open MyModule
printfn "Hello A: %A" {b = "yolo"}
#endif
printfn "Done"
running fsharpi --define:DEV --exec script.fsx works as expected. I expect fsi on Windows to work as well.
Ive fixed this in the following PR: https://github.com/fable-compiler/Fable/pull/429
You can currently pass defines to the interactive checker its just that for fsx files this was not currently being done.
"
Some directives are available when you are executing scripts in F# Interactive that are not available when you are executing the compiler. The following table summarizes directives that are available when you are using F# Interactive.
"
https://learn.microsoft.com/en-us/dotnet/articles/fsharp/tutorials/fsharp-interactive/index#differences-between-the-interactive-scripting-and-compiled-environments
The table then lists #load amongst others. Not entirely clear that text (compiler vs. preprocessor), but it also kind of makes sense...
Is there any way to share a variable by including a fsx script within another fsx script.
e.g script buildConsts.fsx contains
let buildDir = "./build/"
I want to reference this in other build scripts e.g.
#load #".\buildConsts.fsx"
let testDlls = !! (buildDir + "*Test*.dll")
When I attempt to run the script the 'buildDir' variable the script fails to compile.
This is a fairly common approach that is used with tools such as MSBuild and PSAKE to modularise scripts. Is this the correct approach with FAKE ?
What you're doing should work - what exactly is the error message that you're getting?
I suspect that the problem is that F# automatically puts the contents of a file in a module and you need to open the module before you can access the constants. The module is named based on the file name, so in your case buildConsts.fsx will generate a module named BuildConsts. You should be able to use it as follows:
#load #".\buildConsts.fsx"
open BuildConsts
let testDlls = !! (buildDir + "*Test*.dll")
You can also add an explicit module declaration to buildconsts.fsx, which is probably a better idea as it is less fragile (won't change when you rename the file):
moule BuildConstants
let buildDir = "./build/"
In my project I am opening a file with some relative path to the executable. I was trying to test my code in the F# Interractive window, but it seems to run from a completely different path. How can I change the path/ make it run from the same path as the project?
I think __SOURCE_DIRECTORY__ identifier could help here.
You should use compiler directives to separate between using F# Interactive and compiling F# project.
#if INTERACTIVE
let path = __SOURCE_DIRECTORY__ + some_relative_path
#else
let path = another_relative_path
#endif
You could set the current working directory when running in FSI:
#if INTERACTIVE
System.IO.Directory.SetCurrentDirectory("<project_path>")
#endif