How do I parameterise and repeat a build step - f#

I'm writing a FAKE script which will basically do the following: -
1. Clean build outputs
2. Modify a configuration file with a specific value.
3. Perform a build.
4. Upload the outputs somewhere.
I've written all the individual tasks. What I now need to do is to set up a set of FAKE build steps to essentially repeat the above steps, once for each configuration value e.g. let's assume the configuration file had an attribute "colour". I want to repeat the above four build steps, and in step 2 use one of the values [ "black"; "blue"; "red"; "white" ].
What's the best way of achieving this? Should I just make one big build task that does all of this in one e.g. for loop (seems wrong)? Or create multiple build steps e.g. "Set Config to Blue" and "Set Config to Red" etc. and repeat the whole build flow for each colour (again, seems wrong)?

It's not very good documented, but you can create targets programmatically via TargetTemplateWithDependecies
I will add some docs.

You can do it like this
#r "tools/FAKE/tools/FakeLib.dll"
open Fake
Target "Clean" (fun _ ->
trace "Cleaning stuff..."
)
let config color = ignore()
Target "ConfigBlack" (fun _ ->
config "black"
)
Target "ConfigRed" (fun _ ->
config "red"
)
Target "Build" (fun _ ->
trace "Build solution"
)
Target "Upload" (fun _ ->
trace "Upload artifacts"
)
"Clean"
=?> ("ConfigBlack",hasBuildParam "black")
=?> ("ConfigRed",hasBuildParam "red")
==> "Build"
==> "Upload"
Run "Upload"
After that you will be able to call in like this build Upload black or build Upload red

The script below works but uses ExecutedTargets which I don't think you should use.
#I "tools/FAKE/tools"
#r "FakeLib.dll"
open Fake
let mutable value = "Foo"
Target "Clean" (fun _ ->
trace "clean target"
)
Target "Modify config file" (fun _ ->
trace (sprintf "===========> Modify config file: '%s'" value)
)
Target "Perform build" (fun _ ->
trace "Perform build"
)
Target "Default" (fun _ -> ())
"Clean"
==> "Modify config file"
==> "Perform build"
==> "Default"
["black"; "blue"; "red"; "white"]
|> List.iter(fun v ->
value <- v
Run <| getBuildParamOrDefault "target" "Default"
ExecutedTargets.Clear()
)

Can you use various configurations? For example:
BlueBuild:
[configsection]
[Color]Blue[/Color]
[/configsection]
RedBuild:
[configsection]
[Color]Red[/Color]
[/configsection]
See the following Articles:
How to select different app.config for several build configurations
http://www.hanselman.com/blog/managingmultipleconfigurationfileenvironmentswithprebuildevents.aspx

The way I've done this is to basically write a helper function which generates targets - much like the template dependency does - but also generates multiple instances of the "common" targets; each one differs based on the name of the variable element e.g.
let targets =
[ "Blue: Clean"
"Blue: Modify Config"
"Blue: Perform Build"
"Blue: Upload outputs"
"Red: Clean"
"Red: Modify Config"
"Red: Perform Build"
"Red: Upload outputs"
"Yellow: Clean"
"Yellow: Modify Config"
"Yellow: Perform Build"
"Yellow: Upload outputs" ]
You can then easily build then into a sequential chain of dependencies with e.g.
targets |> List.reduce (==>)
Whilst this is not exactly what I had hoped for, it works well enough for me and lets me see through Fake where things are.

Related

F# How to implement parameterized CI – execution loop using FAKE

