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 }