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