Reputation: 101
I'm just learning F#, and setting up a FAKE build harness for a hello-world-like application. (Though the phrase "Hell world" does occasionally come to mind... :-) I'm using a Mac and emacs (generally trying to avoid GUI IDEs by preference).
After a bit of fiddling about with documentation, here's how I'm invoking the F# compiler via FAKE:
let buildDir = @"./build-app/" // Where application build products go
Target "CompileApp" (fun _ -> // Compile application source code
!! @"src/app/**/*.fs" // Look for F# source files
|> Seq.toList // Convert FileIncludes to string list
|> Fsc (fun p -> // which is what the Fsc task wants
{p with //
FscTarget = Exe //
Platform = AnyCpu //
Output = (buildDir + "hello-fsharp.exe") }) // *** Writing to . instead of buildDir?
) //
That uses !!
to make a FileIncludes
of all the sources in the usual way, then uses Seq.toList
to change that to a string list
of filenames, which is then handed off to the Fsc
task. Simple enough, and it even seems to work:
...
Starting Target: CompileApp (==> SetVersions)
FSC with args:[|"-o"; "./build-app/hello-fsharp.exe"; "--target:exe"; "--platform:anycpu";
"/Users/sgr/Documents/laboratory/hello-fsharp/src/app/hello-fsharp.fs"|]
Finished Target: CompileApp
...
However, despite what the console output above says, the actual build products go to the top-level directory, not the build directory. The message above looks like the -o
argument is being passed to the compiler with an appropriate filename, but the executable gets put in .
instead of ./build-app/
.
So, 2 questions:
Upvotes: 2
Views: 333
Reputation: 101
This, or a very similar problem, was reported in FAKE issue #521 and seems to have been fixed in FAKE pull request #601, which see.
As is apparently well-known to everyone but me, the F# compiler as implemented in FSharp.Compiler.Service
has a practice of skipping its first argument. See FSharp.Compiler.Service/tests/service/FscTests.fs
around line 127, where we see the following nicely informative comment:
// fsc parser skips the first argument by default;
// perhaps this shouldn't happen in library code.
Whether it should or should not happen, it's what does happen. Since the -o
came first in the arguments generated by FscHelper
, it was dutifully ignored (along with its argument, apparently). Thus the assembly went to the default place, not the place specified.
The temporary workaround was to specify --out:destinationFile
in the OtherParams
field of the FscParams
setter in addition to the Output
field; the latter is the sacrificial lamb to be ignored while the former gets the job done.
The longer term solution is to fix the arguments generated by FscHelper
to have an extra throwaway argument at the front; then these 2 problems will annihilate in a puff of greasy black smoke. (It's kind of balletic in its beauty, when you think about it.) This is exactly what was just merged into the master by @forki23:
// Always prepend "fsc.exe" since fsc compiler skips the first argument
let optsArr = Array.append [|"fsc.exe"|] optsArr
So that solution should be in the newest version of FAKE (3.11.0).
The answers to my 2 questions are thus:
More to the point: the actual misunderstanding was that I should have checked the FAKE issues and pull requests to see if anybody else had reported this sort of thing, and that's what I'll do next time.
Upvotes: 3