-
Notifications
You must be signed in to change notification settings - Fork 0
/
DeterminismExtracts.fs
143 lines (123 loc) · 3.9 KB
/
DeterminismExtracts.fs
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
module Scripts.DeterminismExtracts
open System
open System.IO
open System.Security.Cryptography
open Newtonsoft.Json
open Scripts.ArgsFile
open Serilog
open Scripts.Utils
[<CLIMutable>]
type ExtractCore =
{
Mvid : string
DllHash : string
PdbHash : string
RefHash : string
Project : string
SigDataHash : string
}
[<CLIMutable>]
type ExtractMeta =
{
DllTimestamp : DateTime
Name : string
Directory : string
}
[<CLIMutable>]
type Extract =
{
Core : ExtractCore
Meta : ExtractMeta
}
/// Paths of various files generated during compilation.
module Paths =
let mvid = "mvid.txt"
let dll = "out.dll"
let pdb = "out.pdb"
let ref = "ref.dll"
let args = "fscargs.txt"
let extract = "extract.json"
let sigData : string = "out.signature-data.json"
let getFileHash (file : string) =
if not (File.Exists(file)) then
"File does not exist"
else
use md5 = MD5.Create()
use stream = File.OpenRead(file)
let hash = md5.ComputeHash(stream)
BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant()
let getExtract (project : string) (extractName : string) (dir : string) =
let subPath name = Path.Combine(dir, name)
let dll = subPath Paths.dll
{
Extract.Core =
{
ExtractCore.Mvid = File.ReadAllText(subPath Paths.mvid).Trim()
DllHash = getFileHash dll
PdbHash = getFileHash (subPath Paths.pdb)
RefHash = getFileHash (subPath Paths.ref)
Project = project
SigDataHash = getFileHash (subPath Paths.sigData)
}
Extract.Meta =
{
ExtractMeta.Directory = dir
DllTimestamp = File.GetLastWriteTimeUtc(dll)
Name = extractName
}
}
type ProjectWithArgs =
{
/// fsproj path
Project : string
Args : SArgs
}
/// Compile a project and extract information out of it helpful in determinism investigations
let compileAndExtract
(useTmpDir : bool)
(name : string)
(baseDir : string)
(projectArgs : ProjectWithArgs)
(fscDll : string)
: Extract
=
let finalDir = Path.Combine(baseDir, name)
let outputDir =
if useTmpDir then
Log.Information("compileAndExtract {finalDir}", finalDir)
"testoutput"
else
finalDir
let outputDir = Path.Combine(Environment.CurrentDirectory, outputDir)
Directory.CreateDirectory(outputDir) |> ignore
let subPath name = Path.Combine(outputDir, name)
let {Project = project; Args = args} = projectArgs
let workDir = Path.GetDirectoryName(project)
let dllPath = subPath Paths.dll
let args =
args
|> SArgs.setOutput (Path.GetRelativePath(workDir, subPath Paths.dll))
|> SArgs.setKeyValue "--refout" (Path.GetRelativePath(workDir, subPath Paths.ref))
|> SArgs.setKeyValue "--debug" "portable"
let argsFile = subPath Paths.args
args
|> SArgs.toFile argsFile
CliWrap.Cli
.Wrap("dotnet")
.WithWorkingDirectory(workDir)
.WithArguments($"{fscDll} @{argsFile}")
.ExecuteAssertSuccess()
let mvid = MvidReader.getMvid dllPath
let mvidPath = subPath Paths.mvid
File.WriteAllText(mvidPath, mvid.ToString())
let extract = getExtract project name outputDir
let json = JsonConvert.SerializeObject(extract, Formatting.Indented)
File.WriteAllText(subPath Paths.extract, json)
if useTmpDir then
if Directory.Exists finalDir then
failwith $"Output directory {finalDir} exists."
// Make sure parent directory exists
Directory.CreateDirectory(finalDir) |> ignore
Directory.Delete(finalDir)
Directory.Move(outputDir, finalDir)
extract