1+ open  System 
2+ open  System.Diagnostics  
3+ open  System.IO  
4+ open  System.Text .RegularExpressions  
5+ 
6+ open  Fake.Core  
7+ open  Fake.Core .TargetOperators  
8+ open  Fake.DotNet  
9+ open  Fake.IO  
10+ open  Fake.IO .Globbing .Operators  
11+ open  Fake.IO .FileSystemOperators  
12+ open  Fake.Tools  
13+ 
14+ open  ExtractDocs 
15+ 
16+ let  target  =  Target.create
17+ let  description  =  Target.description
18+ 
19+ module  FileReaderWriter  = 
20+     let  Read  file  =  File.ReadAllText( file) 
21+     let  Write  file text  =  File.WriteAllText( file,  text) 
22+     let  TransformFile  file target  ( f  :  string  ->  string )  = 
23+         Read file
24+         |>  f
25+         |>  Write target
26+ 
27+ module  ExamplesToCode  = 
28+     open  FileReaderWriter 
29+ 
30+     let  ConvertFile   ( file :  string )  targetDir  = 
31+         let  fileName  =  Path.GetFileNameWithoutExtension( file) 
32+         let  target  =  targetDir @@  fileName +  " .cs" 
33+         Trace.log <|  sprintf " Converting %s %s " 
34+         TransformFile file target ( ExtractDocs.strToFixture fileName) 
35+ 
36+     let  Convert  paths targetDir  = 
37+         let  paths  =  paths |>  Seq.toList
38+         for  p in  paths do 
39+             Trace.trace <|  sprintf " Convert from %s %s " 
40+             let  files  =  !!  " *.markdown" ++  " *.html" ++  " *.md" |>  GlobbingPattern.setBaseDir p
41+             for  file in  files do 
42+                 ConvertFile file targetDir
43+ 
44+ type  BuildVersion  =  {  assembly:  string ;  file:  string ;  info:  string ;  package:  string  } 
45+ let  getVersion   ()  = 
46+     // The --first-parent flag is needed to make our walk linear from current commit and top. 
47+     // This way also merge commit is counted as "1". 
48+     let  desc  =  Git.CommandHelper.runSimpleGitCommand " " " describe --tags --long --abbrev=40 --first-parent --match=v*" 
49+     let  result  =  Regex.Match( desc, 
50+                              @" ^v(?<maj>\d+)\.(?<min>\d+)\.(?<rev>\d+)(?<pre>-\w+\d*)?-(?<num>\d+)-g(?<sha>[a-z0-9]+)$" , 
51+                              RegexOptions.IgnoreCase) 
52+                       .Groups
53+     let  getMatch   ( name : string )  =  result.[ name]. Value
54+ 
55+     let  ( major ,  minor ,  revision ,  preReleaseSuffix ,  commitsNum ,  commitSha )  = 
56+         ( getMatch " maj" |>  int,  getMatch " min" |>  int,  getMatch " rev" |>  int,  getMatch " pre" ,  getMatch " num" |>  int,  getMatch " sha" ) 
57+ 
58+     // Assembly version should contain major and minor only, as no breaking changes are expected in bug fix releases. 
59+     let  assemblyVersion  =  sprintf " %d %d " 
60+     let  fileVersion  =  sprintf " %d %d %d %d " 
61+  
62+     // If number of commits since last tag is greater than zero, we append another identifier with number of commits. 
63+     // The produced version is larger than the last tag version. 
64+     // If we are on a tag, we use version without modification. 
65+     // Examples of output: 3.50.2.1, 3.50.2.215, 3.50.1-rc1.3, 3.50.1-rc3.35 
66+     let  packageVersion  =  match  commitsNum with 
67+                          |  0  ->  sprintf " %d %d %d%s " 
68+                          |  _  ->  sprintf " %d %d %d%s %d " 
69+ 
70+     let  infoVersion  =  match  commitsNum with 
71+                       |  0  ->  packageVersion
72+                       |  _  ->  sprintf " %s %s " 
73+ 
74+     {  assembly =  assemblyVersion;  file =  fileVersion;  info =  infoVersion;  package =  packageVersion } 
75+  
76+ let  root  =  __ SOURCE_ DIRECTORY__  </>  " .." |>  Path.getFullName
77+ 
78+ let  configuration  =  Environment.environVarOrDefault " configuration" " Debug" 
79+ let  version  =  getVersion () 
80+ 
81+ let  additionalArgs  =  [ 
82+     " AssemblyVersion" ,  version.assembly
83+     " FileVersion" ,  version.file
84+     " InformationalVersion" ,  version.info
85+     " PackageVersion" ,  version.package
86+ ] 
87+ 
88+ let  output  =  root </>  " bin" </>  configuration
89+ let  solution  =  ( root </>  " NSubstitute.sln" ) 
90+ 
91+ let  initTargets ()  = 
92+     Target.create " Default" 
93+     Target.create " All" 
94+ 
95+     Target.description( " Clean compilation artifacts and remove output bin directory" ) 
96+     Target.create " Clean" ( fun  _  -> 
97+         DotNet.exec ( fun  p  ->  {  p with  WorkingDirectory =  root })  " clean" 
98+             ( sprintf " --configuration %s " ) 
99+             |>  ignore
100+         Shell.cleanDirs [  output ] 
101+     ) 
102+ 
103+     Target.description( " Restore dependencies" ) 
104+     Target.create " Restore" ( fun  _  -> 
105+         DotNet.restore ( fun  p  ->  p)  solution
106+     ) 
107+ 
108+     Target.description( " Compile all projects" ) 
109+     Target.create " Build" ( fun  _  -> 
110+         DotNet.build ( fun  p  -> 
111+             {  p with  Configuration =  DotNet.BuildConfiguration.fromString configuration
112+                      MSBuildParams =  {  p.MSBuildParams with  Properties =  additionalArgs } 
113+             })  solution
114+     ) 
115+ 
116+     Target.description( " Run tests" ) 
117+     Target.create " Test" ( fun  _  -> 
118+         DotNet.test ( fun  p  -> 
119+             {  p with  Configuration =  DotNet.BuildConfiguration.fromString configuration
120+                      MSBuildParams =  {  p.MSBuildParams with  Properties =  additionalArgs } 
121+             })  ( root </>  " tests/NSubstitute.Acceptance.Specs/NSubstitute.Acceptance.Specs.csproj" ) 
122+     ) 
123+ 
124+     Target.description( " Generate Nuget package" ) 
125+     Target.create " Package" ( fun  _  -> 
126+         DotNet.pack ( fun  p  -> 
127+             {  p with  Configuration =  DotNet.BuildConfiguration.fromString configuration
128+                      MSBuildParams =  {  p.MSBuildParams with  Properties =  additionalArgs } 
129+             })  ( root </>  " src/NSubstitute/NSubstitute.csproj" ) 
130+     ) 
131+ 
132+     Target.description( " Run all benchmarks. Must be run with configuration=Release." ) 
133+     Target.create " Benchmarks" ( fun  _  -> 
134+         if  configuration <>  " Release" then 
135+             failwith " Benchmarks can only be run in Release mode. Please re-run the build in Release configuration." 
136+ 
137+         let  benchmarkCsproj  =  root </>  " tests/NSubstitute.Benchmarks/NSubstitute.Benchmarks.csproj" |>  Path.getFullName
138+         let  benchmarkToRun  =  Environment.environVarOrDefault " benchmark" " *" // Defaults to "*" (all) 
139+         [  " netcoreapp2.1" ] 
140+         |>  List.iter ( fun  framework  -> 
141+             Trace.traceImportant ( " Benchmarking " +  framework) 
142+             let  work  =  output </>  " benchmark-" +  framework
143+             Directory.ensure work
144+             DotNet.exec ( fun  p  ->  {  p with  WorkingDirectory =  work })  " run" 
145+                 ( " --framework " +  framework +  "  --project " +  benchmarkCsproj +  "  -- " +  benchmarkToRun) 
146+                 |>  ignore
147+         ) 
148+     ) 
149+ 
150+     Target.description( " Extract, build and test code from documentation." ) 
151+     Target.create " TestCodeFromDocs" <|  fun  _  -> 
152+         let  outputCodePath  =  output </>  " CodeFromDocs" 
153+         Directory.create outputCodePath
154+         // generate samples from docs 
155+         ExamplesToCode.Convert [  root </>  " docs/" ;  root </>  " docs/help/_posts/" ;  root ]  outputCodePath
156+         // compile code samples 
157+         let  csproj  =  """ 
158+             <Project Sdk="Microsoft.NET.Sdk"> 
159+             <PropertyGroup> 
160+                 <TargetFrameworks>net6.0;net462</TargetFrameworks> 
161+                 <LangVersion>latest</LangVersion> 
162+             </PropertyGroup> 
163+             <ItemGroup> 
164+                 <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> 
165+                 <PackageReference Include="NUnit" Version="3.13.3" /> 
166+                 <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" /> 
167+             </ItemGroup> 
168+             <ItemGroup> 
169+                 <ProjectReference Include="..\..\..\src\NSubstitute\NSubstitute.csproj" /> 
170+             </ItemGroup> 
171+         </Project> 
172+         """  
173+         let  projPath  =  outputCodePath </>  " Docs.csproj" 
174+         FileReaderWriter.Write projPath csproj
175+         DotNet.restore ( fun  p  ->  p)  projPath
176+         DotNet.build ( fun  p  ->  p)  projPath
177+         DotNet.test ( fun  p  ->  p)  projPath
178+ 
179+     let  tryFindFileOnPath   ( file  :  string )  :  string option  = 
180+         Environment.GetEnvironmentVariable( " PATH" ) .Split([|  Path.PathSeparator |]) 
181+         |>  Seq.append [ " ." ] 
182+         |>  fun  path  ->  ProcessUtils.tryFindFile path file
183+ 
184+     Target.description( " Build documentation website. Requires Ruby, bundler and jekyll." ) 
185+     Target.create " Documentation" <|  fun  _  ->  
186+         Trace.log " Building site..." 
187+         let  exe  =  [  " bundle.bat" ;  " bundle" ] 
188+                     |>  Seq.map tryFindFileOnPath
189+                     |>  Seq.collect ( Option.toList) 
190+                     |>  Seq.tryFind ( fun  _  ->  true ) 
191+                     |>  function  |  Some x ->  Trace.log ( " using " +  x);  x
192+                                 |  None   ->  Trace.log ( " count not find exe" );  " bundle" 
193+ 
194+         let  workingDir  =  root </>  " docs/" 
195+         let  docOutputRelativeToWorkingDir  =  " .." </>  output </>  " nsubstitute.github.com" 
196+         
197+         // TODO migrate the following to FAKE API: CreateProcess.ofStartInfo(p) 
198+         // https://fake.build/apidocs/v5/fake-core-createprocess.html 
199+         // that doesn't work for some reason 
200+         let  p  =  ProcessStartInfo( 
201+                         UseShellExecute =  false , 
202+                         CreateNoWindow =  true , 
203+                         FileName =  exe, 
204+                         WorkingDirectory =  workingDir, 
205+                         Arguments =  " exec jekyll build -d \" " +  docOutputRelativeToWorkingDir +  " \" " ) 
206+         let  proc  =  Process.Start( p) 
207+         proc.WaitForExit() 
208+         let  result  =  proc.ExitCode
209+         if  result =  0  then 
210+             " Site built in " +  docOutputRelativeToWorkingDir |>  Trace.log
211+         else 
212+             " failed to build site" |>  failwith
213+ 
214+     Target.description( " List targets, similar to `rake -T`. For more details, run `--listTargets` instead." ) 
215+     Target.create " -T" <|  fun  _  -> 
216+         printfn " Optional config options:" 
217+         printfn "   configuration=Debug|Release" 
218+         printfn "   benchmark=*|<benchmark name>  (only for Benchmarks target in Release mode)" 
219+         printfn " " 
220+         Target.listAvailable() 
221+ 
222+     " Clean" ?=>  " Build" |>  ignore
223+     " Clean" ?=>  " Test" |>  ignore
224+     " Clean" ?=>  " Restore" |>  ignore
225+     " Clean" ?=>  " Documentation" |>  ignore
226+     " Clean" ?=>  " TestCodeFromDocs" |>  ignore
227+     " Clean" ?=>  " Package" |>  ignore
228+     " Clean" ?=>  " Default" |>  ignore
229+ 
230+     " Build" <==  [  " Restore" ] 
231+     " Test" <==  [  " Build" ] 
232+     " Documentation" <==  [  " TestCodeFromDocs" ] 
233+     " Benchmarks" <==  [  " Build" ] 
234+     // For packaging, use a clean build and make sure all tests (inc. docs) pass. 
235+     " Package" <==  [  " Clean" ;  " Build" ;  " Test" ;  " TestCodeFromDocs" ] 
236+ 
237+     " Default" <==  [  " Restore" ;  " Build" ;  " Test" ] 
238+     " All" <==  [  " Clean" ;  " Default" ;  " Documentation" ;  " Package" ] 
239+ 
240+ [<EntryPoint>] 
241+ let  main  argv  = 
242+     argv
243+     |>  Array.toList
244+     |>  Context.FakeExecutionContext.Create false  " build.fsx" 
245+     |>  Context.RuntimeContext.Fake
246+     |>  Context.setExecutionContext
247+     initTargets() 
248+     Target.runOrDefaultWithArguments " Default" 
249+     0  
0 commit comments