The question is mostly about step #3.
I need to implement the following loop in F#:
Load some input parameters from database. If input parameters specify that the loop should be terminated, then quit. This is straightforward, thanks to type providers.
Run a model generator, which generates a single F# source file, call it ModelData.fs. The generation is based on input parameters and some random values. This may take up to several hours. I already have that.
Compile the project. I can hard code a call to MSBuild, but that does not feel right.
Copy executables into some temporary folder, let’s say <SomeTempData>\<SomeModelName>.
Asynchronously run the executable from the output location of the previous step. The execution time is usually between several hours to several days. Each process runs in a single thread because this is the most efficient way to do it in this case. I tried the parallel version, but it did not beat the single threaded one.
Catch when the execution finishes. This seems straightforward: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netframework-4.7.2 . The running process is responsible for storing useful results, if any. This event can be handled by F# MailBoxProcessor.
Repeat from the beginning. Because execution is async, monitor the number of running tasks to ensure that it does not exceed some allowed number. Again, MailBoxProcessor will do that with ease.
The whole thing will run on Windows, so there is no need to maintain multiple platforms. Just NET Framework (let’s say 4.7.2 as of this date) will do fine.
This seems like a very straightforward CI-like exercise and F# FAKE seemed as a proper solution. Unfortunately, none of the provided scarce examples worked (even with reasonable tweaks) and the bugs were cryptic. However, the worst part was that the compiling feature did not work at all. The provided example: http://fake.build/fake-gettingstarted.html#Compiling-the-application cannot be run at all and even after accounting for something like that: https://github.com/fsharp/FAKE/issues/1579 : it still silently choses not to compile the project. I’d appreciate any advice.
Here is the code that I was trying to run. It is based on the references above:
#r #"C:\GitHub\ClmFSharp\Clm\packages\FAKE.5.8.4\tools\FakeLib.dll"
#r #"C:\GitHub\ClmFSharp\Clm\packages\FAKE.5.8.4\tools\System.Reactive.dll"
open System.IO
open Fake.DotNet
open Fake.Core
open Fake.IO
open Fake.IO.Globbing.Operators
let execContext = Fake.Core.Context.FakeExecutionContext.Create false "build.fsx" []
Fake.Core.Context.setExecutionContext (Fake.Core.Context.RuntimeContext.Fake execContext)
// Properties
let buildDir = #"C:\Temp\WTF\"
// Targets
Target.create "Clean" (fun _ ->
Shell.cleanDir buildDir
)
Target.create "BuildApp" (fun _ ->
!! #"..\SolverRunner\SolverRunner.fsproj"
|> MSBuild.runRelease id buildDir "Build"
|> Trace.logItems "AppBuild-Output: "
)
Target.create "Default" (fun _ ->
Trace.trace "Hello World from FAKE"
)
open Fake.Core.TargetOperators
"Clean"
==> "BuildApp"
==> "Default"
Target.runOrDefault "Default"
The problem is that it does not build the project at all but no error messages are produced! This is the output when running it in FSI:
run Default
Building project with version: LocalBuild
Shortened DependencyGraph for Target Default:
<== Default
<== BuildApp
<== Clean
The running order is:
Group - 1
- Clean
Group - 2
- BuildApp
Group - 3
- Default
Starting target 'Clean'
Finished (Success) 'Clean' in 00:00:00.0098793
Starting target 'BuildApp'
Finished (Success) 'BuildApp' in 00:00:00.0259223
Starting target 'Default'
Hello World from FAKE
Finished (Success) 'Default' in 00:00:00.0004329
---------------------------------------------------------------------
Build Time Report
---------------------------------------------------------------------
Target Duration
------ --------
Clean 00:00:00.0025260
BuildApp 00:00:00.0258713
Default 00:00:00.0003934
Total: 00:00:00.2985910
Status: Ok
---------------------------------------------------------------------

Integrating FxCop Gives an error : FAKE F#MAKE

I am using FxCop in FAKE but it is giving an error
i.e.
Analysis was not performed; at least one valid rules assembly and one valid target file must be specified.
* 1 total analysis engine exceptions.
While all targets are successfully build.
here is my code :
Target "FxCop" (fun _ ->
!! (buildDir + "/**/*.dll")
++ (buildDir + "/**/*.exe")
|> FxCop (fun p ->
{p with
//Override default parameters
ReportFileName = testDir + "FXCopResults.xml";
ToolPath = "D:/Fake/FAKE-Calculator/tools/FxCop/FxCopCmd.exe"})
)
It also shows : Project error : No targets were selected .
The FAKE documentation doesn't make it clear enough, but apparently you need to explicitly specify one of two things:
Which FxCop rules you want to run, or
The path to an "FxCop project file" (a file with the .FxCop extension).
I can't tell you how to write an FxCop project file since I've never done so myself, but maybe the programmer who set up the MsBuild system you've working with already did so. If he did, then you just need to add the following parameter to your FxCop call in your FAKE build script:
ProjectFile = buildDir </> "filename.FxCop"
where filename, of course, should be replaced by a real file name.
If you don't have an FxCop project file, then apparently you have to explicitly specify a list of FxCop rules in the RuleLibraries parameter. First, you need to find out which FxCop rules are available. To do that, look in your FxCop installation directory (on my system, where I have an older version of FxCop installed, it was C:\Program Files (x86)\Microsoft FxCop 1.36, but it may be different for you) for a Rules folder. That folder should contain several DLLs; for example, on my system, the Rules folder contained:
DesignRules.dll
GlobalizationRules.dll
InteroperabilityRules.dll
... and several other DLLs that I'm not going to bother typing out. Now you just make that list of filenames into an F# list:
RulesLibraries = ["DesignRules.dll"; "GlobalizationRules.dll"] // and so on
There should be sensible defaults for that, but currently it looks like you have to specify that list of rules by hand. So try writing your target to look something like this:
Target "FxCop" (fun _ ->
!! (buildDir + "/**/*.dll")
++ (buildDir + "/**/*.exe")
|> FxCop (fun p ->
{p with
//Override default parameters
ReportFileName = testDir + "FXCopResults.xml";
RulesLibraries = ["DesignRules.dll"; "GlobalizationRules.dll"] // etc.
ToolPath = "D:/Fake/FAKE-Calculator/tools/FxCop/FxCopCmd.exe"})
)
Remember to separate your list items with ; (semicolon): in F#, the , (comma) character is ONLY for tuples. And don't just copy my example verbatim, but actually look in your FxCop installation directory to see what rule DLLs are available, and include those. (As many, or as few, as your project actually needs).
Also, I don't know if you actually have to specify the .dll extension; you might be able to use ["DesignRules"; "GlobalizationRules"] (and so on). But it's probably just as simple to use the .dll extension and just copy and paste from the filenames.
I haven't tested this myself, so I hope this works for you. If it doesn't, let me know.

