Reputation: 5454
What is the translation of the following MSBuild script into F#/FAKE?
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CurrentMode>None</CurrentMode>
</PropertyGroup>
<Target Name="Compile_A">
<Message Text="Hello from Compile_A" />
<CallTarget Targets="SetToA"/>
<CallTarget Targets="IntermediateStage"/>
</Target>
<Target Name="Compile_B">
<Message Text="Hello from Compile_B" />
<CallTarget Targets="SetToB"/>
<CallTarget Targets="IntermediateStage"/>
</Target>
<Target Name="IntermediateStage">
<Message Text="Hello from IntermediateStage" />
<CallTarget Targets="Compile"/>
</Target>
<Target Name="Compile">
<Message Text="Hello from Compile. I'm using $(CurrentMode)" />
</Target>
<!-- The following Targets are only necessary due to a MSBuild bug (CallTarget and CreateProperty cannot be combined) -->
<Target Name="SetToA">
<CreateProperty Value="A">
<Output TaskParameter="Value" PropertyName="CurrentMode" />
</CreateProperty>
</Target>
<Target Name="SetToB">
<CreateProperty Value="B">
<Output TaskParameter="Value" PropertyName="CurrentMode" />
</CreateProperty>
</Target>
</Project>
My main goal is to set a property in the topmost targets with different values (CurrentMode
is either A
or B
) and consume it in the deepest target (Compile
).
Upvotes: 1
Views: 414
Reputation: 243096
The best answer will probably differ based on what is your actual scenario. In particular, you might not even need separate targets for the different steps of your process if you do not ever plan to run them separately (in which case, just using a function that takes the mode as a parameter and invoking that from CompileA
and CompileB
would work fine).
However, if you want to keep separate targets for all the steps, you could do something like this:
#load ".fake/build.fsx/intellisense.fsx"
open Fake.Core
open Fake.Core.TargetOperators
let mutable CurrentMode = "None"
Target.create "SetA" (fun _ ->
CurrentMode <- "A"
)
Target.create "SetB" (fun _ ->
CurrentMode <- "B"
)
Target.create "IntermediateStage" (fun _ ->
printfn "In the intermediate stage"
)
Target.create "Compile" (fun _ ->
printfn "Compiling using mode %s" CurrentMode
)
Target.create "CompileA" ignore
Target.create "CompileB" ignore
"SetA" ==> "CompileA"
"SetB" ==> "CompileB"
"IntermediateStage" ==> "Compile" ==> "CompileA"
"IntermediateStage" ==> "Compile" ==> "CompileB"
"SetA" ?=> "IntermediateStage"
"SetB" ?=> "IntermediateStage"
Target.runOrDefault "CompileA"
This uses mutable variable CurrentMode
that is set by SetA
or SetB
targets (arguably, not very functional, but it captures what you're doing).
The dependencies between targets are specified using ==>
. Note that SetA
has to happen before CompileA
(and similar for B) and IntermediateStage
needs to go before Compile
which is pre-requisite for both kinds of compiles.
There is one subtle trick - you don't want to say that SetA
and SetB
are required for IntermediateStep
, because then FAKE would run both in non-deterministic order. the ?=>
opreator lets you specify soft dependencies which say that if both IntermediateStep
and SetA
are to be executed, then SetA
has to go first - so the last two lines are not adding dependencies, but making ordering explicit.
Upvotes: 3