1 module tests.ut.ninja;
2 
3 import unit_threaded;
4 import reggae;
5 import reggae.options;
6 
7 
8 void testEmpty() {
9     auto ninja = Ninja();
10     ninja.buildEntries.shouldEqual([]);
11     ninja.ruleEntries.shouldEqual([]);
12 }
13 
14 void testCppLinker() {
15     auto ninja = Ninja(Build(Target("mybin",
16                                      "/usr/bin/c++ $in -o $out",
17                                      [Target("foo.o"), Target("bar.o")],
18                                   )));
19     ninja.buildEntries.shouldEqual([NinjaEntry("build mybin: cpp foo.o bar.o",
20                                                ["between = -o"])
21                                        ]);
22     ninja.ruleEntries.shouldEqual([NinjaEntry("rule cpp",
23                                               ["command = /usr/bin/c++ $in $between $out"])
24                                       ]);
25 }
26 
27 void testCppLinkerProjectPath() {
28     auto ninja = Ninja(Build(Target("mybin",
29                                      "/usr/bin/c++ $in -o $out",
30                                      [Target("foo.o"), Target("bar.o")],
31                                   )),
32                         "/home/user/myproject");
33     ninja.buildEntries.shouldEqual([NinjaEntry("build mybin: cpp /home/user/myproject/foo.o /home/user/myproject/bar.o",
34                                                ["between = -o"])
35                                        ]);
36     ninja.ruleEntries.shouldEqual([NinjaEntry("rule cpp",
37                                               ["command = /usr/bin/c++ $in $between $out"])
38                                       ]);
39 }
40 
41 
42 void testCppLinkerProjectPathAndBuild() {
43     auto ninja = Ninja(Build(Target("mybin",
44                                      "/usr/bin/c++ $in -o $out",
45                                      [Target("foo.o"), Target("bar.o")],
46                                   )),
47                         "/home/user/myproject");
48     ninja.buildEntries.shouldEqual([NinjaEntry("build mybin: cpp /home/user/myproject/foo.o /home/user/myproject/bar.o",
49                                                ["between = -o"])
50                                        ]);
51     ninja.ruleEntries.shouldEqual([NinjaEntry("rule cpp",
52                                               ["command = /usr/bin/c++ $in $between $out"])
53                                       ]);
54 }
55 
56 
57 void testIccBuild() {
58     auto ninja = Ninja(Build(Target("/path/to/foo.o",
59                                      "icc.12.0.022b.i686-linux -pe-file-prefix=/usr/intel/12.0.022b/cc/12.0.022b/include/ @/usr/lib/icc-cc.cfg -I/path/to/headers -gcc-version=345 -fno-strict-aliasing -nostdinc -include /path/to/myheader.h -DTOOL_CHAIN_GCC=gcc-user -D__STUFF__ -imacros /path/to/preinclude_macros.h -I/path/to -Wall -c -MD -MF /path/to/foo.d -o $out $in",
60                                      [Target("/path/to/foo.c")])));
61     ninja.buildEntries.shouldEqual([NinjaEntry("build /path/to/foo.o: icc.12.0.022b.i686-linux /path/to/foo.c",
62                                                ["before = -pe-file-prefix=/usr/intel/12.0.022b/cc/12.0.022b/include/ @/usr/lib/icc-cc.cfg -I/path/to/headers -gcc-version=345 -fno-strict-aliasing -nostdinc -include /path/to/myheader.h -DTOOL_CHAIN_GCC=gcc-user -D__STUFF__ -imacros /path/to/preinclude_macros.h -I/path/to -Wall -c -MD -MF /path/to/foo.d -o"])]);
63     ninja.ruleEntries.shouldEqual([NinjaEntry("rule icc.12.0.022b.i686-linux",
64                                               ["command = icc.12.0.022b.i686-linux $before $out $in"])]);
65 }
66 
67 
68 void testBeforeAndAfter() {
69     auto ninja = Ninja(Build(Target("foo.temp",
70                                      "icc @/path/to/icc-ld.cfg -o $out $in -Wl,-rpath-link -Wl,/usr/lib",
71                                      [Target("main.o"), Target("extra.o"), Target("sub_foo.o"), Target("sub_bar.o"),
72                                       Target("sub_baz.a")])));
73     ninja.buildEntries.shouldEqual([NinjaEntry("build foo.temp: icc main.o extra.o sub_foo.o sub_bar.o sub_baz.a",
74                                                ["before = @/path/to/icc-ld.cfg -o",
75                                                 "after = -Wl,-rpath-link -Wl,/usr/lib"])]);
76     ninja.ruleEntries.shouldEqual([NinjaEntry("rule icc",
77                                               ["command = icc $before $out $in $after"])]);
78 }
79 
80 void testSimpleDBuild() {
81     auto mainObj  = Target(`main.o`,  `dmd -I$project/src -c $in -of$out`, Target(`src/main.d`));
82     auto mathsObj = Target(`maths.o`, `dmd -c $in -of$out`, Target(`src/maths.d`));
83     auto app = Target(`myapp`,
84                        `dmd -of$out $in`,
85                        [mainObj, mathsObj]
86         );
87     auto build = Build(app);
88     auto ninja = Ninja(build, "/path/to/project");
89 
90     ninja.buildEntries.shouldEqual(
91         [NinjaEntry("build objs/myapp.objs/main.o: dmd /path/to/project/src/main.d",
92                     ["before = -I/path/to/project/src -c",
93                      "between = -of"]),
94          NinjaEntry("build objs/myapp.objs/maths.o: dmd /path/to/project/src/maths.d",
95                     ["before = -c",
96                      "between = -of"]),
97          NinjaEntry("build myapp: dmd_2 objs/myapp.objs/main.o objs/myapp.objs/maths.o",
98                     ["before = -of"])
99             ]);
100 
101     ninja.ruleEntries.shouldEqual(
102         [NinjaEntry("rule dmd",
103                     ["command = dmd $before $in $between$out"]),
104          NinjaEntry("rule dmd_2",
105                     ["command = dmd $before$out $in"])
106             ]);
107 }
108 
109 
110 void testImplicitDependencies() {
111     auto target = Target("foo.o", "gcc -o $out -c $in", [Target("foo.c")], [Target("foo.h")]);
112     auto ninja = Ninja(Build(target));
113     ninja.buildEntries.shouldEqual(
114         [NinjaEntry("build foo.o: gcc foo.c | foo.h",
115                     ["before = -o",
116                      "between = -c"])
117             ]);
118 
119     ninja.ruleEntries.shouldEqual(
120         [NinjaEntry("rule gcc",
121                     ["command = gcc $before $out $between $in"])]);
122 }
123 
124 void testImplicitDependenciesMoreThanOne() {
125     auto target = Target("foo.o", "gcc -o $out -c $in", [Target("foo.c")], [Target("foo.h"), Target("foo.idl")]);
126     auto ninja = Ninja(Build(target));
127     ninja.buildEntries.shouldEqual(
128         [NinjaEntry("build foo.o: gcc foo.c | foo.h foo.idl",
129                     ["before = -o",
130                      "between = -c"])
131             ]);
132 
133     ninja.ruleEntries.shouldEqual(
134         [NinjaEntry("rule gcc",
135                     ["command = gcc $before $out $between $in"])]);
136 }
137 
138 
139 void testDefaultRules() {
140     import reggae.config: gDefaultOptions;
141     defaultRules(gDefaultOptions).shouldEqual(
142         [
143             NinjaEntry("rule _ccompile",
144                        ["command = gcc $flags $includes -MMD -MT $out -MF $out.dep -o $out -c $in",
145                         "deps = gcc",
146                         "depfile = $out.dep"]),
147             NinjaEntry("rule _cppcompile",
148                        ["command = g++ $flags $includes -MMD -MT $out -MF $out.dep -o $out -c $in",
149                         "deps = gcc",
150                         "depfile = $out.dep"]),
151             NinjaEntry("rule _dcompile",
152                        ["command = .reggae/dcompile --objFile=$out --depFile=$out.dep dmd $flags $includes $stringImports $in",
153                         "deps = gcc",
154                         "depfile = $out.dep"]),
155             NinjaEntry("rule _clink",
156                        ["command = gcc -o $out $flags $in"]),
157             NinjaEntry("rule _cpplink",
158                        ["command = g++ -o $out $flags $in"]),
159             NinjaEntry("rule _dlink",
160                        ["command = dmd -of$out $flags $in"]),
161             NinjaEntry("rule _ulink",
162                        ["command = dmd -of$out $flags $in"]),
163             NinjaEntry("rule _ccompileAndLink",
164                        ["command = gcc $flags $includes -MMD -MT $out -MF $out.dep -o $out $in",
165                         "deps = gcc",
166                         "depfile = $out.dep"]),
167             NinjaEntry("rule _cppcompileAndLink",
168                        ["command = g++ $flags $includes -MMD -MT $out -MF $out.dep -o $out $in",
169                         "deps = gcc",
170                         "depfile = $out.dep"]),
171             NinjaEntry("rule _dcompileAndLink",
172                        ["command = .reggae/dcompile --objFile=$out --depFile=$out.dep dmd $flags $includes $stringImports $in",
173                         "deps = gcc",
174                         "depfile = $out.dep"]),
175 
176             NinjaEntry("rule _phony",
177                        ["command = $cmd"]),
178             ]);
179 }
180 
181 void testDefaultRulesWeirdCCompiler() {
182     auto options = Options();
183     options.cCompiler = "weirdcc";
184     auto rules = defaultRules(options);
185     auto entry = NinjaEntry("rule _ccompile",
186                             ["command = weirdcc $flags $includes -MMD -MT $out -MF $out.dep -o $out -c $in",
187                              "deps = gcc",
188                              "depfile = $out.dep"]);
189     entry.shouldBeIn(rules);
190 }
191 
192 void testImplicitOutput() {
193     auto foo = Target(["foo.h", "foo.c"], "protocomp $in", [Target("foo.proto")]);
194     auto bar = Target(["bar.h", "bar.c"], "protocomp $in", [Target("bar.proto")]);
195     auto ninja = Ninja(Build(foo, bar));
196 
197     ninja.buildEntries.shouldEqual(
198         [NinjaEntry("build foo.h foo.c: protocomp foo.proto"),
199          NinjaEntry("build bar.h bar.c: protocomp bar.proto")]);
200 
201     ninja.ruleEntries.shouldEqual(
202         [NinjaEntry("rule protocomp",
203                     ["command = protocomp $in "])]);
204 }
205 
206 
207 void testImplicitInput() {
208     auto protoSrcs = Target([`$builddir/gen/protocol.c`, `$builddir/gen/protocol.h`],
209                              `./compiler $in`,
210                              [Target(`protocol.proto`)]);
211     auto protoObj = Target(`$builddir/bin/protocol.o`,
212                             `gcc -o $out -c $builddir/gen/protocol.c`,
213                             [], [protoSrcs]);
214     auto protoD = Target(`$builddir/gen/protocol.d`,
215                           `./translator $builddir/gen/protocol.h $out`,
216                           [], [protoSrcs]);
217     auto app = Target(`app`, `dmd -of$out $in`,
218                        [Target("src/main.d"), protoObj, protoD]);
219 
220     auto ninja = Ninja(Build(app));
221 
222     ninja.buildEntries.shouldEqual(
223         [NinjaEntry("build gen/protocol.c gen/protocol.h: compiler protocol.proto"),
224          NinjaEntry("build bin/protocol.o: gcc gen/protocol.c | gen/protocol.c gen/protocol.h",
225                     ["before = -o",
226                      "between = -c"]),
227          NinjaEntry("build gen/protocol.d: translator gen/protocol.h | gen/protocol.c gen/protocol.h"),
228          NinjaEntry("build app: dmd src/main.d bin/protocol.o gen/protocol.d",
229                     ["before = -of"])
230             ]);
231 
232     ninja.ruleEntries.shouldEqual(
233         [NinjaEntry("rule compiler",
234                     ["command = ./compiler $in "]),
235          NinjaEntry("rule gcc",
236                     ["command = gcc $before $out $between $in"]),
237          NinjaEntry("rule translator",
238                     ["command = ./translator $in $out"]),
239          NinjaEntry("rule dmd",
240                     ["command = dmd $before$out $in"])
241             ]);
242 }
243 
244 
245 void testOutputInProjectPathCustom() {
246     auto tgt = Target("$project/foo.o", "gcc -o $out -c $in", Target("foo.c"));
247     auto ninja = Ninja(Build(tgt), "/path/to/proj");
248     ninja.buildEntries.shouldEqual(
249         [NinjaEntry("build /path/to/proj/foo.o: gcc /path/to/proj/foo.c",
250                     ["before = -o",
251                      "between = -c"])]);
252 }
253 
254 
255 void testOutputAndDepOutputInProjectPath() {
256     auto fooLib = Target("$project/foo.so", "dmd -of$out $in", [Target("src1.d"), Target("src2.d")]);
257     auto symlink1 = Target("$project/weird/path/thingie1", "ln -sf $in $out", fooLib);
258     auto symlink2 = Target("$project/weird/path/thingie2", "ln -sf $in $out", fooLib);
259     auto build = Build(symlink1, symlink2); //defined by the mixin
260     auto ninja = Ninja(build, "/tmp/proj");
261 
262     ninja.buildEntries.shouldEqual(
263         [NinjaEntry("build /tmp/proj/foo.so: dmd /tmp/proj/src1.d /tmp/proj/src2.d",
264                     ["before = -of"]),
265          NinjaEntry("build /tmp/proj/weird/path/thingie1: ln /tmp/proj/foo.so",
266                     ["before = -sf"]),
267          NinjaEntry("build /tmp/proj/weird/path/thingie2: ln /tmp/proj/foo.so",
268                     ["before = -sf"]),
269             ]
270         );
271 }
272 
273 void testOutputInProjectPathDefault() {
274     import reggae.ctaa;
275     auto tgt = Target("$project/foo.o",
276                        Command(CommandType.compile, assocListT("foo", ["bar"])),
277                        Target("foo.c"));
278     auto ninja = Ninja(Build(tgt), "/path/to/proj");
279     ninja.buildEntries.shouldEqual(
280         [NinjaEntry("build /path/to/proj/foo.o: _ccompile /path/to/proj/foo.c",
281                     ["foo = bar"])]);
282 }
283 
284 
285 void testPhonyRule() {
286     auto tgt = Target("lephony",
287                        Command.phony("whatever boo bop"),
288                        [Target("toto"), Target("tata")],
289                        [Target("implicit")]);
290     auto ninja = Ninja(Build(tgt), "/path/to/proj");
291     ninja.buildEntries.shouldEqual(
292         [NinjaEntry("build lephony: _phony /path/to/proj/toto /path/to/proj/tata | /path/to/proj/implicit",
293                     ["cmd = whatever boo bop",
294                      "pool = console"])]
295         );
296 }
297 
298 void testImplicitsWithNoIn() {
299     Target[] emptyDependencies;
300     auto stuff = Target("foo.o", "dmd -of$out -c $in", Target("foo.d"));
301     auto foo = Target("$project/foodir", "mkdir -p $out", emptyDependencies, [stuff]);
302     auto ninja = Ninja(Build(foo), "/path/to/proj"); //to make sure we can
303     ninja.buildEntries.shouldEqual(
304         [NinjaEntry("build objs//path/to/proj/foodir.objs/foo.o: dmd /path/to/proj/foo.d",
305                     ["before = -of",
306                      "between = -c"]),
307          NinjaEntry("build /path/to/proj/foodir: mkdir  | objs//path/to/proj/foodir.objs/foo.o",
308                     ["before = -p"]),
309             ]);
310     ninja.ruleEntries.shouldEqual(
311         [NinjaEntry("rule dmd",
312                     ["command = dmd $before$out $between $in"]),
313          NinjaEntry("rule mkdir",
314                     ["command = mkdir $before $out$in"])
315             ]);
316 }
317 
318 
319 void testCustomRuleInvolvingProjectPath() {
320     auto foo = Target("foo.o", "$project/../dmd/src/dmd -of$out -c $in", Target("foo.d"));
321     auto app = Target("app", "$project/../dmd/src/dmd -of$out -c $in", Target("foo.d"));
322     auto ninja = Ninja(Build(app), "/path/to/proj");
323     ninja.ruleEntries.shouldEqual(
324         [NinjaEntry("rule dmd",
325                     ["command = /path/to/proj/../dmd/src/dmd $before$out $between $in"]),
326             ]);
327 }
328 
329 
330 void testTargetWithNoDependencies() {
331     auto obj = Target("utmain.o", "dmd -of$out -c $in",
332                        Target("utmain.d", "/home/atila/coding/d/dtest/bin/dtest -f $out --generate"));
333     //before the fix this throws because of no $in and $out in the target
334     auto ninja = Ninja(Build(obj), "/path/to/proj");
335 }