Being a .NET developer, I’m sure one of your pain points throughout the development process in any project has been trying to setup automated building, testing, and even deploying. However, build tools have been evolving much like other development tools and have been getting much, much better. I’m sure most of you have heard of Rake and there are a ton of other tools that can be used as well. One of those tools we’ll be using here is FAKE – F# Make. I do believe, however, that the concepts you can learn here from FAKE can translate to other MAKE-like build tools such as Rake or Cake if you do decide to go with this similar concept.
In this post, we’ll introduce FAKE and show how to set it up in your project as well as a basic FAKE build script. The full source is available on GitHub and will point to all the code in this section.
Introduction to FAKE
While it’s not crucial to have a deep knowledge of F#, some aspects of it can be a bit helpful, such as being able to tap into the community for support and leveraging functional concepts available in F#. While a good bit of the F# projects use it for their building needs, you may recognize a few other projects that use it as well.
Personally, I’ve decided to use FAKE to create build scripts mainly due to two reasons: I really enjoy F# and FAKE does have some specific API calls for building Xamarin projects that we’ll be exploring in more detail throughout this blog series.
There are a few other nice reasons to use FAKE as well:
– Since it is F#, the type system can find some mistakes at compile time of the script instead of in the middle of a build.
– It includes helper functions for several things including packaging and deploying Azure Web Jobs and Azure Cloud Services, support for FxCop, compiling typescript applications, and just tons of other useful stuff.
Setting up FAKE
Honestly, this may be the most cumbersome part of putting FAKE into place as you would need to have a bash and/or command file to launch FAKE. Before that, however, it would need to check Nuget to see if FAKE itself is installed and restore it if it isn’t. The main reason is that it this process requires a
.nuget folder in your root project location (which is where we’ll place our scripts, as well). Probably not the best way to do it, but I just tend to just copy it from another project and paste it in. Below is how the root of our demo project looks and you’ll notice these items are right there at the root.
It’s worthwhile to note that you may see a project have
.paketfolder being used instead of a
.nuget. Paket is another way to manage packages. It is used quite a lot in the F# community and I would recommend giving it a look. One thing to note about what Paket solves is that it doesn’t put the file version number of packages in the file system which can cause some version control issues1. Since this series is mostly about FAKE we will continue to use NuGet for our demo.
For FAKE, what you tend to see a lot is that there are one or two platform specific scripts – a shell script for Mac/Linux and a command script for Windows. Depending on what environment the author is primarily working in you will see one or both of them. I’ll show the basic scripts for each here, but if you dig around the other projects you’ll get a sense that these can get a bit complex.
For both of these initial scripts, all we’re doing is seeing if FAKE is installed. If not, it will use NuGet to restore the package in the
packages directory. Then it will run FAKE and call our
build.fsx script. First up is the
build.sh file for Mac and Linux platforms.
if [ ! -f packages/FAKE/tools/FAKE.exe ]; then mono .nuget/NuGet.exe install FAKE -OutputDirectory packages -ExcludeVersion -Prerelease fi mono packages/FAKE/tools/FAKE.exe build.fsx [email protected]
And here is a
build.cmd file for Windows. This can be run in
cmd.exe or within a PowerShell prompt.
@echo off cls if not exist packagesFAKEtoolsFake.exe ( .nugetnuget.exe install FAKE -OutputDirectory packages -ExcludeVersion ) if not exist packagesNUnit.Runnerstoolsnunit-console.exe ( .nugetnuget.exe install NUnit.Runners -OutputDirectory packages -ExcludeVersion ) packagesFAKEtoolsFAKE.exe build.fsx %* 2>&1
The FAKE Script
What’s in it?
The FAKE script, usually named as
build.fsx is the script that will do all the work and will host all of our build targets. I mentioned earlier in this post that it’s not real important to know a lot of F#, but I’ll give a quick rundown of what’s going on in the below FAKE script in case any of it looks a bit weird when coming from a mainly C# background.
#r "packages/FAKE/tools/FakeLib.dll" // 1 open Fake // 2 Target "Test" (fun _ -> // 3 trace "Testing stuff..." ) Target "Build" (fun _ -> trace "Heavy build action" ) "Build" // 4 ==> "Test" RunTargetOrDefault "Test" // 5
#rtells the script to reference the FAKE library assembly.
openkeyword is the same as the
usingkeyword in C#; we are opening the FAKE library so it can be used within the script.
Targetmethod is where we define all of our targets. The string parameter is the name of the target and the second parameter takes in a lambda. The
fun _ -> ...syntax is similar to
() => ...in C# with the
_being a wildcard parameter in the lambda since we aren’t really using it.
- This defines our target dependencies. The
Testtarget will depend on the
==>operator is an F# custom operator2 that is defined in the FAKE library. You can imagine how easy this can be to set up more complex builds.
RunTargetOrDefaulttarget will run the default that is specified in the script or a specified target that you have specified in the script. You can specify as many targets as you need and run them separately when executing the build script (e.g.
Executing the script
To run our build script, all we would need to do is to run our
build.cmd file. Here’s what the above script looks like after running.
And that’s all there is to setting up our build script! In our next post we’ll look at how to build our PCL, iOS, and Android projects within our FAKE script as well as how to run different targets.
- For more info on that, Paket has a nice explanation on their FAQ. ↩
- For more info on F# custom operators take a look at the F# For Fun and Profit article on defining functions and reference the
Defining new operatorssection. ↩