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 .reggae/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 .reggae/objs/myapp.objs/maths.o: dmd /path/to/project/src/maths.d", 95 ["before = -c", 96 "between = -of"]), 97 NinjaEntry("build myapp: dmd_2 .reggae/objs/myapp.objs/main.o .reggae/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 .reggae/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 | .reggae/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 }