forked from MiloszKrajewski/K4os.Compression.LZ4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.tools.fsx
217 lines (194 loc) · 10.1 KB
/
build.tools.fsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#r ".fake/FakeLib.dll"
namespace Fake
open Fake
open System
open System.IO
open System.Net
open System.Text.RegularExpressions
open System.Diagnostics
[<AutoOpen>]
module Fx =
let inline tap f a = f a; a
let inline forgive f a = try f a with | _ -> ()
module String =
let quote (arg: string) = if arg.Contains(" ") then sprintf "\"%s\"" arg else arg
let replace pattern (replacement: string) (input: string) = input.Replace(pattern, replacement)
let same textA textB = String.Compare(textA, textB, true) = 0
module Option =
let inline def v o = defaultArg o v
let inline alt a o = match o with | None -> a | _ -> o
let inline altGet a o = match o with | None -> a () | _ -> o
let inline cast<'a> (o: obj) = match o with | :? 'a as v -> Some v | _ -> None
let inline fromRef o = match o with | null -> None | _ -> Some o
module File =
let update modifier inputFile =
let outputFile = Path.GetTempFileName()
inputFile |> CopyFile outputFile
modifier outputFile
if CompareFiles true inputFile outputFile |> not then
tracefn "File %s has been modified. Overwriting." inputFile
outputFile |> Rename (inputFile |> tap DeleteFile)
let touch filename =
if File.Exists(filename)
then FileInfo(filename).LastWriteTimeUtc <- DateTime.UtcNow
else [] |> WriteFile filename
let exists filename = File.Exists(filename)
let loadText filename = File.ReadAllText(filename)
let saveText filename text = File.WriteAllText(filename, text)
let download fn (url: string) =
if not (exists fn) then
printfn "Downloading: %s" url
use wc = new WebClient()
ServicePointManager.SecurityProtocol <-
ServicePointManager.SecurityProtocol
||| SecurityProtocolType.Tls
||| SecurityProtocolType.Tls11
||| SecurityProtocolType.Tls12
wc.DownloadFile(url, fn)
module Regex =
let create ignoreCase pattern =
let ignoreCase = if ignoreCase then RegexOptions.IgnoreCase else RegexOptions.None
Regex(pattern, RegexOptions.ExplicitCapture ||| RegexOptions.IgnorePatternWhitespace ||| ignoreCase)
let replace pattern (replacement: string) input = Regex.Replace(input, pattern, replacement)
let matches pattern input = Regex.IsMatch(input, pattern)
let (|Match|_|) (pattern: Regex) text =
match pattern.Match(text) with | m when m.Success -> Some m | _ -> None
module Shell =
let mono = "Mono.Runtime" |> Type.GetType |> isNull |> not
let runAt directory executable arguments =
let command = sprintf "%s %s" (String.quote executable) arguments |> tap (tracefn "> %s")
let comspec, comspecArgs = if mono then "bash", "-c" else environVarOrFail "COMSPEC", "/c"
let info = ProcessStartInfo(comspec, (comspecArgs, command) ||> sprintf "%s \"%s\"", UseShellExecute = false, WorkingDirectory = directory)
let proc = Process.Start(info)
proc.WaitForExit()
match proc.ExitCode with | 0 -> () | c -> failwithf "Execution failed with error code %d" c
let run executable arguments = runAt "." executable arguments
module Config =
type Item = { Section: string; Key: string; Value: string }
let private sectionRx = """^\s*\[\s*(?<name>.*?)\s*\]\s*$""" |> Regex.create true
let private valueRx = """^\s*(?<key>.*?)\s*(=\s*(?<value>.*?)\s*)?$""" |> Regex.create true
let private emptyRx = """^\s*(;.*)?$""" |> Regex.create true
let validate (items: Item seq) = items |> Seq.distinctBy (fun i -> i.Section, i.Key) |> List.ofSeq
let merge configs = configs |> Seq.collect id |> validate
let load (lines: seq<string>) =
let rec parse lines section result =
match lines with
| [] -> result
| line :: lines ->
match line with
| Regex.Match emptyRx _ -> parse lines section result
| Regex.Match sectionRx m ->
let section = m.Groups.["name"].Value
parse lines section result
| Regex.Match valueRx m ->
let key = m.Groups.["key"].Value
let value = match m.Groups.["value"] with | m when m.Success -> m.Value | _ -> ""
let item = { Section = section; Key = key; Value = value }
parse lines section (item :: result)
| _ ->
printfn "Line '%s' does not match config pattern as has been ignored" line
parse lines section result
parse (lines |> List.ofSeq) "" [] |> List.rev |> validate
let tryLoadFile fileName = if File.Exists(fileName) then fileName |> ReadFile |> load else []
let items section (config: Item seq) = config |> Seq.filter (fun i -> i.Section = section)
let keys section (config: Item seq) = config |> items section |> Seq.map (fun i -> i.Key)
let value section key (config: Item seq) =
config |> items section |> Seq.filter (fun i -> i.Key = key) |> Seq.tryHead |> Option.map (fun i -> i.Value)
let valueOrDefault section key defaultValue config =
config |> value section key |> Option.def defaultValue
let valueOrFail section key config =
match config |> value section key with
| Some v -> v | None -> failwithf "Value %s:%s could not be found" section key
module Proj =
let outputFolder = ".output"
let releaseNotes = "./CHANGES.md" |> ReleaseNotesHelper.LoadReleaseNotes
let timestamp =
let baseline = DateTime.Parse("2000-01-01T00:00:00") // ~y2k
DateTime.Now.Subtract(baseline).TotalSeconds |> int |> sprintf "%8x"
let productVersion = releaseNotes.NugetVersion |> Regex.replace "-wip$" (timestamp |> sprintf "-wip%s")
let assemblyVersion = releaseNotes.AssemblyVersion
let settings = [ "settings.config"; ".secrets.config" ] |> Seq.map Config.tryLoadFile |> Config.merge
let listProj () =
let isValidProject projectPath =
let projectFolder = projectPath |> directory |> filename
let projectName = projectPath |> fileNameWithoutExt
String.same projectFolder projectName
!! "src/*/*.*proj" |> Seq.filter isValidProject
let findSln name = !! (sprintf "src/%s.sln" name)
let findProj name = !! (sprintf "src/%s/%s.??proj" name name)
let isTestProj projectFile = projectFile |> directory |> Regex.matches "Test$"
let restore solution =
solution
|> findSln
|> Seq.iter (fun sln -> DotNetCli.Restore (fun p -> { p with Project = sln }))
let build solution =
solution
|> findSln
|> Seq.iter (fun sln -> DotNetCli.Build (fun p -> { p with Configuration = "Release"; Project = sln }))
let test project = DotNetCli.Test (fun p ->
{ p with
Configuration = "Release"
Project = project
AdditionalArgs = ["--no-build"; "--no-restore"]
})
let testAll () = listProj () |> Seq.filter isTestProj |> Seq.iter test
let xtest project =
Shell.runAt project "dotnet" "xunit -verbose -configuration Release -nobuild"
let xtestAll () =
listProj () |> Seq.filter isTestProj |> Seq.iter (directory >> xtest)
let pack version project =
DotNetCli.Pack (fun p ->
{ p with
Project = project
Configuration = "Release"
OutputPath = outputFolder |> FullName
AdditionalArgs = ["--no-build"; "--no-restore"] // "--include-symbols"
})
let versionFile = outputFolder @@ (project |> filename) |> sprintf "%s.nupkg.latest"
[ version ] |> WriteFile versionFile
let publish targetFolder project =
DotNetCli.Publish (fun p ->
{ p with
Project = project
Configuration = "Release"
Output = targetFolder |> FullName
AdditionalArgs = ["--no-restore"; "/p:BuildProjectReferences=false"] // "--no-build"
})
let publishNugetOrg accessKey project =
let version = outputFolder @@ project + ".nupkg.latest" |> ReadFile |> Seq.head
let nupkg = project + "." + version + ".nupkg"
Shell.runAt outputFolder "dotnet" (sprintf "nuget push -s https://www.nuget.org/api/v2/package %s -k %s" nupkg accessKey)
let updateVersion nugetVersion fileVersion projectFile =
projectFile |> File.update (forgive (fun fn ->
XmlPokeInnerText fn "/Project/PropertyGroup/Version" nugetVersion
XmlPokeInnerText fn "/Project/PropertyGroup/AssemblyVersion" fileVersion
XmlPokeInnerText fn "/Project/PropertyGroup/FileVersion" fileVersion
))
let fixPackReferences folder =
let fileMissing filename = File.Exists(filename) |> not
!! (folder @@ "**/paket.references")
|> Seq.map directory
|> Seq.iter (fun projectPath ->
let projectName = projectPath |> filename
!! (projectPath @@ (projectName |> sprintf "%s.*proj"))
|> Seq.iter (fun projectFile ->
let referenceFile = projectPath @@ "obj" @@ (filename projectFile) + ".references"
let nuspecFile = projectPath @@ "obj" @@ projectName + "." + productVersion + ".nuspec"
// recreate 'projectName.csproj.refereces' even if it is empty
if referenceFile |> fileMissing then
referenceFile
|> tap (tracefn "Creating: %s")
|> tap (directory >> CreateDir)
|> File.touch
// delete 'old' .nuspec (they are messing with pack)
!! (projectPath @@ "obj" @@ "*.nuspec") -- nuspecFile |> DeleteFiles
)
)
let packMany projects =
updateVersion productVersion assemblyVersion "Common.targets"
let projectFiles = projects |> Seq.map (fun p -> sprintf "src/%s/%s.*proj" p p) |> Seq.collect (!!) |> Seq.toArray
let folders = projectFiles |> Seq.map directory |> Seq.distinct |> Seq.toArray
folders |> Seq.iter (fun folder ->
folders |> Seq.iter fixPackReferences
pack productVersion folder
)