1 module tests.build;
2 
3 import unit_threaded;
4 import reggae;
5 
6 
7 void testIsLeaf() {
8     Target("tgt").isLeaf.shouldBeTrue;
9     Target("other", "", [Target("foo"), Target("bar")]).isLeaf.shouldBeFalse;
10     Target("implicits", "", [], [Target("foo")]).isLeaf.shouldBeFalse;
11 }
12 
13 
14 void testInOut() {
15     //Tests that specifying $in and $out in the command string gets substituted correctly
16     {
17         const target = Target("foo",
18                               "createfoo -o $out $in",
19                               [Target("bar.txt"), Target("baz.txt")]);
20         target.expandCommand.shouldEqual("createfoo -o foo bar.txt baz.txt");
21     }
22     {
23         const target = Target("tgt",
24                               "gcc -o $out $in",
25                               [
26                                   Target("src1.o", "gcc -c -o $out $in", [Target("src1.c")]),
27                                   Target("src2.o", "gcc -c -o $out $in", [Target("src2.c")])
28                                   ],
29             );
30         target.expandCommand.shouldEqual("gcc -o tgt src1.o src2.o");
31     }
32 
33     {
34         const target = Target(["proto.h", "proto.c"],
35                               "protocompile $out -i $in",
36                               [Target("proto.idl")]);
37         target.expandCommand.shouldEqual("protocompile proto.h proto.c -i proto.idl");
38     }
39 
40     {
41         const target = Target("lib1.a",
42                               "ar -o$out $in",
43                               [Target(["foo1.o", "foo2.o"], "cmd", [Target("tmp")]),
44                                Target("bar.o"),
45                                Target("baz.o")]);
46         target.expandCommand.shouldEqual("ar -olib1.a foo1.o foo2.o bar.o baz.o");
47     }
48 }
49 
50 
51 void testProject() {
52     const target = Target("foo",
53                           "makefoo -i $in -o $out -p $project",
54                           [Target("bar"), Target("baz")]);
55     target.expandCommand("/tmp").shouldEqual("makefoo -i /tmp/bar /tmp/baz -o foo -p /tmp");
56 }
57 
58 
59 void testMultipleOutputs() {
60     const target = Target(["foo.hpp", "foo.cpp"], "protocomp $in", [Target("foo.proto")]);
61     target.outputs.shouldEqual(["foo.hpp", "foo.cpp"]);
62     target.expandCommand("myproj").shouldEqual("protocomp myproj/foo.proto");
63 
64     const bld = Build(target);
65     bld.targets.array[0].outputs.shouldEqual(["foo.hpp", "foo.cpp"]);
66 }
67 
68 
69 void testInTopLevelObjDir() {
70 
71     const theApp = Target("theapp");
72     const dirName = topLevelDirName(theApp);
73     const fooObj = Target("foo.o", "", [Target("foo.c")]);
74     fooObj.inTopLevelObjDirOf(dirName).shouldEqual(
75         Target("objs/theapp.objs/foo.o", "", [Target("foo.c")]));
76 
77     const barObjInBuildDir = Target("$builddir/bar.o", "", [Target("bar.c")]);
78     barObjInBuildDir.inTopLevelObjDirOf(dirName).shouldEqual(
79         Target("bar.o", "", [Target("bar.c")]));
80 
81     const leafTarget = Target("foo.c");
82     leafTarget.inTopLevelObjDirOf(dirName).shouldEqual(leafTarget);
83 }
84 
85 
86 void testMultipleOutputsImplicits() {
87     const protoSrcs = Target([`$builddir/gen/protocol.c`, `$builddir/gen/protocol.h`],
88                              `./compiler $in`,
89                              [Target(`protocol.proto`)]);
90     const protoObj = Target(`$builddir/bin/protocol.o`,
91                             `gcc -o $out -c $builddir/gen/protocol.c`,
92                             [], [protoSrcs]);
93     const protoD = Target(`$builddir/gen/protocol.d`,
94                           `echo "extern(C) " > $out; cat $builddir/gen/protocol.h >> $out`,
95                           [], [protoSrcs]);
96     const app = Target(`app`,
97                        `dmd -of$out $in`,
98                        [Target(`src/main.d`), protoObj, protoD]);
99     const build = Build(app);
100 
101     const newProtoSrcs = Target([`gen/protocol.c`, `gen/protocol.h`],
102                                 `./compiler $in`,
103                                 [Target(`protocol.proto`)]);
104     const newProtoD = Target(`gen/protocol.d`,
105                              `echo "extern(C) " > $out; cat gen/protocol.h >> $out`,
106                              [], [newProtoSrcs]);
107 
108     build.targets.array.shouldEqual(
109         [Target("app", "dmd -of$out $in",
110                 [Target("src/main.d"),
111                  Target("bin/protocol.o", "gcc -o $out -c gen/protocol.c",
112                         [], [newProtoSrcs]),
113                  newProtoD])]
114         );
115 }
116 
117 
118 void testRealTargetPath() {
119     const fooLib = Target("$project/foo.so", "dmd -of$out $in", [Target("src1.d"), Target("src2.d")]);
120     const barLib = Target("$builddir/bar.so", "dmd -of$out $in", [Target("src1.d"), Target("src2.d")]);
121     const symlink1 = Target("$project/weird/path/thingie1", "ln -sf $in $out", fooLib);
122     const symlink2 = Target("$project/weird/path/thingie2", "ln -sf $in $out", fooLib);
123     const symlinkBar = Target("$builddir/weird/path/thingie2", "ln -sf $in $out", fooLib);
124 
125     immutable dirName = "/made/up/dir";
126 
127     realTargetPath(dirName, symlink1.outputs[0]).shouldEqual("$project/weird/path/thingie1");
128     realTargetPath(dirName, symlink2.outputs[0]).shouldEqual("$project/weird/path/thingie2");
129     realTargetPath(dirName, fooLib.outputs[0]).shouldEqual("$project/foo.so");
130 
131 
132     realTargetPath(dirName, symlinkBar.outputs[0]).shouldEqual("weird/path/thingie2");
133     realTargetPath(dirName, barLib.outputs[0]).shouldEqual("bar.so");
134 
135 }
136 
137 
138 void testOptional() {
139     enum foo = Target("foo", "dmd -of$out $in", Target("foo.d"));
140     enum bar = Target("bar", "dmd -of$out $in", Target("bar.d"));
141 
142     optional(bar).target.shouldEqual(bar);
143     mixin build!(foo, optional(bar));
144     auto build = buildFunc();
145     build.targets.array[1].shouldEqual(bar);
146 }
147 
148 
149 void testDiamondDeps() {
150     const src1 = Target("src1.d");
151     const src2 = Target("src2.d");
152     const obj1 = Target("obj1.o", "dmd -of$out -c $in", src1);
153     const obj2 = Target("obj2.o", "dmd -of$out -c $in", src2);
154     const fooLib = Target("$project/foo.so", "dmd -of$out $in", [obj1, obj2]);
155     const symlink1 = Target("$project/weird/path/thingie1", "ln -sf $in $out", fooLib);
156     const symlink2 = Target("$project/weird/path/thingie2", "ln -sf $in $out", fooLib);
157     const build = Build(symlink1, symlink2);
158 
159     const newObj1 = Target("objs/$project/foo.so.objs/obj1.o", "dmd -of$out -c $in", src1);
160     const newObj2 = Target("objs/$project/foo.so.objs/obj2.o", "dmd -of$out -c $in", src2);
161     const newFooLib = Target("$project/foo.so", "dmd -of$out $in", [newObj1, newObj2]);
162     const newSymlink1 = Target("$project/weird/path/thingie1", "ln -sf $in $out", newFooLib);
163     const newSymlink2 = Target("$project/weird/path/thingie2", "ln -sf $in $out", newFooLib);
164 
165     build.range.array.shouldEqual([newObj1, newObj2, newFooLib, newSymlink1, newSymlink2]);
166 }
167 
168 void testPhobosOptionalBug() {
169     enum obj1 = Target("obj1.o", "dmd -of$out -c $in", Target("src1.d"));
170     enum obj2 = Target("obj2.o", "dmd -of$out -c $in", Target("src2.d"));
171     enum foo = Target("foo", "dmd -of$out $in", [obj1, obj2]);
172     Target bar() {
173         return Target("bar", "dmd -of$out $in", [obj1, obj2]);
174     }
175     mixin build!(foo, optional!(bar));
176     const build = buildFunc();
177 
178     const fooObj1 = Target("objs/foo.objs/obj1.o", "dmd -of$out -c $in", Target("src1.d"));
179     const fooObj2 = Target("objs/foo.objs/obj2.o", "dmd -of$out -c $in", Target("src2.d"));
180     const newFoo = Target("foo", "dmd -of$out $in", [fooObj1, fooObj2]);
181 
182     const barObj1 = Target("objs/bar.objs/obj1.o", "dmd -of$out -c $in", Target("src1.d"));
183     const barObj2 = Target("objs/bar.objs/obj2.o", "dmd -of$out -c $in", Target("src2.d"));
184     const newBar = Target("bar", "dmd -of$out $in", [barObj1, barObj2]);
185 
186     build.range.array.shouldEqual([fooObj1, fooObj2, newFoo, barObj1, barObj2, newBar]);
187 }
188 
189 
190 void testOutputsInProjectPath() {
191     const mkDir = Target("$project/foodir", "mkdir -p $out", [], []);
192     mkDir.outputsInProjectPath("/path/to/proj").shouldEqual(["/path/to/proj/foodir"]);
193 }
194 
195 
196 void testExpandOutputs() {
197     const foo = Target("$project/foodir", "mkdir -p $out", [], []);
198     foo.expandOutputs("/path/to/proj").array.shouldEqual(["/path/to/proj/foodir"]);
199 
200     const bar = Target("$builddir/foodir", "mkdir -p $out", [], []);
201     bar.expandOutputs("/path/to/proj").array.shouldEqual(["foodir"]);
202 }
203 
204 
205 void testCommandBuilddir() {
206     const cmd = Command("dmd -of$builddir/ut_debug $in");
207     cmd.shellCommand("/path/to/proj", Language.unknown, ["$builddir/ut_debug"], ["foo.d"]).
208         shouldEqual("dmd -ofut_debug foo.d");
209 }
210 
211 
212 void testBuilddirInTopLevelTarget() {
213     const ao = objectFile(SourceFile("a.c"));
214     const liba = Target("$builddir/liba.a", "ar rcs liba.a a.o", [ao]);
215     mixin build!(liba);
216     const build = buildFunc();
217     build.targets[0].outputs.shouldEqual(["liba.a"]);
218 }