My favorites | Sign in
Project Logo
                
Show all Featured wiki pages:
FxCop

What is "FAKE - F# Make"?

Introduction

Modern build automation systems are not limited to simply recompile programs if source code has changed. They are supposed to get the latest sources from a source code management system, build test databases, run automatic tests, check guidelines, create documentation files, install setup projects and much more. Some companies are even deploying virtual machines, which are created during a nightly build process. In order to simplify the writing of such build scripts and to provide reusability of common tasks most build automation systems are using a domain-specic language (DSL). These tools can be divided into tools using external DSLs with a custom syntax like make, tools using external DSLs with an XML based syntax like MSBuild or Apache Ant and tools using internal DSLs which are integrated in a host language like Rake, which uses Ruby.

FAKE - An internal DSL

"FAKE - F# Make" is a build automation system, which is intended to combine the advantages of the above mentioned tools but to provide a better tooling support. Due to its integration in F#, all benets of the .NET Framework and functional programming can be used, including the extensive class library, powerful debuggers and integrated development environments like Visual Studio 2008 or SharpDevelop, which provide syntax highlighting and code completion.

The new language was designed to be succinct, typed, declarative, extensible and easy to use. For instance custom build tasks can be added simply by referencing .NET assemblies and using the corresponding classes.

Articles

Main Features

Using FAKE

Targets

Targets are the main unit of work in a Fake script. Targets have a name (usually given as a symbol or a string) and a action (given as a code block).

// This target cleans the build and deploy folders
Target "Clean" (fun () -> 
  CleanDir "./build/"
  CleanDir "./deploy/"
)

Dependencies

You can define prerequisites for tasks:

// Target Default is dependent from target Clean and BuildApp
// Fake will run these targets before Default
"Default"  <== ["Clean"; "BuildApp"]

Running targets

You can execute targets with the "run"-command:

// Exceutes Default target
run "Default"

Final targets

Final target can be used for TearDown functionality. These targets will be executed even if the build fails but have to be activated via ActivateFinalTarget().

// FinalTarget will be excuted even if build fails
FinalTarget "CloseSomePrograms" (fun () ->
  // close stuff and release resources
)
// activate FinalTarget somewhere during build
ActivateFinalTarget "CloseSomePrograms"

UnitTests

NUnit

Target "NUnitTest" (fun () -> 
  let testAssemblies =
    !+ (testDir + @"\Test.*.dll")
      |> Scan

  NUnit 
    (fun p -> 
      {p with 
         ToolPath = nunitPath; 
         DisableShadowCopy = true; 
         OutputFile = testDir + @"TestResults.xml"}) 
    testAssemblies  
)

xUnit.net

Target "xUnitTest" (fun () -> 
  let testAssemblies =
    !+ (testDir + @"\Test.*.dll")
      |> Scan

  xUnit
    (fun p ->
       {p with
           ShadowCopy = false;
           HtmlPrefix = testDir})
    testAssemblies 
)

FileSets

Fake uses similar include patterns as NAnt and MSBuild.

Includes

// Includes all `*`.csproj-Files under \src\app
// Includes with !+ operator
!+ "src/app/**/*.csproj"
// Includes all `*`.csproj-Files under \src\app and \test
// Multiple includes with ++ operator
!+ "src/app/**/*.csproj"
  ++ "test/**/*.csproj"

Excludes

// Includes all files under \src\app
// but excludes `*`.zip files
!+ "src/app/**/*.*"
  -- "*.zip"

Scan vs. ScanImmediately

Fake provides two scan methods: Scan and ScanImmediately.

Scan is a lazy method and evaluates the FileSet as late as possible ("on-demand"). If the FileSet is used twice, it will be reevaluated.

The following code defines a lazy FileSet:

// Includes all `*`.csproj-Files under \src\app
// lazy scan
let apps = 
  !+ "src/app/**/*.csproj"
    |> Scan

ScanImmediately scans the FileSet immediatly at time of its definition and memoizes it:

// Includes all files under \src\app but excludes `*`.zip files
// eager scan ==> All files memoized at the time of this definition
let files = 
  !+ "src/app/**/*.csproj"
    -- "*.zip"
    |> ScanImmediately

Predefined Tasks

FAKE provides a lot of predefined tasks. Some of them are listed below:

Sample script

This sample script

You can read Getting started with FAKE to build such a script.

#light

// Include FAKE libraries
#I "tools\FAKE"
#r "FakeLib.dll"
open Fake 

// properties 
let projectName = "MyProject"
let version     = "0.1" 
let buildDir    = "./build/"
let deployDir   = "./deploy/"
let nunitPath   = "./Tools/NUnit/bin"

// files
let appReferences  = !+ "src/app/**/*.csproj"  |> Scan
let testReferences = !+ "src/test/**/*.csproj" |> Scan

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

Target "BuildApp" (fun () -> 
  let apps = MSBuild buildDir "Build" appReferences
  Log "AppBuild-Output: " apps
)

Target "BuildTest" (fun () -> 
  let testApps = MSBuild buildDir "Build" testReferences
  Log "TestBuild-Output: " testApps
)

Target "Test" (fun () ->  
  let testAssemblies = 
    !+ (buildDir + "*.Test.dll") |> Scan
      
  let output = buildDir + "TestResults.xml"
  NUnit (fun p -> 
      {p with 
         ToolPath = nunitPath; 
         DisableShadowCopy = true; 
         OutputFile = output}) 
    testAssemblies  
)

Target "BuildZip" (fun () ->
  let artifacts = !+ (buildDir + "/**/*.*") -- "*.zip" |> Scan
  let zipFileName = deployDir + sprintf "%s-%s.zip" projectName version
  Zip buildDir zipFileName artifacts
)

Target "Default" DoNothing

// Dependencies
"BuildApp" <== ["Clean"]
"Test"     <== ["BuildApp"; "BuildTest"]
"BuildZip" <== ["Test"]
"Default"  <== ["BuildZip"]

// start build
run "Default"








Hosted by Google Code