Scott Hanselman

Exploring FAKE, an F# Build System for all of .NET

March 14, '14 Comments [24] Posted in Open Source
Sponsored By

I've been orbiting around F# - but not really jumping in - for a while now. In fact, I looked at F# in 2008 almost 6 years ago and more recently talked to Richard Minerich and Phillip Trelford on Hanselminutes Podcast #311 "Why F#?"

Last week I looked at using ScriptCS, literally C# as a Scripting language, to drive browser automation. Today I'm exploring a make system called FAKE. It's F# Make, a build automation system similar to Make (which is 38 years old next month!) or Rake (which uses Ruby).

Fake is a Domain Specific Language that you can use without knowing F#, but if and when you outgrow it you can keep heading down the F# road. In all cases you've got all of .NET at your command.

Here's their Hello World example, a basic deploy script:

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


Target "Test" (fun _ ->
trace "Testing stuff..."
)

Target "Deploy" (fun _ ->
trace "Deploy stuff..."
)

"Test" // define the dependencies
==> "Deploy"

Run "Deploy"

Note that Deploy depends on Test.

FAKE uses F# but you can use it go build whatever. These are some C# OSS projects that use FAKE to build themselves:

FAKE isn't new, it's actually been around for 4 years with 55 contributors so far! It works not only on .NET but also on Linux and Mac under Mono - it works everywhere. Because it's all .NET you can use it on your Continuous Integration Servers like TeamCity or Travis CI.

image

Getting Started with FAKE

Check out their Calculator sample, a .NET project you'll extend to build itself with FAKE. Just download the zip, unblock it, and unzip. Then run build.bat from a developer command prompt.

The build.net is a bootstrapper. It could be powershell or a shell script if you like, of course.

@echo off
cls
"tools\nuget\nuget.exe" "install" "FAKE" "-OutputDirectory" "tools" "-ExcludeVersion"
"tools\FAKE\tools\Fake.exe" build.fsx
pause

This batch file uses NuGet to get FAKE, just as npm install restores node_modules, or gem gets ruby libraries. Then it calls Fake on the build.fsx file. Follow their Getting Started instructions to slowly expand the responsibilities of the build.fsx file.

FAKE has a lot of Helpers in their API documentation. Hundreds, and there's a whole community making others that you can call upon. For example, the built in FileHelper has things like CleanDir to remove files and subdirs.

Here we Clean before we build by making Clean a dependency of Default. BuildDir here is a property that's shared.

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

// Properties
let buildDir = "./build/"

// Targets
Target "Clean" (fun _ ->
CleanDir buildDir
)

Target "Default" (fun _ ->
trace "Hello World from FAKE"
)

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

// start build
RunTargetOrDefault "Default"

Jumping to the end of the tutorial, the syntax gets a little more tricky, butu once you get the |> format, it makes sense.

Some cool things to note and file away as interesting at in the script below.

  • the use of !! to include files
  • the use of -- to exclude a file spec after a !! operator
  • Zip is built-in as a helper and zips up the results of the build.
  • the options passed into NUnit
  • The dependency chain
  • #r for referencing .NET DLLs
// include Fake lib
#r "tools/FAKE/tools/FakeLib.dll"
open Fake

RestorePackages()

// Properties
let buildDir = "./build/"
let testDir = "./test/"
let deployDir = "./deploy/"

// version info
let version = "0.2" // or retrieve from CI server

// Targets
Target "Clean" (fun _ ->
CleanDirs [buildDir; testDir; deployDir]
)

Target "BuildApp" (fun _ ->
!! "src/app/**/*.csproj"
|> MSBuildRelease buildDir "Build"
|> Log "AppBuild-Output: "
)

Target "BuildTest" (fun _ ->
!! "src/test/**/*.csproj"
|> MSBuildDebug testDir "Build"
|> Log "TestBuild-Output: "
)

Target "Test" (fun _ ->
!! (testDir + "/NUnit.Test.*.dll")
|> NUnit (fun p ->
{p with
DisableShadowCopy = true;
OutputFile = testDir + "TestResults.xml" })
)

Target "Zip" (fun _ ->
!! (buildDir + "/**/*.*")
-- "*.zip"
|> Zip buildDir (deployDir + "Calculator." + version + ".zip")
)

Target "Default" (fun _ ->
trace "Hello World from FAKE"
)

// Dependencies
"Clean"
==> "BuildApp"
==> "BuildTest"
==> "Test"
==> "Zip"
==> "Default"

// start build
RunTargetOrDefault "Default"

You can do virtually anything and there's a great community out there to help.

Here's a more complex dependency chain with an optional parameter:

// Build order
"Clean"
==> "BuildApp"
==> "BuildTest"
==> "FxCop"
==> "NUnitTest"
=?> ("xUnitTest",hasBuildParam "xUnitTest") // runs the target only if FAKE was called with parameter xUnitTest
==> "Deploy"

