/
build.d
170 lines (138 loc) · 5.31 KB
/
build.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
module reggae.build;
import std.string: replace;
import std.algorithm: map;
import std.path: buildPath;
import std.typetuple: allSatisfy;
import std.traits: Unqual, isSomeFunction, ReturnType, arity;
import std.array: array, join;
struct Build {
const(Target)[] targets;
this(T...)(in T targets) {
foreach(t; targets) {
static if(isSomeFunction!(typeof(t))) {
const target = t();
} else {
const target = t;
}
this.targets ~= Target(target.outputs,
target._command.removeBuilddir,
target.dependencies.map!(a => a.enclose(target)).array,
target.implicits.map!(a => a.enclose(target)).array);
}
}
}
//a directory for each top-level target no avoid name clashes
//@trusted because of map -> buildPath -> array
Target enclose(in Target target, in Target topLevel) @trusted {
if(target.isLeaf) return Target(target.outputs.map!(a => a.removeBuilddir).array,
target._command.removeBuilddir,
target.dependencies,
target.implicits);
immutable dirName = buildPath("objs", topLevel.outputs[0] ~ ".objs");
return Target(target.outputs.map!(a => realTargetPath(dirName, a)).array,
target._command.removeBuilddir,
target.dependencies.map!(a => a.enclose(topLevel)).array,
target.implicits.map!(a => a.enclose(topLevel)).array);
}
immutable gBuilddir = "$builddir";
private string realTargetPath(in string dirName, in string output) @trusted pure {
import std.algorithm: canFind;
return output.canFind(gBuilddir)
? output.removeBuilddir
: buildPath(dirName, output);
}
private string removeBuilddir(in string output) @trusted pure {
import std.path: buildNormalizedPath;
import std.algorithm;
return output.
splitter.
map!(a => a.canFind(gBuilddir) ? a.replace(gBuilddir, ".").buildNormalizedPath : a).
join(" ");
}
enum isTarget(alias T) = is(Unqual!(typeof(T)) == Target) ||
isSomeFunction!T && is(ReturnType!T == Target);
unittest {
auto t1 = Target();
const t2 = Target();
static assert(isTarget!t1);
static assert(isTarget!t2);
}
mixin template build(T...) if(allSatisfy!(isTarget, T)) {
Build buildFunc() {
return Build(T);
}
}
package template isBuildFunction(alias T) {
static if(!isSomeFunction!T) {
enum isBuildFunction = false;
} else {
enum isBuildFunction = is(ReturnType!T == Build) && arity!T == 0;
}
}
unittest {
Build myBuildFunction() { return Build(); }
static assert(isBuildFunction!myBuildFunction);
float foo;
static assert(!isBuildFunction!foo);
}
struct Target {
const(string)[] outputs;
const(Target)[] dependencies;
const(Target)[] implicits;
this(in string output) @safe pure nothrow {
this(output, null, null);
}
this(in string output, string command, in Target dependency,
in Target[] implicits = []) @safe pure nothrow {
this([output], command, [dependency], implicits);
}
this(in string output, string command,
in Target[] dependencies, in Target[] implicits = []) @safe pure nothrow {
this([output], command, dependencies, implicits);
}
this(in string[] outputs, string command,
in Target[] dependencies, in Target[] implicits = []) @safe pure nothrow {
this.outputs = outputs;
this.dependencies = dependencies;
this.implicits = implicits;
this._command = command;
}
@property string dependencyFiles(in string projectPath = "") @safe const nothrow {
return depFilesImpl(dependencies, projectPath);
}
@property string implicitFiles(in string projectPath = "") @safe const nothrow {
return depFilesImpl(implicits, projectPath);
}
@property string command(in string projectPath = "") @trusted pure const nothrow {
//functional didn't work here, I don't know why so sticking with loops for now
string[] depOutputs;
foreach(dep; dependencies) {
foreach(output; dep.outputs) {
depOutputs ~= dep.isLeaf ? buildPath(projectPath, output) : output;
}
}
auto replaceIn = _command.replace("$in", depOutputs.join(" "));
auto replaceOut = replaceIn.replace("$out", outputs.join(" "));
return replaceOut.replace("$project", projectPath);
}
bool isLeaf() @safe pure const nothrow {
return dependencies is null && implicits is null;
}
//@trusted because of replace
package string inOutCommand(in string projectPath = "") @trusted pure nothrow const {
return _command.replace("$project", projectPath);
}
private:
string _command;
//@trusted because of join
string depFilesImpl(in Target[] deps, in string projectPath) @trusted const nothrow {
import std.conv;
string files;
//join doesn't do const, resort to loops
foreach(i, dep; deps) {
files ~= text(dep.outputs.map!(a => dep.isLeaf ? buildPath(projectPath, a) : a).join(" "));
if(i != deps.length - 1) files ~= " ";
}
return files;
}
}