The "FAKE - F# Make" mailing list can be found at http://groups.google.com/group/fsharpMake.
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-specific 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 - 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 benefits 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.
You can download the latest builds from http://teamcity.codebetter.com. You don't need to register, a guest login is ok.
- Getting started with "FAKE - F# Make": This tutorial shows you how to build the CalculatorSample project.
- Adding FxCop to a "FAKE" build script
- Debugging "FAKE - F# Make" build scripts
- Modifying AssemblyInfo and Version via "FAKE - F# Make"
- Writing custom tasks for "FAKE - F# Make"
- Integrating a "FAKE - F# Make" build script into TeamCity
- Integrating a "FAKE - F# Make" build script into CruiseControl.NET
- Running specific targets in "FAKE – F# Make"
- Simple build infrastructure
- Easy systax
- Full power of .NET Framework
- Simple TeamCity integration (see Integrating a "FAKE - F# Make" build script into TeamCity)
- Simple CruiseControl.NET integration (see Integrating a "FAKE - F# Make" build script into CruiseControl.NET)
- FinalTarget feature (to release resources even if build fails)
- Extensible platform - Write your own tasks
- Easy debugging
- Intellisense support (when using Visual Studio)
- Clean task
- NUnit support
- xUnit.net support
- NCover support
- FxCop support
- ExecProcess task (To run tools via the command line)
- MSBuild task (to compile *.csproj and *.fsproj projects or run MSBuild scripts)
- XMLRead task
- VSS task (Get sources from Visual Source Safe)
- XCopy task
- Zip task
- git tasks
- AssemblyInfo task
- ...
Targets are the main unit of work in a "FAKE - F# Make" script. Targets have a name and an action (given as a code block).
// The clean target cleans the build and deploy folders
Target "Clean" (fun _ ->
CleanDir "./build/"
CleanDir "./deploy/"
)
You can define prerequisites for tasks:
// Target Default is dependent from target Clean and BuildApp
// "FAKE - F# Make" will run these targets before Default
"Default" <== ["Clean"; "BuildApp"]
You can execute targets with the "run"-command:
// Executes Default target
Run "Default"
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"
"FAKE - F# Make" uses similar include and exclude patterns as NAnt and MSBuild.
// Includes all *.csproj files under /src/app by using the !+ operator
!+ "src/app/**/*.csproj"
// Includes all *.csproj files under /src/app and /test with the ++ operator
!+ "src/app/**/*.csproj"
++ "test/**/*.csproj"
// Includes all files under /src/app but excludes *.zip files
!+ "src/app/**/*.*"
-- "*.zip"
"FAKE - F# Make" 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 and scans them lazy
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
// define test dlls
let testDlls = !+ (testDir + @"/Test.*.dll") |> Scan
Target "NUnitTest" (fun _ ->
testDlls
|> NUnit (fun p ->
{p with
ToolPath = nunitPath;
DisableShadowCopy = true;
OutputFile = testDir + "TestResults.xml"}))
// define test dlls
let testDlls = !+ (testDir + @"/Test.*.dll") |> Scan
Target "xUnitTest" (fun _ ->
testDlls
|> xUnit (fun p ->
{p with
ShadowCopy = false;
HtmlPrefix = testDir}))
This sample script
- Assumes "FAKE - F# Make" is located at ./tools/FAKE
- Assumes NUnit is located at ./tools/NUnit
- Cleans the build and deploy paths
- Builds all C# projects below src/app/ and puts the output to .\build
- Builds all NUnit test projects below src/test/ and puts the output to .\build
- Uses NUnit to test the generated Test.*.dll's
- Zips all generated files to deploy\MyProject-0.1.zip
You can read Getting started with FAKE to build such a script.
// include Fake libs
#I "tools\FAKE"
#r "FakeLib.dll"
open Fake
// Directories
let buildDir = @".\build\"
let testDir = @".\test\"
let deployDir = @".\deploy\"
// tools
let nunitPath = @".\Tools\NUnit"
let fxCopRoot = @".\Tools\FxCop\FxCopCmd.exe"
// Filesets
let appReferences =
!+ @"src\app\**\*.csproj"
++ @"src\app\**\*.fsproj"
|> Scan
let testReferences =
!+ @"src\test\**\*.csproj"
|> Scan
// version info
let version = "0.2" // or retrieve from CI server
// Targets
Target "Clean" (fun _ ->
CleanDirs [buildDir; testDir; deployDir]
)
Target "BuildApp" (fun _ ->
AssemblyInfo
(fun p ->
{p with
CodeLanguage = CSharp;
AssemblyVersion = version;
AssemblyTitle = "Calculator Command line tool";
AssemblyDescription = "Sample project for FAKE - F# MAKE";
Guid = "A539B42C-CB9F-4a23-8E57-AF4E7CEE5BAA";
OutputFileName = @".\src\app\Calculator\Properties\AssemblyInfo.cs"})
AssemblyInfo
(fun p ->
{p with
CodeLanguage = CSharp;
AssemblyVersion = version;
AssemblyTitle = "Calculator library";
AssemblyDescription = "Sample project for FAKE - F# MAKE";
Guid = "EE5621DB-B86B-44eb-987F-9C94BCC98441";
OutputFileName = @".\src\app\CalculatorLib\Properties\AssemblyInfo.cs"})
// compile all projects below src\app\
MSBuildRelease buildDir "Build" appReferences
|> Log "AppBuild-Output: "
)
Target "BuildTest" (fun _ ->
MSBuildDebug testDir "Build" testReferences
|> Log "TestBuild-Output: "
)
Target "NUnitTest" (fun _ ->
!+ (testDir + @"\NUnit.Test.*.dll")
|> Scan
|> NUnit (fun p ->
{p with
ToolPath = nunitPath;
DisableShadowCopy = true;
OutputFile = testDir + @"TestResults.xml"})
)
Target "xUnitTest" (fun _ ->
!+ (testDir + @"\xUnit.Test.*.dll")
|> Scan
|> xUnit (fun p ->
{p with
ShadowCopy = false;
HtmlOutput = true;
XmlOutput = true;
OutputDir = testDir })
)
Target "FxCop" (fun _ ->
!+ (buildDir + @"\**\*.dll")
++ (buildDir + @"\**\*.exe")
|> Scan
|> FxCop (fun p ->
{p with
ReportFileName = testDir + "FXCopResults.xml";
ToolPath = fxCopRoot})
)
Target "Deploy" (fun _ ->
!+ (buildDir + "\**\*.*")
-- "*.zip"
|> Scan
|> Zip buildDir (deployDir + "Calculator." + version + ".zip")
)
Target "Test" DoNothing
// Dependencies
"BuildApp" <== ["Clean"]
"BuildTest" <== ["Clean"]
"NUnitTest" <== ["BuildApp"; "BuildTest"; "FxCop"]
"xUnitTest" <== ["BuildApp"; "BuildTest"; "FxCop"]
"Test" <== ["xUnitTest"; "NUnitTest"]
"Deploy" <== ["Test"]
// start build
Run "Deploy