Building and running FsCheck.Xunit tests in an F# ionide project

I created a standalone console application using the yo fsharp generator. The github repo concerning this particular question is here: https://github.com/KurtRMueller/PascalsTriangleKata.
I'd like to build and run some basic FsCheck.Xunit tests. I'm aware that I need to add some targets to FAKE's build.fsx, but as I'm quite new to .net and the C#/F# ecosystem, I don't know how to do that.
The example from the FAKE example page is as follows:
// define test dlls
let testDlls = !! (testDir + "/Test.*.dll")
Target "xUnitTest" (fun _ ->
testDlls
|> xUnit (fun p ->
{p with
ShadowCopy = false;
HtmlOutput = true;
XmlOutput = true;
OutputDir = testDir })
)
I'm not quite sure how to build the these Test dlls.
Any help is appreciated. Thanks.

Any way to check-in into TFS from FAKE build target?

I'm trying to create FAKE(F# Make) build target that will update AssemblyInfo files in my project after sucsess build. Version info files are stored in TFS CVS, so I need to checkin updated files to TFS from FAKE build task.
Using TFS 2010, call FAKE from custom activity.
My flow is:
- ... cleanup, call build target
- Update AssemblyInfo files
- Check-in files to TFS
Faced with check-in to TFS issues...
Is there any way to check-in to TFS from FAKE (F# Make) target?
Yes, but this will come in two parts. Also, this is working for TFS 2015 new Build Definitions and TFS Git repository. You can modify this as necessary to fit your particular situation. These two targets can be called as you wish.
Part I - Update the AssemblyInfo files
let AssemblyInfoFiles = !! #"src\**\AssemblyInfo.cs";
Target "SetVersions" (fun _ ->
AssemblyInfoFiles
|> Seq.iter (fun a ->
if tfBuildNumber <> null then
if isLocalBuild = false then
UpdateAttributes a [ Attribute.FileVersion tfBuildNumber]
)
)
Part II - Check in the modified AssemblyInfo file(s)
let CheckInMessage = "Files related to versioning where checked in. ***NO_CI***"
let tfBuildSourceBranchName = environVar "BUILD_SOURCEBRANCHNAME"
let tfBuilderRepositoryProvider = environVar "BUILD_REPOSITORY_PROVIDER"
Target "CheckinFiles" (fun _ ->
AssemblyInfoFiles
|> Seq.iter (fun a ->
//This is for GIT REPOSITORY in TFS 2015 Build Definition
if isLocalBuild = false && tfBuilderRepositoryProvider = "TfsGit" then
checkoutBranch solutionDir tfBuildSourceBranchName
trace ("File to stage: " + a)
StageFile solutionDir a |> ignore
Commit solutionDir CheckInMessage
pull solutionDir "origin" tfBuildSourceBranchName
push solutionDir
)
)
The problem with Part II is that it does a commit for EACH AssemblyInfo, whereas I would like to batch these in one single commit. I can do that, I'm just lazy to fix it.

F# FAKE passing /gac to FxCopHelper

In my build script, when I run it on my local machine (Win 8.1 x64) the whole script works perfectly fine.
When I run it on my build server (Jenkins, Server 2012 r2 x64), the FxCop task fails, because it cannot resolve any of the System.* assembly references which are required by some of the 3rd party libs I'm using.
I know full well that the version of FxCop (10.0) being used is the same, because it is checked into the project's git repo.
I don't understand why it works ok on my local machine, and not on the build server, but as I understand it from the results of my googling, I should be able to pass the /gac switch to FxCop in order to tell it that it should look there to resolve references it needs when scanning assemblies.
I just can't work out how to pass that switch to FxCop, using FAKE.
The target is as follows, but it's honestly essentially the same as the FxCop tutorial on the FAKE website, I've just removed the names of some dlls and exes.
Target "FxCop" (fun _ ->
!! (build ## "**/*.dll")
++ (build ## "**/*.exe")
|> FxCop (fun p ->
{p with
ReportFileName = testResults + "FXCopResults.xml";
ToolPath = "./tools/FxCop/FxCopCmd.exe" })
)
I've had a look at the source of the FxCopHelper, but my F# isn't that great, and there doesn't appear (to me) to be a way of passing extra command line options other than the ones already specified.
Does anyone have any suggestions?
The parameter you are looking for is UseGACSwitch and it is a boolean.
https://github.com/fsharp/FAKE/blob/master/src/app/FakeLib/FXCopHelper.fs#L77
To update your example:
Target "FxCop" (fun _ ->
!! (build ## "**/*.dll")
++ (build ## "**/*.exe")
|> FxCop (fun p ->
{p with
ReportFileName = testResults + "FXCopResults.xml";
UseGACSwitch = true;
ToolPath = "./tools/FxCop/FxCopCmd.exe" })
)
That should get you what you need for your build server to be happy. You may have already found this out or something else, but it is good that everyone knows there are options. Thank you. Good day.

Resources