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 }