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