1 module tests.ninja;
2 
3 import unit_threaded;
4 import reggae.backend.ninja;
5 import reggae.build;
6 
7 
8 void testEmpty() {
9     const ninja = Ninja();
10     ninja.buildEntries.shouldEqual([]);
11     ninja.ruleEntries.shouldEqual([]);
12 }
13 
14 void testCppLinker() {
15     const 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     const 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     const 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     const 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     const 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     const mainObj  = Target(`main.o`,  `dmd -I$project/src -c $in -of$out`, Target(`src/main.d`));
82     const mathsObj = Target(`maths.o`, `dmd -c $in -of$out`, Target(`src/maths.d`));
83     const app = Target(`myapp`,
84                        `dmd -of$out $in`,
85                        [mainObj, mathsObj]
86         );
87     const build = Build(app);
88     const 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     const target = Target("foo.o", "gcc -o $out -c $in", [Target("foo.c")], [Target("foo.h")]);
112     const 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     const target = Target("foo.o", "gcc -o $out -c $in", [Target("foo.c")], [Target("foo.h"), Target("foo.idl")]);
126     const 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     defaultRules().shouldEqual(
141         [
142             NinjaEntry("rule _ccompile",
143                        ["command = gcc $flags $includes -MMD -MT $out -MF $DEPFILE -o $out -c $in",
144                         "deps = gcc",
145                         "depfile = $DEPFILE"]),
146             NinjaEntry("rule _cppcompile",
147                        ["command = g++ $flags $includes -MMD -MT $out -MF $DEPFILE -o $out -c $in",
148                         "deps = gcc",
149                         "depfile = $DEPFILE"]),
150             NinjaEntry("rule _dcompile",
151                     ["command = .reggae/dcompile --objFile=$out --depFile=$DEPFILE dmd $flags $includes $stringImports $in",
152                      "deps = gcc",
153                      "depfile = $DEPFILE"]),
154             NinjaEntry("rule _clink",
155                        ["command = gcc -o $out $flags $in"]),
156             NinjaEntry("rule _cpplink",
157                        ["command = g++ -o $out $flags $in"]),
158             NinjaEntry("rule _dlink",
159                        ["command = dmd -of$out $flags $in"]),
160             NinjaEntry("rule _ulink",
161                        ["command = dmd -of$out $flags $in"]),
162             ]);
163 }
164 
165 
166 void testImplicitOutput() {
167     const foo = Target(["foo.h", "foo.c"], "protocomp $in", [Target("foo.proto")]);
168     const bar = Target(["bar.h", "bar.c"], "protocomp $in", [Target("bar.proto")]);
169     const ninja = Ninja(Build(foo, bar));
170 
171     ninja.buildEntries.shouldEqual(
172         [NinjaEntry("build foo.h foo.c: protocomp foo.proto"),
173          NinjaEntry("build bar.h bar.c: protocomp bar.proto")]);
174 
175     ninja.ruleEntries.shouldEqual(
176         [NinjaEntry("rule protocomp",
177                     ["command = protocomp $in "])]);
178 }
179 
180 
181 void testImplicitInput() {
182     const protoSrcs = Target([`$builddir/gen/protocol.c`, `$builddir/gen/protocol.h`],
183                              `./compiler $in`,
184                              [Target(`protocol.proto`)]);
185     const protoObj = Target(`$builddir/bin/protocol.o`,
186                             `gcc -o $out -c $builddir/gen/protocol.c`,
187                             [], [protoSrcs]);
188     const protoD = Target(`$builddir/gen/protocol.d`,
189                           `./translator $builddir/gen/protocol.h $out`,
190                           [], [protoSrcs]);
191     const app = Target(`app`, `dmd -of$out $in`,
192                        [Target("src/main.d"), protoObj, protoD]);
193 
194     const ninja = Ninja(Build(app));
195 
196     ninja.buildEntries.shouldEqual(
197         [NinjaEntry("build gen/protocol.c gen/protocol.h: compiler protocol.proto"),
198          NinjaEntry("build bin/protocol.o: gcc gen/protocol.c | gen/protocol.c gen/protocol.h",
199                     ["before = -o",
200                      "between = -c"]),
201          NinjaEntry("build gen/protocol.c gen/protocol.h: compiler protocol.proto"),
202          NinjaEntry("build gen/protocol.d: translator gen/protocol.h | gen/protocol.c gen/protocol.h"),
203          NinjaEntry("build app: dmd src/main.d bin/protocol.o gen/protocol.d",
204                     ["before = -of"])
205             ]);
206 
207     ninja.ruleEntries.shouldEqual(
208         [NinjaEntry("rule compiler",
209                     ["command = ./compiler $in "]),
210          NinjaEntry("rule gcc",
211                     ["command = gcc $before $out $between $in"]),
212          NinjaEntry("rule translator",
213                     ["command = ./translator $in $out"]),
214          NinjaEntry("rule dmd",
215                     ["command = dmd $before$out $in"])
216             ]);
217 }
218 
219 
220 void testOutputInProjectPathCustom() {
221     const tgt = Target("$project/foo.o", "gcc -o $out -c $in", Target("foo.c"));
222     const ninja = Ninja(Build(tgt), "/path/to/proj");
223     ninja.buildEntries.shouldEqual(
224         [NinjaEntry("build /path/to/proj/foo.o: gcc /path/to/proj/foo.c",
225                     ["before = -o",
226                      "between = -c"])]);
227 }
228 
229 void testOutputInProjectPathDefault() {
230     import reggae.ctaa;
231     const tgt = Target("$project/foo.o",
232                        Command(CommandType.compile, assocListT("foo", ["bar"])),
233                        Target("foo.c"));
234     const ninja = Ninja(Build(tgt), "/path/to/proj");
235     ninja.buildEntries.shouldEqual(
236         [NinjaEntry("build /path/to/proj/foo.o: _ccompile /path/to/proj/foo.c",
237                     ["foo = bar"])]);
238 }