Updated build scripts (migration to latest ProjectScaffold).

This commit is contained in:
Achim D. Brucker 2018-07-06 22:53:00 +01:00
parent 4f04eaaa45
commit 5b1e16bce3
1 changed files with 207 additions and 209 deletions

416
build.fsx
View File

@ -2,16 +2,20 @@
// FAKE build script // FAKE build script
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
#r @"packages/build/FAKE/tools/FakeLib.dll" #r "paket: groupref FakeBuild //"
open Fake
open Fake.Git #load "./.fake/build.fsx/intellisense.fsx"
open Fake.AssemblyInfoFile
open Fake.ReleaseNotesHelper
open Fake.UserInputHelper
open System
open System.IO open System.IO
open System.Diagnostics open Fake.Core
open Fake.Core.TargetOperators
open Fake.DotNet
open Fake.IO
open Fake.IO.FileSystemOperators
open Fake.IO.Globbing.Operators
open Fake.DotNet.Testing
open Fake.Tools
open Fake.Api
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// START TODO: Provide project-specific details below // START TODO: Provide project-specific details below
@ -36,7 +40,7 @@ let summary = "A data science framework for analyzing Chrome browser extensions.
let description = "A data science framework for analyzing Chrome browser extensions." let description = "A data science framework for analyzing Chrome browser extensions."
// List of author names (for NuGet package) // List of author names (for NuGet package)
let authors = [ "Achim D. Brucker" ] let author = "Achim D. Brucker"
// Tags for your project (for NuGet package) // Tags for your project (for NuGet package)
let tags = "ChromeExtension DataScience BrowserSecurity" let tags = "ChromeExtension DataScience BrowserSecurity"
@ -47,8 +51,8 @@ let solutionFile = "LogicalHacking.ExtensionDsLab.sln"
// Default target configuration // Default target configuration
let configuration = "Release" let configuration = "Release"
// Pattern specifying assemblies to be tested using NUnit // Pattern specifying assemblies to be tested using Expecto
let testAssemblies = "tests/**/bin" </> configuration </> "*Tests*.dll" let testAssemblies = "tests/**/bin" </> configuration </> "**" </> "*Tests.exe"
// Git configuration (used for publishing documentation in gh-pages branch) // Git configuration (used for publishing documentation in gh-pages branch)
// The profile where the project is posted // The profile where the project is posted
@ -59,14 +63,16 @@ let gitHome = sprintf "%s/%s" "https://git.logicalhacking.com" gitOwner
let gitName = "ExtensionDsLab" let gitName = "ExtensionDsLab"
// The url for the raw files hosted // The url for the raw files hosted
let gitRaw = environVarOrDefault "gitRaw" "https://git.logicalhacking.com/BrowserSecurity" let gitRaw = Environment.environVarOrDefault "gitRaw" "https://raw.githubusercontent.com/BrowserSecurity"
let website = "/ExtensionDsLab"
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// END TODO: The rest of the file includes standard build steps // END TODO: The rest of the file includes standard build steps
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Read additional information from the release notes document // Read additional information from the release notes document
let release = LoadReleaseNotes "RELEASE_NOTES.md" let release = ReleaseNotes.load "RELEASE_NOTES.md"
// Helper active pattern for project types // Helper active pattern for project types
let (|Fsproj|Csproj|Vbproj|Shproj|) (projFileName:string) = let (|Fsproj|Csproj|Vbproj|Shproj|) (projFileName:string) =
@ -78,30 +84,30 @@ let (|Fsproj|Csproj|Vbproj|Shproj|) (projFileName:string) =
| _ -> failwith (sprintf "Project file %s not supported. Unknown project type." projFileName) | _ -> failwith (sprintf "Project file %s not supported. Unknown project type." projFileName)
// Generate assembly info files with the right version & up-to-date information // Generate assembly info files with the right version & up-to-date information
Target "AssemblyInfo" (fun _ -> Target.create "AssemblyInfo" (fun _ ->
let getAssemblyInfoAttributes projectName = let getAssemblyInfoAttributes projectName =
[ Attribute.Title (projectName) [ AssemblyInfo.Title (projectName)
Attribute.Product project AssemblyInfo.Product project
Attribute.Description summary AssemblyInfo.Description summary
Attribute.Version release.AssemblyVersion AssemblyInfo.Version release.AssemblyVersion
Attribute.FileVersion release.AssemblyVersion AssemblyInfo.FileVersion release.AssemblyVersion
Attribute.Configuration configuration ] AssemblyInfo.Configuration configuration ]
let getProjectDetails projectPath = let getProjectDetails projectPath =
let projectName = System.IO.Path.GetFileNameWithoutExtension(projectPath) let projectName = Path.GetFileNameWithoutExtension(projectPath)
( projectPath, ( projectPath,
projectName, projectName,
System.IO.Path.GetDirectoryName(projectPath), Path.GetDirectoryName(projectPath),
(getAssemblyInfoAttributes projectName) (getAssemblyInfoAttributes projectName)
) )
!! "src/**/*.??proj" !! "src/**/*.??proj"
|> Seq.map getProjectDetails |> Seq.map getProjectDetails
|> Seq.iter (fun (projFileName, projectName, folderName, attributes) -> |> Seq.iter (fun (projFileName, _, folderName, attributes) ->
match projFileName with match projFileName with
| Fsproj -> CreateFSharpAssemblyInfo (folderName </> "AssemblyInfo.fs") attributes | Fsproj -> AssemblyInfoFile.createFSharp (folderName </> "AssemblyInfo.fs") attributes
| Csproj -> CreateCSharpAssemblyInfo ((folderName </> "Properties") </> "AssemblyInfo.cs") attributes | Csproj -> AssemblyInfoFile.createCSharp ((folderName </> "Properties") </> "AssemblyInfo.cs") attributes
| Vbproj -> CreateVisualBasicAssemblyInfo ((folderName </> "My Project") </> "AssemblyInfo.vb") attributes | Vbproj -> AssemblyInfoFile.createVisualBasic ((folderName </> "My Project") </> "AssemblyInfo.vb") attributes
| Shproj -> () | Shproj -> ()
) )
) )
@ -109,63 +115,57 @@ Target "AssemblyInfo" (fun _ ->
// Copies binaries from default VS location to expected bin folder // Copies binaries from default VS location to expected bin folder
// But keeps a subdirectory structure for each project in the // But keeps a subdirectory structure for each project in the
// src folder to support multiple project outputs // src folder to support multiple project outputs
Target "CopyBinaries" (fun _ -> Target.create "CopyBinaries" (fun _ ->
!! "src/**/*.??proj" !! "src/**/*.??proj"
-- "src/**/*.shproj" -- "src/**/*.shproj"
|> Seq.map (fun f -> ((System.IO.Path.GetDirectoryName f) </> "bin" </> configuration, "bin" </> (System.IO.Path.GetFileNameWithoutExtension f))) |> Seq.map (fun f -> ((Path.getDirectory f) </> "bin" </> configuration, "bin" </> (Path.GetFileNameWithoutExtension f)))
|> Seq.iter (fun (fromDir, toDir) -> CopyDir toDir fromDir (fun _ -> true)) |> Seq.iter (fun (fromDir, toDir) -> Shell.copyDir toDir fromDir (fun _ -> true))
) )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Clean build results // Clean build results
let vsProjProps = let buildConfiguration = DotNet.Custom <| Environment.environVarOrDefault "configuration" configuration
#if MONO
[ ("DefineConstants","MONO"); ("Configuration", configuration) ]
#else
[ ("Configuration", configuration); ("Platform", "Any CPU") ]
#endif
Target "Clean" (fun _ -> Target.create "Clean" (fun _ ->
!! solutionFile |> MSBuildReleaseExt "" vsProjProps "Clean" |> ignore Shell.cleanDirs ["bin"; "temp"]
CleanDirs ["bin"; "temp"; "docs"] )
Target.create "CleanDocs" (fun _ ->
Shell.cleanDirs ["docs"]
) )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Build library & test project // Build library & test project
Target "Build" (fun _ -> Target.create "Build" (fun _ ->
!! solutionFile solutionFile
|> MSBuildReleaseExt "" vsProjProps "Rebuild" |> DotNet.build (fun p ->
|> ignore { p with
Configuration = buildConfiguration })
) )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Run the unit tests using test runner // Run the unit tests using test runner
Target "RunTests" (fun _ -> Target.create "RunTests" (fun _ ->
!! testAssemblies !! testAssemblies
|> NUnit (fun p -> |> Expecto.run id
{ p with
DisableShadowCopy = true
TimeOut = TimeSpan.FromMinutes 20.
OutputFile = "TestResults.xml" })
) )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Build a NuGet package // Build a NuGet package
Target "NuGet" (fun _ -> Target.create "NuGet" (fun _ ->
Paket.Pack(fun p -> Paket.pack(fun p ->
{ p with { p with
OutputPath = "bin" OutputPath = "bin"
Version = release.NugetVersion Version = release.NugetVersion
ReleaseNotes = toLines release.Notes}) ReleaseNotes = String.toLines release.Notes})
) )
Target "PublishNuget" (fun _ -> Target.create "PublishNuget" (fun _ ->
Paket.Push(fun p -> Paket.push(fun p ->
{ p with { p with
PublishUrl = "https://www.nuget.org" PublishUrl = "https://www.nuget.org"
WorkingDir = "bin" }) WorkingDir = "bin" })
@ -175,193 +175,191 @@ Target "PublishNuget" (fun _ ->
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Generate the documentation // Generate the documentation
// Paths with template/source/output locations
let bin = __SOURCE_DIRECTORY__ @@ "bin"
let content = __SOURCE_DIRECTORY__ @@ "docsrc/content"
let output = __SOURCE_DIRECTORY__ @@ "docs"
let files = __SOURCE_DIRECTORY__ @@ "docsrc/files"
let templates = __SOURCE_DIRECTORY__ @@ "docsrc/tools/templates"
let formatting = __SOURCE_DIRECTORY__ @@ "packages/formatting/FSharp.Formatting"
let docTemplate = "docpage.cshtml"
let fakePath = "packages" </> "build" </> "FAKE" </> "tools" </> "FAKE.exe" let github_release_user = Environment.environVarOrDefault "github_release_user" gitOwner
let fakeStartInfo script workingDirectory args fsiargs environmentVars = let githubLink = sprintf "https://github.com/%s/%s" github_release_user gitName
(fun (info: ProcessStartInfo) ->
info.FileName <- System.IO.Path.GetFullPath fakePath
info.Arguments <- sprintf "%s --fsiargs -d:FAKE %s \"%s\"" args fsiargs script
info.WorkingDirectory <- workingDirectory
let setVar k v =
info.EnvironmentVariables.[k] <- v
for (k, v) in environmentVars do
setVar k v
setVar "MSBuild" msBuildExe
setVar "GIT" Git.CommandHelper.gitPath
setVar "FSI" fsiPath)
/// Run the given buildscript with FAKE.exe // Specify more information about your project
let executeFAKEWithOutput workingDirectory script fsiargs envArgs = let info =
let exitCode = [ "project-name", "LogicalHacking.ExtensionDsLab"
ExecProcessWithLambdas "project-author", "Achim D. Brucker"
(fakeStartInfo script workingDirectory "" fsiargs envArgs) "project-summary", "A data science framework for analyzing Chrome browser extensions."
TimeSpan.MaxValue false ignore ignore "project-github", githubLink
System.Threading.Thread.Sleep 1000 "project-nuget", "http://nuget.org/packages/LogicalHacking.ExtensionDsLab" ]
exitCode
// Documentation let root = website
let buildDocumentationTarget fsiargs target =
trace (sprintf "Building documentation (%s), this could take some time, please wait..." target)
let exit = executeFAKEWithOutput "docsrc/tools" "generate.fsx" fsiargs ["target", target]
if exit <> 0 then
failwith "generating reference documentation failed"
()
Target "GenerateReferenceDocs" (fun _ -> let referenceBinaries = []
buildDocumentationTarget "-d:RELEASE -d:REFERENCE" "Default"
let layoutRootsAll = new System.Collections.Generic.Dictionary<string, string list>()
layoutRootsAll.Add("en",[ templates;
formatting @@ "templates"
formatting @@ "templates/reference" ])
Target.create "ReferenceDocs" (fun _ ->
Directory.ensure (output @@ "reference")
let binaries () =
let manuallyAdded =
referenceBinaries
|> List.map (fun b -> bin @@ b)
let conventionBased =
DirectoryInfo.getSubDirectories <| DirectoryInfo bin
|> Array.collect (fun d ->
let name, dInfo =
let net45Bin =
DirectoryInfo.getSubDirectories d |> Array.filter(fun x -> x.FullName.ToLower().Contains("net45"))
let net47Bin =
DirectoryInfo.getSubDirectories d |> Array.filter(fun x -> x.FullName.ToLower().Contains("net47"))
if net45Bin.Length > 0 then
d.Name, net45Bin.[0]
else
d.Name, net47Bin.[0]
dInfo.GetFiles()
|> Array.filter (fun x ->
x.Name.ToLower() = (sprintf "%s.dll" name).ToLower())
|> Array.map (fun x -> x.FullName)
)
|> List.ofArray
conventionBased @ manuallyAdded
binaries()
|> FSFormatting.createDocsForDlls (fun args ->
{ args with
OutputDirectory = output @@ "reference"
LayoutRoots = layoutRootsAll.["en"]
ProjectParameters = ("root", root)::info
SourceRepository = githubLink @@ "tree/master" }
)
) )
let generateHelp' fail debug = let copyFiles () =
let args = Shell.copyRecursive files output true
if debug then "--define:HELP" |> Trace.logItems "Copying file: "
else "--define:RELEASE --define:HELP" Directory.ensure (output @@ "content")
try Shell.copyRecursive (formatting @@ "styles") (output @@ "content") true
buildDocumentationTarget args "Default" |> Trace.logItems "Copying styles and scripts: "
traceImportant "Help generated"
with Target.create "Docs" (fun _ ->
| e when not fail -> File.delete "docsrc/content/release-notes.md"
traceImportant "generating help documentation failed" Shell.copyFile "docsrc/content/" "RELEASE_NOTES.md"
Shell.rename "docsrc/content/release-notes.md" "docsrc/content/RELEASE_NOTES.md"
let generateHelp fail = File.delete "docsrc/content/license.md"
generateHelp' fail false Shell.copyFile "docsrc/content/" "LICENSE"
Shell.rename "docsrc/content/license.md" "docsrc/content/LICENSE"
Target "GenerateHelp" (fun _ ->
DeleteFile "docsrc/content/release-notes.md" DirectoryInfo.getSubDirectories (DirectoryInfo.ofPath templates)
CopyFile "docsrc/content/" "RELEASE_NOTES.md" |> Seq.iter (fun d ->
Rename "docsrc/content/release-notes.md" "docsrc/content/RELEASE_NOTES.md" let name = d.Name
if name.Length = 2 || name.Length = 3 then
layoutRootsAll.Add(
name, [templates @@ name
formatting @@ "templates"
formatting @@ "templates/reference" ]))
copyFiles ()
for dir in [ content; ] do
let langSpecificPath(lang, path:string) =
path.Split([|'/'; '\\'|], System.StringSplitOptions.RemoveEmptyEntries)
|> Array.exists(fun i -> i = lang)
let layoutRoots =
let key = layoutRootsAll.Keys |> Seq.tryFind (fun i -> langSpecificPath(i, dir))
match key with
| Some lang -> layoutRootsAll.[lang]
| None -> layoutRootsAll.["en"] // "en" is the default language
DeleteFile "docsrc/content/license.md" FSFormatting.createDocs (fun args ->
CopyFile "docsrc/content/" "LICENSE" { args with
Rename "docsrc/content/license.md" "docsrc/content/LICENSE" Source = content
OutputDirectory = output
generateHelp true LayoutRoots = layoutRoots
) ProjectParameters = ("root", root)::info
Template = docTemplate } )
Target "GenerateHelpDebug" (fun _ ->
DeleteFile "docsrc/content/release-notes.md"
CopyFile "docsrc/content/" "RELEASE_NOTES.md"
Rename "docsrc/content/release-notes.md" "docsrc/content/RELEASE_NOTES.md"
DeleteFile "docsrc/content/license.md"
CopyFile "docsrc/content/" "LICENSE"
Rename "docsrc/content/license.md" "docsrc/content/LICENSE"
generateHelp' true true
)
Target "KeepRunning" (fun _ ->
use watcher = !! "docsrc/content/**/*.*" |> WatchChanges (fun changes ->
generateHelp' true true
)
traceImportant "Waiting for help edits. Press any key to stop."
System.Console.ReadKey() |> ignore
watcher.Dispose()
)
Target "GenerateDocs" DoNothing
let createIndexFsx lang =
let content = """(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../../../bin"
(**
F# Project Scaffold ({0})
=========================
*)
"""
let targetDir = "docsrc/content" </> lang
let targetFile = targetDir </> "index.fsx"
ensureDirectory targetDir
System.IO.File.WriteAllText(targetFile, System.String.Format(content, lang))
Target "AddLangDocs" (fun _ ->
let args = System.Environment.GetCommandLineArgs()
if args.Length < 4 then
failwith "Language not specified."
args.[3..]
|> Seq.iter (fun lang ->
if lang.Length <> 2 && lang.Length <> 3 then
failwithf "Language must be 2 or 3 characters (ex. 'de', 'fr', 'ja', 'gsw', etc.): %s" lang
let templateFileName = "template.cshtml"
let templateDir = "docsrc/tools/templates"
let langTemplateDir = templateDir </> lang
let langTemplateFileName = langTemplateDir </> templateFileName
if System.IO.File.Exists(langTemplateFileName) then
failwithf "Documents for specified language '%s' have already been added." lang
ensureDirectory langTemplateDir
Copy langTemplateDir [ templateDir </> templateFileName ]
createIndexFsx lang)
) )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Release Scripts // Release Scripts
#load "paket-files/build/fsharp/FAKE/modules/Octokit/Octokit.fsx" //#load "paket-files/fsharp/FAKE/modules/Octokit/Octokit.fsx"
open Octokit //open Octokit
Target "Release" (fun _ -> Target.create "Release" (fun _ ->
let user = // not fully converted from FAKE 4
match getBuildParam "github-user" with
| s when not (String.IsNullOrWhiteSpace s) -> s
| _ -> getUserInput "Username: "
let pw =
match getBuildParam "github-pw" with
| s when not (String.IsNullOrWhiteSpace s) -> s
| _ -> getUserPassword "Password: "
let remote =
Git.CommandHelper.getGitResult "" "remote -v"
|> Seq.filter (fun (s: string) -> s.EndsWith("(push)"))
|> Seq.tryFind (fun (s: string) -> s.Contains(gitOwner + "/" + gitName))
|> function None -> gitHome + "/" + gitName | Some (s: string) -> s.Split().[0]
StageAll "" //let user =
Git.Commit.Commit "" (sprintf "Bump version to %s" release.NugetVersion) // match getBuildParam "github-user" with
Branches.pushBranch "" remote (Information.getBranchName "") // | s when not (String.isNullOrWhiteSpace s) -> s
// | _ -> getUserInput "Username: "
//let pw =
// match getBuildParam "github-pw" with
// | s when not (String.isNullOrWhiteSpace s) -> s
// | _ -> getUserPassword "Password: "
//let remote =
// Git.CommandHelper.getGitResult "" "remote -v"
// |> Seq.filter (fun (s: string) -> s.EndsWith("(push)"))
// |> Seq.tryFind (fun (s: string) -> s.Contains(gitOwner + "/" + gitName))
// |> function None -> gitHome + "/" + gitName | Some (s: string) -> s.Split().[0]
Branches.tag "" release.NugetVersion //Git.Staging.stageAll ""
Branches.pushTag "" remote release.NugetVersion //Git.Commit.exec "" (sprintf "Bump version to %s" release.NugetVersion)
//Git.Branches.pushBranch "" remote (Git.Information.getBranchName "")
// release on github //Git.Branches.tag "" release.NugetVersion
createClient user pw //Git.Branches.pushTag "" remote release.NugetVersion
|> createDraft gitOwner gitName release.NugetVersion (release.SemVer.PreRelease <> None) release.Notes
// TODO: |> uploadFile "PATH_TO_FILE" //// release on github
|> releaseDraft //GitHub.createClient user pw
|> Async.RunSynchronously //|> createDraft gitOwner gitName release.NugetVersion (release.SemVer.PreRelease <> None) release.Notes
//// TODO: |> uploadFile "PATH_TO_FILE"
//|> releaseDraft
//|> Async.RunSynchronously
// using simplified FAKE 5 release for now
Git.Staging.stageAll ""
Git.Commit.exec "" (sprintf "Bump version to %s" release.NugetVersion)
Git.Branches.push ""
Git.Branches.tag "" release.NugetVersion
Git.Branches.pushTag "" "origin" release.NugetVersion
) )
Target "BuildPackage" DoNothing Target.create "BuildPackage" ignore
Target.create "GenerateDocs" ignore
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Run all targets by default. Invoke 'build <Target>' to override // Run all targets by default. Invoke 'build <Target>' to override
Target "All" DoNothing Target.create "All" ignore
"AssemblyInfo" "Clean"
==> "AssemblyInfo"
==> "Build" ==> "Build"
==> "CopyBinaries" ==> "CopyBinaries"
==> "RunTests" ==> "RunTests"
==> "GenerateReferenceDocs"
==> "GenerateDocs" ==> "GenerateDocs"
==> "NuGet" ==> "NuGet"
==> "BuildPackage"
==> "All" ==> "All"
"GenerateHelp" "RunTests" ?=> "CleanDocs"
==> "GenerateReferenceDocs"
==> "GenerateDocs"
"GenerateHelpDebug" "CleanDocs"
==> "KeepRunning" ==>"Docs"
==> "ReferenceDocs"
==> "GenerateDocs"
"Clean" "Clean"
==> "Release" ==> "Release"
@ -370,4 +368,4 @@ Target "All" DoNothing
==> "PublishNuget" ==> "PublishNuget"
==> "Release" ==> "Release"
RunTargetOrDefault "All" Target.runOrDefault "All"