There's a rich FAKE world out there with support for Octopus Deploy, all Unit Test systems, Xamarin's xpkg format and much more. Thanks to Steffan Forkmann for helping me explore this. ;)

Related Links


Sponsor: Big thanks to Red Gate for sponsoring the blog feed this week. Check out the Free Starter Edition of their release management tool! Deploy your SQL Server databases, .NET apps and services in a single, repeatable process with Red Gate’s Deployment Manager. Get started now with the free Starter Edition.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Saturday, March 15, 2014 2:18:40 AM UTC
Functional programming languages make great testbeds for DSLs, and build scripts are no exception. In the GNU world there's Guix, which implements a full-fledged, transactional package management system using Guile (GNU's implementation of the Scheme language).
Saturday, March 15, 2014 4:55:18 AM UTC
Interesting but the syntax looks very daunting to me.

My preferred build tool is psake. This is despite the fact I have a hate/love relationship with powershell :-)
Tim Murphy
Saturday, March 15, 2014 9:00:22 AM UTC
@Tim, check out Scott's link ( http://bugsquash.blogspot.ca/2010/11/migrating-to-fake.html ) for comparison screenshots of a Fake build script versus an MSBuild script. You might change your mind about the syntax!
Saturday, March 15, 2014 1:33:14 PM UTC
After having experimented with any number of language-based build systems (versus XML- or other textually-based declarative build systems), I can only hope that Visual Studio some day moves away from MSBuild and replaces it with something like this. Programming languages are so much more powerful than XML declarations.
moswald
Saturday, March 15, 2014 4:05:16 PM UTC
Looks promising. I'll have to try it out in place of NAnt the next time I have a build script to write. One note - I believe your dependency chain in your first example is backwards. Test doesn't depend on Default. Default has a dependency on Test. That was a little confusing the first time I read it and looked at the code, but the Getting Started link set me straight.
Saturday, March 15, 2014 4:12:24 PM UTC
Using a real programming language for a build system is certainly superior to a DSL, however I'd prefer one in C# so I don't have to learn yet another new language syntax.

Once Roslyn is released I hope MS moves towards allowing C# scripts everywhere- pre/post-build scripts (which are still BATCH files and not even Powershell), and as an alternative for MSBuild's syntax.
Sam
Saturday, March 15, 2014 7:10:34 PM UTC
@Sam ... Conversely I think ScriptCS is misguided and should be killed off. In the same way that TypeScript should be killed off. Functional programming is here and it is better in every way. Everything is composable which is a trait that all programmers seek to achieve.
nbe
Saturday, March 15, 2014 11:59:16 PM UTC
I think you have a typo after the first deploy script: Deploy probably depends on Test, not the other way around.
Tomi
Sunday, March 16, 2014 12:55:46 AM UTC
I find that I really like simple node.js scripts for build/deploy... gulp helps with this, and npm scripts are awesome too. I've even been using node scripts for my resources in .Net projects for a couple years now. Though VS has gotten a lot better.

There's something to be said for an execution environment that you can run as a script, and from there using a language that is as pervasive as JS has become. That's just my $.02
Sunday, March 16, 2014 5:32:24 AM UTC
There's an error in the first snippet. 'f' should be 'fun'.
Luke
Sunday, March 16, 2014 12:10:59 PM UTC
Interesting but the syntax looks very daunting to me. - Tim

I'd prefer one in C# so I don't have to learn yet another new language syntax. - Sam

Sam and Tim. Learn F# now and 6 months time your programming world will be 87% more rainbows and unicorns. I back this statement with my personal guarantee. If you are not satisfied, I will send you each one chocolate bar of your choice from the UK.

It will not be easy at the start with and you will think everything is weird and clearly wrong. But soon enough, you will experience elation. After that, even if you remain a C# programmer, your C# will be much, much better.

You may enjoy Domain Driven Design, F# and Types and then want to play with Try F#.
Sunday, March 16, 2014 3:14:41 PM UTC
Hi Scott,

Good write-up but note one mistake in the description of the first code sample:

You say "Note that Test depends on Deploy."

It should be the other way round - the dependency chain works backwards. Deploy depends on Test (and thus why you Run "Deploy" and not "Test").

i.e. in the last example:
// Dependencies
"Clean"
==> "BuildApp"
==> "BuildTest"
==> "Test"
==> "Zip"
==> "Default"

You just run "Default" as it depends on "Zip" which depends on "Test" etc etc.

Anyway, you get the picture :)

Dave
Monday, March 17, 2014 3:39:10 AM UTC
@Ben thanks for the references, quite timely. Soon I will be starting a new project project and intend on researching f# for ddd & cqrs.
Tim Murphy
Monday, March 17, 2014 11:19:56 AM UTC
The article would have been better for me if it included a review comparing FAKE with calling MSBuild from a command script (.cmd). The project file(s) which VS uses are also MS Build files. So with a simple command you can execute targets (such as clean, debug, release x64) that are defined in the file. The dependencies are already defined in the project file. Need a special build? Create another project file using VS. Need to copy files or run NAnt after a specific build task? Define your own tasks. Want the build exceptions to be highlighted in florescent blue? There's an MSBuild command line option for that.

