23 February 2015 FAKE, Build, MSBuild Robert Muehsig

MSBuild is the default build engine in the .NET world, but if you do more with MSBuild you will find yourself in a XML-mess, so that’s the reason why I tried out FAKE. You can find a couple of MSBuild related posts here.

Enough from MSBuild, now meet FAKE:

  • A DSL for build tasks (e.g. build following projects, copy stuff, deploy stuff etc.)
  • Active community, OSS & written in F#
  • You don’t need to learn F# for using it (at least the basic tasks are easy)
  • You can get FAKE via NuGet
  • Before we begin you might want to check out the actual website of FAKE

Our goal: Building, running tests, package NuGet Packages etc.

Let’s start with the basics: Building

I created a new console application and downloaded the NuGet Command-line utility:

x

The build.fsx script

So, now we get in touch with a very simple build script. (Scroll down to read the description of each section)

// include Fake lib
#r "packages/FAKE/tools/FakeLib.dll"
open Fake

RestorePackages()

// Properties
let artifactsDir = @".\artifacts\"
let artifactsBuildDir = "./artifacts/build/"

// Targets
Target "Clean" (fun _ ->
    trace "Cleanup..."
    CleanDirs [artifactsDir]
)

Target "BuildApp" (fun _ ->
   trace "Building App..."
   !! "**/*.csproj"
     |> MSBuildRelease artifactsBuildDir "Build"
     |> Log "AppBuild-Output: "
)

Target "Default" (fun _ ->
    trace "Default Target invoked."
)

// Dependencies
"Clean"
  ==> "BuildApp"
  ==> "Default"

// start build
RunTargetOrDefault "Default"

The top line is a reference to the FAKE helper libraries (included via the #r keyword) and a ‘using’:

#r "packages/FAKE/tools/FakeLib.dll"
open Fake

The next important line is the RestorePackages()-line. With this line all NuGet packages will be restored from NuGet.org or your own NuGet-Repository. If you check-in your NuGet packages then you don’t need this.

Then I define some general properties, e.g. my build output folders:

let artifactsDir = @".\artifacts\"
let artifactsBuildDir = "./artifacts/build/"

Now the interesting part: Like in MSBuild you define a couple of “Targets”:

Target "Clean" (fun _ ->
    trace "Cleanup..."
    CleanDirs [artifactsDir]
)

Target "BuildApp" (fun _ ->
   trace "Building App..."
   !! "**/*.csproj"
     |> MSBuildRelease artifactsBuildDir "Build"
     |> Log "AppBuild-Output: "
)

Target "Default" (fun _ ->
    trace "Default Target invoked."
)

The first target cleans the artifactsDir via the FileHelper. The next one is responsible for calling MSBuild (which builds the actual project) (via MSBuildHelper). This is maybe the most awkward part of the build script for a C# dev, but this “pipelining” is part of F#’s nature. I can’t explain it right now, but it works (I didn’t lie when I said that I have not a tiny bit of knowledge about F#).

At the end of the file you define the dependency graph for each target:

"Clean"
  ==> "BuildApp"
  ==> "Default"

After that we invoke the ‘Default’ target with this line:

RunTargetOrDefault "Default"

Invoke the build.fsx

Now that we have our project in place and a valid FAKE script we need to invoke the script. The easiest option on Windows is a batch file like this:

@echo off
cls
".nuget\NuGet.exe" "Install" "FAKE" "-OutputDirectory" "packages" "-ExcludeVersion"
"packages\FAKE\tools\Fake.exe" build.fsx

First we need to load FAKE via NuGet and after that we invoke our build.fsx file with Fake.exe.

Now you or any build machine can invoke the batch file and start the build!

Our first FAKE script!

The output is very well formatted and should explain the mechanics behind it good enough:

Package "FAKE" is already installed.
Building project with version: LocalBuild
Shortened DependencyGraph for Target Default:
<== Default
<== BuildApp
<== Clean

The resulting target order is:
- Clean
- BuildApp
- Default
Starting Target: Clean
Cleanup...
Creating C:\Users\Robert\Documents\Visual Studio 2013\Projects\LetsUseFake\artifacts\
Finished Target: Clean
Starting Target: BuildApp (==> Clean)
Building App...
Building project: C:\Users\Robert\Documents\Visual Studio 2013\Projects\LetsUseFake\LetsUseFake\LetsUseFake.csproj
... (standard MSBuild output, nothing special...)

Time Elapsed 00:00:01.24
AppBuild-Output: C:\Users\Robert\Documents\Visual Studio 2013\Projects\LetsUseFake\artifacts\build\LetsUseFake.exe
AppBuild-Output: C:\Users\Robert\Documents\Visual Studio 2013\Projects\LetsUseFake\artifacts\build\LetsUseFake.exe.config
AppBuild-Output: C:\Users\Robert\Documents\Visual Studio 2013\Projects\LetsUseFake\artifacts\build\LetsUseFake.pdb
Finished Target: BuildApp
Starting Target: Default (==> BuildApp)
Default Target invoked.
Finished Target: Default

---------------------------------------------------------------------
Build Time Report
---------------------------------------------------------------------
Target     Duration
------     --------
Clean      00:00:00.0032838
BuildApp   00:00:01.5844651
Default    00:00:00.0007512
Total:     00:00:01.6153969
Status:    Ok
---------------------------------------------------------------------

The resulting build artifact:

x

You can find the complete sample on GitHub


Written by Robert Muehsig

Software Developer - from Saxony, Germany - working on primedocs.io. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de