-
Notifications
You must be signed in to change notification settings - Fork 0
Reference | Recipe
The recipe body is a computation expression of type recipe. It is similar to F#'s async computation — both regular code and do!/let! notation can be used inside.
let compileProject config = recipe {
do! sh "dotnet build" {
args ["-c"; config]
}
}Use let! or do! to call recipes:
recipe {
let! result = someRecipe()
do! anotherRecipe "arg"
}recipe {
// Get the current target file
let! file = getTargetFile()
let fullPath = file.FullName
// Shortcut for full path
let! fullPath = getTargetFullName()
// Get execution options
let! opts = getCtxOptions()
let rootPath = opts.ProjectRoot
}Typed variables provide compile-time safety and --help integration. See Variables for full details.
let vars = {|
Config = Var.string() |> withDefault "Debug"
Version = Var.string() |> required
|}
// In a recipe:
let! config = vars.Config // string
let! version = vars.Version // stringThese functions return string option and record dependencies:
recipe {
let! ver = getVar "VERSION"
let! user = getEnv "USERNAME"
}When a variable or environment value changes between builds, targets depending on it will be rebuilt.
need queues targets for execution and pauses the recipe until they complete:
recipe {
do! need ["lib.dll"; "utils.dll"]
}This also records dependencies — the current target will be rebuilt if any demanded target changes.
recipe {
// Get files matching a pattern, recording dependency on the file list
let! files = getFiles <| fileset {
basedir "src"
includes "**/*.fs"
}
// Demand those files — rebuild if any file changes
do! needFiles files
// Shortcut: getFiles + needFiles in one call
do! dependsOn !! "src/**/*.fs"
}The trace function writes messages to all log targets:
do! trace Message "Build started"
do! trace Info "Building %s version %s" name version
do! trace Error "Compilation failed"Level values: Message, Error, Command, Warning, Info, Debug, Verbose, Never.
Messages are filtered by the configured verbosity level and prefixed with the executing task's identifier.
Recipe supports calling F# async and Task values directly:
recipe {
let! data = someAsyncOperation()
let! result = someTaskReturningMethod()
}Transforms the result of a recipe:
let! isDebug = getVar "DEBUG" |> Recipe.map (Option.defaultValue "0")Discards the result of a recipe that returns a value:
do! someRecipeThatReturnsInt() |> Ignoreif, for, and while are supported:
recipe {
if condition then
do! trace Info "Condition met"
for file in files do
do! trace Info "Processing %s" file
let mutable i = 0
while i < 3 do
do! trace Info "Step %d" i
i <- i + 1
}recipe {
try
try
do! riskyOperation()
with e ->
do! trace Error "Failed: %s" e.Message
finally
printfn "Cleanup"
}Recipes with
do!notation work inwithblocks but not infinallyblocks — this is a limitation of F# computation expressions.
Wraps a recipe to intercept exceptions:
do! riskyRecipe |> WhenError (fun ex -> defaultValue)
do! riskyRecipe |> WhenError ignoreRaises an exception if the recipe's result meets a condition:
do! shell {cmd "dir"} |> FailWhen Not0 "Command failed"
// Shortcut:
do! shell {cmd "dir"} |> CheckErrorLevelDefine a recipe when you need to:
- Access the execution context
- Call async or task-returning functions
- Use Xake functions like
trace,need,getVar
Use a plain function for everything else.