Exploring FAKE, an F# Build System for all of .NET
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:
- Octokit.NET by GitHub
- Portable.Licensing
- Machine.Fakes
- NSubstitute (https://github.com/nsubstitute/NSubstitute/pull/138 WIP)
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.
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
- Integrate F# Make with TFS N Visual Studio
- Migrating to FAKE - Why?
- FAKE - F# Make - A DSL for build tasks
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.
About Newsletter
My preferred build tool is psake. This is despite the fact I have a hate/love relationship with powershell :-)
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.
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
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#.
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
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?
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.
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.
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.
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
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.