-
Notifications
You must be signed in to change notification settings - Fork 9
/
digger.d
278 lines (240 loc) · 8.36 KB
/
digger.d
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
module digger;
import std.array;
import std.exception;
import std.file : thisExePath, exists;
import std.path;
import std.process;
import std.stdio;
import std.typetuple;
static if(!is(typeof({import ae.utils.text;}))) static assert(false, "ae library not found, did you clone with --recursive?"); else:
version (Windows)
static import ae.sys.net.wininet;
else
static import ae.sys.net.curl;
import ae.sys.d.manager : DManager;
import ae.utils.funopt;
import ae.utils.main;
import ae.utils.meta : structFun;
import ae.utils.text : eatLine;
import bisect;
import common;
import config;
import custom;
import install;
import repo;
// http://d.puremagic.com/issues/show_bug.cgi?id=7016
version(Windows) static import ae.sys.windows;
alias BuildOptions(string action, string pastAction, bool showBuildActions = true) = TypeTuple!(
Switch!(hiddenOption, 0, "64"),
Option!(string, showBuildActions ? "Select models (32, 64, or, on Windows, 32mscoff). You can specify multiple models by comma-separating them.\nOn this system, the default is " ~ DManager.Config.Build.components.common.defaultModel ~ " [build.components.common.models]" : hiddenOption, null, 0, "model"),
Option!(string[], "Do not " ~ action ~ " a component (that would otherwise be " ~ pastAction ~ " by default). List of default components: " ~ DManager.defaultComponents.join(", ") ~ " [build.components.enable.COMPONENT=false]", "COMPONENT", 0, "without"),
Option!(string[], "Specify an additional D component to " ~ action ~ ". List of available additional components: " ~ DManager.additionalComponents.join(", ") ~ " [build.components.enable.COMPONENT=true]", "COMPONENT", 0, "with"),
Option!(string[], showBuildActions ? `Additional make parameters, e.g. "HOST_CC=g++48" [build.components.common.makeArgs]` : hiddenOption, "ARG", 0, "makeArgs"),
Switch!(showBuildActions ? "Bootstrap the compiler (build from C++ source code) instead of downloading a pre-built binary package [build.components.dmd.bootstrap.fromSource]" : hiddenOption, 0, "bootstrap"),
Switch!(hiddenOption, 0, "use-vc"),
Switch!(hiddenOption, 0, "clobber-local-changes"),
);
enum specDescription = "D ref (branch / tag / point in time) to build, plus any additional forks or pull requests. Example:\n" ~
"\"master @ 3 weeks ago + dmd#123 + You/dmd/awesome-feature\"";
void parseBuildOptions(T...)(T options) // T == BuildOptions!action
{
if (options[0])
d.config.build.components.common.models = ["64"];
if (options[1])
d.config.build.components.common.models = options[1].value.split(",");
foreach (componentName; options[2])
d.config.build.components.enable[componentName] = false;
foreach (componentName; options[3])
d.config.build.components.enable[componentName] = true;
d.config.build.components.common.makeArgs ~= options[4];
d.config.build.components.dmd.bootstrap.fromSource |= options[5];
d.config.build.components.dmd.useVC |= options[6];
d.verifyWorkTree = !options[7];
static assert(options.length == 8);
}
struct Digger
{
static:
@(`Build D from source code`)
int build(BuildOptions!("build", "built") options, Parameter!(string, specDescription) spec = "master")
{
parseBuildOptions(options);
buildCustom(spec);
return 0;
}
@(`Incrementally rebuild the current D checkout`)
int rebuild(BuildOptions!("rebuild", "rebuilt") options)
{
parseBuildOptions(options);
incrementalBuild();
return 0;
}
@(`Run tests for enabled components`)
int test(BuildOptions!("test", "tested") options)
{
parseBuildOptions(options);
runTests();
return 0;
}
@(`Check out D source code from git`)
int checkout(BuildOptions!("check out", "checked out", false) options, Parameter!(string, specDescription) spec = "master")
{
parseBuildOptions(options);
.checkout(spec);
return 0;
}
@(`Run a command using a D version`)
int run(
BuildOptions!("build", "built") options,
Parameter!(string, specDescription ~ "\nSpecify \"-\" to use the previously-built version.") spec,
Parameter!(string[], "Command to run and its arguments (use -- to pass switches)") command)
{
if (spec == "-")
enforce(options == typeof(options).init, "Can't specify build options when using the last built version.");
else
{
parseBuildOptions(options);
buildCustom(spec, /*asNeeded*/true);
}
auto binPath = resultDir.buildPath("bin").absolutePath();
environment["PATH"] = binPath ~ pathSeparator ~ environment["PATH"];
version (Windows)
return spawnProcess(command).wait();
else
{
execvp(command[0], command);
errnoEnforce(false, "execvp failed");
assert(false); // unreachable
}
}
@(`Install Digger's build result on top of an existing stable DMD installation`)
int install(
Switch!("Do not prompt", 'y') yes,
Switch!("Only print what would be done", 'n') dryRun,
Parameter!(string, "Directory to install to. Default is to find one in PATH.") installLocation = null,
)
{
enforce(!yes || !dryRun, "--yes and --dry-run are mutually exclusive");
.install.install(yes, dryRun, installLocation);
return 0;
}
@(`Undo the "install" action`)
int uninstall(
Switch!("Only print what would be done", 'n') dryRun,
Switch!("Do not verify files to be deleted; ignore errors") force,
Parameter!(string, "Directory to uninstall from. Default is to search PATH.") installLocation = null,
)
{
.uninstall(dryRun, force, installLocation);
return 0;
}
@(`Bisect D history according to a bisect.ini file`)
int bisect(
Switch!("Skip sanity-check of the GOOD/BAD commits.") noVerify,
Option!(string[], "Additional bisect configuration. Equivalent to bisect.ini settings.", "NAME=VALUE", 'c', "config") configLines,
Parameter!(string, "Location of the bisect.ini file containing the bisection description.") bisectConfigFile = null,
)
{
return doBisect(noVerify, bisectConfigFile, configLines);
}
@(`Cache maintenance actions (run with no arguments for details)`)
int cache(string[] args)
{
static struct CacheActions
{
static:
@(`Compact the cache`)
int compact()
{
d.optimizeCache();
return 0;
}
@(`Delete entries cached as unbuildable`)
int purgeUnbuildable()
{
d.purgeUnbuildable();
return 0;
}
@(`Migrate cached entries from one cache engine to another`)
int migrate(string source, string target)
{
d.migrateCache(source, target);
return 0;
}
}
return funoptDispatch!CacheActions(["digger cache"] ~ args);
}
// hidden actions
int buildAll(BuildOptions!("build", "built") options, string spec = "master")
{
parseBuildOptions(options);
.buildAll(spec);
return 0;
}
int delve(bool inBisect)
{
return doDelve(inBisect);
}
int parseRev(string rev)
{
stdout.writeln(.parseRev(rev));
return 0;
}
int show(string revision)
{
d.getMetaRepo().git.run("log", "-n1", revision);
d.getMetaRepo().git.run("log", "-n1", "--pretty=format:t=%ct", revision);
return 0;
}
int getLatest()
{
writeln((cast(DManager.Website)d.getComponent("website")).getLatest());
return 0;
}
int help()
{
throw new Exception("For help, run digger without any arguments.");
}
version (Windows)
int getAllMSIs()
{
d.getVSInstaller().getAllMSIs();
return 0;
}
}
int digger()
{
version (D_Coverage)
{
import core.runtime;
dmd_coverSetMerge(true);
}
static void usageFun(string usage)
{
import std.algorithm, std.array, std.stdio, std.string;
auto lines = usage.splitLines();
stderr.writeln("Digger v" ~ diggerVersion ~ " - a D source code building and archaeology tool");
stderr.writeln("Created by Vladimir Panteleev <vladimir@thecybershadow.net>");
stderr.writeln("https://github.com/CyberShadow/Digger");
stderr.writeln();
stderr.writeln("Configuration file: ", opts.configFile.value.exists ? opts.configFile.value : "(not present)");
stderr.writeln("Working directory: ", config.config.local.workDir);
stderr.writeln();
if (lines[0].canFind("ACTION [ACTION-ARGUMENTS]"))
{
lines =
[lines[0].replace(" ACTION ", " [OPTION]... ACTION ")] ~
getUsageFormatString!(structFun!Opts).splitLines()[1..$] ~
lines[1..$];
stderr.writefln("%-(%s\n%)", lines);
stderr.writeln();
stderr.writeln("For help on a specific action, run: digger ACTION --help");
stderr.writeln("For more information, see README.md.");
stderr.writeln();
}
else
stderr.writefln("%-(%s\n%)", lines);
}
return funoptDispatch!(Digger, FunOptConfig.init, usageFun)([thisExePath] ~ (opts.action ? [opts.action.value] ~ opts.actionArguments : []));
}
mixin main!digger;