sgr
sgr

Reputation: 101

FAKE Fsc task is writing build products to wrong directory

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:

  1. Is this a reasonable way to be invoking the F# compiler in a FAKE build harness?
  2. What am I misunderstanding that is causing the build products to go to the wrong place?

Upvotes: 2

Views: 333

Answers (1)

sgr
sgr

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.

Explanation of the Problem

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.

Solutions

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:

  1. Yes, this appears to be a reasonable way to invoke the F# compiler.
  2. I didn't misunderstand anything; it was just a bug and a fix is in the pipeline.

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

Related Questions