I must be missing something because my take from the article FAKE seems to be a solution to a problem that's been solved for a long time. I accept it's me being dim-witted and I can't see the benefit which is why a comparative review would have been helpful.

The message seems to be 'do it in F# because then it's composable'. But is that really justification enough not to use a tool like MS Build which is tried and trusted and supported by Microsoft?
Bill Seddon
Monday, March 17, 2014 1:36:41 PM UTC
I just started using FAKE for build scripts a few weeks ago as it provided a way for me to finally learn F# and not have the knowledge leak out my feeble little mind like it did the last 4 times I tried to learn it. That plus FAKE has such a vast library of helpers, I've switched from psake to FAKE.
Damian Reeves
Monday, March 17, 2014 2:02:57 PM UTC
@Bill
One difference is that you have the full f# typesystem at your hand with visual studio helping you.
Another is that you have fullest control and very easy extensibility at your hands. We use FAKE for a lot of projects and some of them have pretty complicated build/testing/deployment steps, but the f# syntax keeps the build-scripts clean and concise.
We created a lot of custom helpers for many different tasks specially for our environment.
Need some extra functionality? just plug in some Nuget packages...
It also works pretty nicely with TeamCity.
pj
Monday, March 17, 2014 5:27:34 PM UTC
Thanks for the corrections and info, everyone!
Monday, March 17, 2014 6:22:53 PM UTC
@BenTaylor I don't disagree that functional programming is elegant and can be extremely powerful. I'm a huge user of LINQ in C# and have added many of my own functional extensions in my codebase. I'd like to learn at F# at some point and see what ideas it gives me, but that being said, I don't have the time to commit to doing that at this point and certainly not just for the sake of one specific tool (a build system).
Sam
Monday, March 17, 2014 9:57:27 PM UTC
@ nbe

That's quite some statement, and a little silly. You might as well say C++, Java, Python, Ruby, PHP or any other OO language you care to name should be killed off. I'm quite comfortable with C# and can do everything I need to as I should imagine are most of those that use the languages I've just referred to.
Matthew Blott
Wednesday, March 19, 2014 4:13:39 AM UTC
I've used MSBuild, PSake, Powershell, but couldn't get the hang of it. I just find it easier to have a dedicated project in the solution for the deployment. Advantages:
1. I can use whatever language I like most, with all the intellisense and fancy stuff the IDE provides.
2. The whole solution will be built by MSBuild just like any other standard solution, so no additional configuration on the server.
3. Once the solution is built, I add one more step in the CI configuration to run the deploy project, which knows where to find the files to customize, obfuscate, minify, etc.. Even running node.js tasks are much simpler using Process.Start so that I can debug it without having to constantly running test builds.
4. I can have configuration specific task using preprocessors and debug it right inside the IDE.
Van
Wednesday, March 19, 2014 6:04:57 PM UTC
@Sam of course, I am not to know if you have the time or not :) When you do have some time, using FAKE is a great way to gently introduce and learn F#. I use FAKE and other great F# supporting projects like TickSpec alongside my existing C# projects. Rather than thinking it's just a build system, I thought it's another opportunity to improve my build process AND learn F#.
Thursday, March 20, 2014 11:08:45 PM UTC
@Sam Since I have discovered FAKE, and because the language I master the best is C#, I try to do a build automation tool with Scriptcs (use Roslyn).

And after just quite some hours of work, I have something that can do what I have done painfully with msbuild.

It's easily extensible and you could use all the .Net Framework!

Here it is :
https://github.com/pmiossec/Buildcs
Philippe
Friday, March 21, 2014 4:51:23 AM UTC
If you enjoy TFS, you can use FAKE to drive TeamBuild workflows using Cameron Taggart's <a href=https://github.com/ctaggart/SourceLink">SourceLink</a> project. SourceLink is a tool to update .pdb files with links to web-based source control solutions like GitHub and Bitbucket and includes support for FAKE and TFS. Cameron helped me get this working for a project in Visual Studio Online. See his article Code your TFS builds in F# instead of XAML. It's amazing, and I LOVE it. I find FAKE so much easier to manage than the WF XAML necessary for TeamBuild.
Friday, March 21, 2014 3:22:00 PM UTC
@Sam I agree 100% with @BenTaylor and you'll come around.

F# is a far better language for tests than C#.

Hence learning some F# in the context of FAKE AND TickSpec etc is something you won't regret.

It may take you a year or two to realize it though (I managed to wait more than a year despite knowing I really should be using F# for testing) :-

( See a presentation summarising my experience and the power of F# wrt testing at: http://slideshare.net/bartelink/testing-cinfdublinaltnet2013 )
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.