github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/graph_test.go (about) 1 // Copyright 2011 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nin 16 17 import ( 18 "runtime" 19 "testing" 20 ) 21 22 type GraphTest struct { 23 StateTestWithBuiltinRules 24 fs VirtualFileSystem 25 scan DependencyScan 26 } 27 28 func NewGraphTest(t *testing.T) GraphTest { 29 g := GraphTest{ 30 StateTestWithBuiltinRules: NewStateTestWithBuiltinRules(t), 31 fs: NewVirtualFileSystem(), 32 } 33 g.scan = NewDependencyScan(&g.state, nil, nil, &g.fs) 34 return g 35 } 36 37 func TestGraphTest_MissingImplicit(t *testing.T) { 38 g := NewGraphTest(t) 39 g.AssertParse(&g.state, "build out: cat in | implicit\n", ParseManifestOpts{}) 40 g.fs.Create("in", "") 41 g.fs.Create("out", "") 42 43 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 44 t.Fatal(err) 45 } 46 47 // A missing implicit dep *should* make the output dirty. 48 // (In fact, a build will fail.) 49 // This is a change from prior semantics of ninja. 50 if !g.GetNode("out").Dirty { 51 t.Fatal("expected true") 52 } 53 } 54 55 func TestGraphTest_ModifiedImplicit(t *testing.T) { 56 g := NewGraphTest(t) 57 g.AssertParse(&g.state, "build out: cat in | implicit\n", ParseManifestOpts{}) 58 g.fs.Create("in", "") 59 g.fs.Create("out", "") 60 g.fs.Tick() 61 g.fs.Create("implicit", "") 62 63 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 64 t.Fatal(err) 65 } 66 67 // A modified implicit dep should make the output dirty. 68 if !g.GetNode("out").Dirty { 69 t.Fatal("expected true") 70 } 71 } 72 73 func TestGraphTest_FunkyMakefilePath(t *testing.T) { 74 g := NewGraphTest(t) 75 g.AssertParse(&g.state, "rule catdep\n depfile = $out.d\n command = cat $in > $out\nbuild out.o: catdep foo.cc\n", ParseManifestOpts{}) 76 g.fs.Create("foo.cc", "") 77 g.fs.Create("out.o.d", "out.o: ./foo/../implicit.h\n") 78 g.fs.Create("out.o", "") 79 g.fs.Tick() 80 g.fs.Create("implicit.h", "") 81 82 if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil { 83 t.Fatal(err) 84 } 85 86 // implicit.h has changed, though our depfile refers to it with a 87 // non-canonical path; we should still find it. 88 if !g.GetNode("out.o").Dirty { 89 t.Fatal("expected true") 90 } 91 } 92 93 func TestGraphTest_ExplicitImplicit(t *testing.T) { 94 g := NewGraphTest(t) 95 g.AssertParse(&g.state, "rule catdep\n depfile = $out.d\n command = cat $in > $out\nbuild implicit.h: cat data\nbuild out.o: catdep foo.cc || implicit.h\n", ParseManifestOpts{}) 96 g.fs.Create("implicit.h", "") 97 g.fs.Create("foo.cc", "") 98 g.fs.Create("out.o.d", "out.o: implicit.h\n") 99 g.fs.Create("out.o", "") 100 g.fs.Tick() 101 g.fs.Create("data", "") 102 103 if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil { 104 t.Fatal(err) 105 } 106 107 // We have both an implicit and an explicit dep on implicit.h. 108 // The implicit dep should "win" (in the sense that it should cause 109 // the output to be dirty). 110 if !g.GetNode("out.o").Dirty { 111 t.Fatal("expected true") 112 } 113 } 114 115 func TestGraphTest_ImplicitOutputParse(t *testing.T) { 116 g := NewGraphTest(t) 117 g.AssertParse(&g.state, "build out | out.imp: cat in\n", ParseManifestOpts{}) 118 119 edge := g.GetNode("out").InEdge 120 if 2 != len(edge.Outputs) { 121 t.Fatal("expected equal") 122 } 123 if "out" != edge.Outputs[0].Path { 124 t.Fatal("expected equal") 125 } 126 if "out.imp" != edge.Outputs[1].Path { 127 t.Fatal("expected equal") 128 } 129 if 1 != edge.ImplicitOuts { 130 t.Fatal("expected equal") 131 } 132 if edge != g.GetNode("out.imp").InEdge { 133 t.Fatal("expected equal") 134 } 135 } 136 137 func TestGraphTest_ImplicitOutputMissing(t *testing.T) { 138 g := NewGraphTest(t) 139 g.AssertParse(&g.state, "build out | out.imp: cat in\n", ParseManifestOpts{}) 140 g.fs.Create("in", "") 141 g.fs.Create("out", "") 142 143 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 144 t.Fatal(err) 145 } 146 147 if !g.GetNode("out").Dirty { 148 t.Fatal("expected true") 149 } 150 if !g.GetNode("out.imp").Dirty { 151 t.Fatal("expected true") 152 } 153 } 154 155 func TestGraphTest_ImplicitOutputOutOfDate(t *testing.T) { 156 g := NewGraphTest(t) 157 g.AssertParse(&g.state, "build out | out.imp: cat in\n", ParseManifestOpts{}) 158 g.fs.Create("out.imp", "") 159 g.fs.Tick() 160 g.fs.Create("in", "") 161 g.fs.Create("out", "") 162 163 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 164 t.Fatal(err) 165 } 166 167 if !g.GetNode("out").Dirty { 168 t.Fatal("expected true") 169 } 170 if !g.GetNode("out.imp").Dirty { 171 t.Fatal("expected true") 172 } 173 } 174 175 func TestGraphTest_ImplicitOutputOnlyParse(t *testing.T) { 176 g := NewGraphTest(t) 177 g.AssertParse(&g.state, "build | out.imp: cat in\n", ParseManifestOpts{}) 178 179 edge := g.GetNode("out.imp").InEdge 180 if 1 != len(edge.Outputs) { 181 t.Fatal("expected equal") 182 } 183 if "out.imp" != edge.Outputs[0].Path { 184 t.Fatal("expected equal") 185 } 186 if 1 != edge.ImplicitOuts { 187 t.Fatal("expected equal") 188 } 189 if edge != g.GetNode("out.imp").InEdge { 190 t.Fatal("expected equal") 191 } 192 } 193 194 func TestGraphTest_ImplicitOutputOnlyMissing(t *testing.T) { 195 g := NewGraphTest(t) 196 g.AssertParse(&g.state, "build | out.imp: cat in\n", ParseManifestOpts{}) 197 g.fs.Create("in", "") 198 199 if _, err := g.scan.RecomputeDirty(g.GetNode("out.imp")); err != nil { 200 t.Fatal(err) 201 } 202 203 if !g.GetNode("out.imp").Dirty { 204 t.Fatal("expected true") 205 } 206 } 207 208 func TestGraphTest_ImplicitOutputOnlyOutOfDate(t *testing.T) { 209 g := NewGraphTest(t) 210 g.AssertParse(&g.state, "build | out.imp: cat in\n", ParseManifestOpts{}) 211 g.fs.Create("out.imp", "") 212 g.fs.Tick() 213 g.fs.Create("in", "") 214 215 if _, err := g.scan.RecomputeDirty(g.GetNode("out.imp")); err != nil { 216 t.Fatal(err) 217 } 218 219 if !g.GetNode("out.imp").Dirty { 220 t.Fatal("expected true") 221 } 222 } 223 224 func TestGraphTest_PathWithCurrentDirectory(t *testing.T) { 225 g := NewGraphTest(t) 226 g.AssertParse(&g.state, "rule catdep\n depfile = $out.d\n command = cat $in > $out\nbuild ./out.o: catdep ./foo.cc\n", ParseManifestOpts{}) 227 g.fs.Create("foo.cc", "") 228 g.fs.Create("out.o.d", "out.o: foo.cc\n") 229 g.fs.Create("out.o", "") 230 231 if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil { 232 t.Fatal(err) 233 } 234 235 if g.GetNode("out.o").Dirty { 236 t.Fatal("expected false") 237 } 238 } 239 240 func TestGraphTest_RootNodes(t *testing.T) { 241 g := NewGraphTest(t) 242 g.AssertParse(&g.state, "build out1: cat in1\nbuild mid1: cat in1\nbuild out2: cat mid1\nbuild out3 out4: cat mid1\n", ParseManifestOpts{}) 243 244 rootNodes := g.state.RootNodes() 245 if 4 != len(rootNodes) { 246 t.Fatal("expected equal") 247 } 248 for i := 0; i < len(rootNodes); i++ { 249 name := rootNodes[i].Path 250 if "out" != name[0:3] { 251 t.Fatal("expected equal") 252 } 253 } 254 } 255 256 func TestGraphTest_VarInOutPathEscaping(t *testing.T) { 257 g := NewGraphTest(t) 258 g.AssertParse(&g.state, "build a$ b: cat no'space with$ space$$ no\"space2\n", ParseManifestOpts{}) 259 260 edge := g.GetNode("a b").InEdge 261 want := "cat 'no'\\''space' 'with space$' 'no\"space2' > 'a b'" 262 if runtime.GOOS == "windows" { 263 want = "cat no'space \"with space$\" \"no\\\"space2\" > \"a b\"" 264 } 265 if got := edge.EvaluateCommand(false); want != got { 266 t.Fatalf("want %q, got %q", want, got) 267 } 268 } 269 270 // Regression test for https://github.com/ninja-build/ninja/issues/380 271 func TestGraphTest_DepfileWithCanonicalizablePath(t *testing.T) { 272 g := NewGraphTest(t) 273 g.AssertParse(&g.state, "rule catdep\n depfile = $out.d\n command = cat $in > $out\nbuild ./out.o: catdep ./foo.cc\n", ParseManifestOpts{}) 274 g.fs.Create("foo.cc", "") 275 g.fs.Create("out.o.d", "out.o: bar/../foo.cc\n") 276 g.fs.Create("out.o", "") 277 278 if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil { 279 t.Fatal(err) 280 } 281 282 if g.GetNode("out.o").Dirty { 283 t.Fatal("expected false") 284 } 285 } 286 287 // Regression test for https://github.com/ninja-build/ninja/issues/404 288 func TestGraphTest_DepfileRemoved(t *testing.T) { 289 g := NewGraphTest(t) 290 g.AssertParse(&g.state, "rule catdep\n depfile = $out.d\n command = cat $in > $out\nbuild ./out.o: catdep ./foo.cc\n", ParseManifestOpts{}) 291 g.fs.Create("foo.h", "") 292 g.fs.Create("foo.cc", "") 293 g.fs.Tick() 294 g.fs.Create("out.o.d", "out.o: foo.h\n") 295 g.fs.Create("out.o", "") 296 297 if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil { 298 t.Fatal(err) 299 } 300 if g.GetNode("out.o").Dirty { 301 t.Fatal("expected false") 302 } 303 304 g.state.Reset() 305 g.fs.RemoveFile("out.o.d") 306 if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil { 307 t.Fatal(err) 308 } 309 if !g.GetNode("out.o").Dirty { 310 t.Fatal("expected true") 311 } 312 } 313 314 // Check that rule-level variables are in scope for eval. 315 func TestGraphTest_RuleVariablesInScope(t *testing.T) { 316 g := NewGraphTest(t) 317 g.AssertParse(&g.state, "rule r\n depfile = x\n command = depfile is $depfile\nbuild out: r in\n", ParseManifestOpts{}) 318 edge := g.GetNode("out").InEdge 319 if got := edge.EvaluateCommand(false); got != "depfile is x" { 320 t.Fatal(got) 321 } 322 } 323 324 // Check that build statements can override rule builtins like depfile. 325 func TestGraphTest_DepfileOverride(t *testing.T) { 326 g := NewGraphTest(t) 327 g.AssertParse(&g.state, "rule r\n depfile = x\n command = unused\nbuild out: r in\n depfile = y\n", ParseManifestOpts{}) 328 edge := g.GetNode("out").InEdge 329 if "y" != edge.GetBinding("depfile") { 330 t.Fatal("expected equal") 331 } 332 } 333 334 // Check that overridden values show up in expansion of rule-level bindings. 335 func TestGraphTest_DepfileOverrideParent(t *testing.T) { 336 g := NewGraphTest(t) 337 g.AssertParse(&g.state, "rule r\n depfile = x\n command = depfile is $depfile\nbuild out: r in\n depfile = y\n", ParseManifestOpts{}) 338 edge := g.GetNode("out").InEdge 339 if "depfile is y" != edge.GetBinding("command") { 340 t.Fatal("expected equal") 341 } 342 } 343 344 // Verify that building a nested phony rule prints "no work to do" 345 func TestGraphTest_NestedPhonyPrintsDone(t *testing.T) { 346 g := NewGraphTest(t) 347 g.AssertParse(&g.state, "build n1: phony \nbuild n2: phony n1\n", ParseManifestOpts{}) 348 if _, err := g.scan.RecomputeDirty(g.GetNode("n2")); err != nil { 349 t.Fatal(err) 350 } 351 352 plan := newPlan(nil) 353 if _, err := plan.addTarget(g.GetNode("n2")); err != nil { 354 t.Fatal(err) 355 } 356 357 if 0 != plan.commandEdges { 358 t.Fatal("expected equal") 359 } 360 if plan.moreToDo() { 361 t.Fatal("expected false") 362 } 363 } 364 365 func TestGraphTest_PhonySelfReferenceError(t *testing.T) { 366 g := NewGraphTest(t) 367 var parserOpts ParseManifestOpts 368 parserOpts.ErrOnPhonyCycle = true 369 g.AssertParse(&g.state, "build a: phony a\n", parserOpts) 370 371 if _, err := g.scan.RecomputeDirty(g.GetNode("a")); err == nil { 372 t.Fatal("expected false") 373 } else if err.Error() != "dependency cycle: a -> a [-w phonycycle=err]" { 374 t.Fatal(err) 375 } 376 } 377 378 func TestGraphTest_DependencyCycle(t *testing.T) { 379 g := NewGraphTest(t) 380 g.AssertParse(&g.state, "build out: cat mid\nbuild mid: cat in\nbuild in: cat pre\nbuild pre: cat out\n", ParseManifestOpts{}) 381 382 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil { 383 t.Fatal("expected false") 384 } else if err.Error() != "dependency cycle: out -> mid -> in -> pre -> out" { 385 t.Fatal(err) 386 } 387 } 388 389 func TestGraphTest_CycleInEdgesButNotInNodes1(t *testing.T) { 390 g := NewGraphTest(t) 391 g.AssertParse(&g.state, "build a b: cat a\n", ParseManifestOpts{}) 392 if _, err := g.scan.RecomputeDirty(g.GetNode("b")); err == nil { 393 t.Fatal("expected false") 394 } else if err.Error() != "dependency cycle: a -> a" { 395 t.Fatal(err) 396 } 397 } 398 399 func TestGraphTest_CycleInEdgesButNotInNodes2(t *testing.T) { 400 g := NewGraphTest(t) 401 g.AssertParse(&g.state, "build b a: cat a\n", ParseManifestOpts{}) 402 if _, err := g.scan.RecomputeDirty(g.GetNode("b")); err == nil { 403 t.Fatal("expected false") 404 } else if err.Error() != "dependency cycle: a -> a" { 405 t.Fatal(err) 406 } 407 } 408 409 func TestGraphTest_CycleInEdgesButNotInNodes3(t *testing.T) { 410 g := NewGraphTest(t) 411 g.AssertParse(&g.state, "build a b: cat c\nbuild c: cat a\n", ParseManifestOpts{}) 412 if _, err := g.scan.RecomputeDirty(g.GetNode("b")); err == nil { 413 t.Fatal("expected false") 414 } else if err.Error() != "dependency cycle: a -> c -> a" { 415 t.Fatal(err) 416 } 417 } 418 419 func TestGraphTest_CycleInEdgesButNotInNodes4(t *testing.T) { 420 g := NewGraphTest(t) 421 g.AssertParse(&g.state, "build d: cat c\nbuild c: cat b\nbuild b: cat a\nbuild a e: cat d\nbuild f: cat e\n", ParseManifestOpts{}) 422 if _, err := g.scan.RecomputeDirty(g.GetNode("f")); err == nil { 423 t.Fatal("expected false") 424 } else if err.Error() != "dependency cycle: a -> d -> c -> b -> a" { 425 t.Fatal(err) 426 } 427 } 428 429 // Verify that cycles in graphs with multiple outputs are handled correctly 430 // in RecomputeDirty() and don't cause deps to be loaded multiple times. 431 func TestGraphTest_CycleWithLengthZeroFromDepfile(t *testing.T) { 432 g := NewGraphTest(t) 433 g.AssertParse(&g.state, "rule deprule\n depfile = dep.d\n command = unused\nbuild a b: deprule\n", ParseManifestOpts{}) 434 g.fs.Create("dep.d", "a: b\n") 435 436 if _, err := g.scan.RecomputeDirty(g.GetNode("a")); err == nil { 437 t.Fatal("expected false") 438 } else if err.Error() != "dependency cycle: b -> b" { 439 t.Fatal(err) 440 } 441 442 // Despite the depfile causing edge to be a cycle (it has outputs a and b, 443 // but the depfile also adds b as an input), the deps should have been loaded 444 // only once: 445 edge := g.GetNode("a").InEdge 446 if 1 != len(edge.Inputs) { 447 t.Fatal("expected equal") 448 } 449 if "b" != edge.Inputs[0].Path { 450 t.Fatal("expected equal") 451 } 452 } 453 454 // Like CycleWithLengthZeroFromDepfile but with a higher cycle length. 455 func TestGraphTest_CycleWithLengthOneFromDepfile(t *testing.T) { 456 g := NewGraphTest(t) 457 g.AssertParse(&g.state, "rule deprule\n depfile = dep.d\n command = unused\nrule r\n command = unused\nbuild a b: deprule\nbuild c: r b\n", ParseManifestOpts{}) 458 g.fs.Create("dep.d", "a: c\n") 459 460 if _, err := g.scan.RecomputeDirty(g.GetNode("a")); err == nil { 461 t.Fatal("expected false") 462 } else if err.Error() != "dependency cycle: b -> c -> b" { 463 t.Fatal(err) 464 } 465 466 // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b, 467 // but c's in_edge has b as input but the depfile also adds |edge| as 468 // output)), the deps should have been loaded only once: 469 edge := g.GetNode("a").InEdge 470 if 1 != len(edge.Inputs) { 471 t.Fatal("expected equal") 472 } 473 if "c" != edge.Inputs[0].Path { 474 t.Fatal("expected equal") 475 } 476 } 477 478 // Like CycleWithLengthOneFromDepfile but building a node one hop away from 479 // the cycle. 480 func TestGraphTest_CycleWithLengthOneFromDepfileOneHopAway(t *testing.T) { 481 g := NewGraphTest(t) 482 g.AssertParse(&g.state, "rule deprule\n depfile = dep.d\n command = unused\nrule r\n command = unused\nbuild a b: deprule\nbuild c: r b\nbuild d: r a\n", ParseManifestOpts{}) 483 g.fs.Create("dep.d", "a: c\n") 484 485 if _, err := g.scan.RecomputeDirty(g.GetNode("d")); err == nil { 486 t.Fatal("expected false") 487 } else if err.Error() != "dependency cycle: b -> c -> b" { 488 t.Fatal(err) 489 } 490 491 // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b, 492 // but c's in_edge has b as input but the depfile also adds |edge| as 493 // output)), the deps should have been loaded only once: 494 edge := g.GetNode("a").InEdge 495 if 1 != len(edge.Inputs) { 496 t.Fatal("expected equal") 497 } 498 if "c" != edge.Inputs[0].Path { 499 t.Fatal("expected equal") 500 } 501 } 502 503 func TestGraphTest_Decanonicalize(t *testing.T) { 504 if runtime.GOOS != "windows" { 505 t.Skip("windows only") 506 } 507 g := NewGraphTest(t) 508 g.AssertParse(&g.state, "build out\\out1: cat src\\in1\nbuild out\\out2/out3\\out4: cat mid1\nbuild out3 out4\\foo: cat mid1\n", ParseManifestOpts{}) 509 510 rootNodes := g.state.RootNodes() 511 if 4 != len(rootNodes) { 512 t.Fatal("expected equal") 513 } 514 if rootNodes[0].Path != "out/out1" { 515 t.Fatal("expected equal") 516 } 517 if rootNodes[1].Path != "out/out2/out3/out4" { 518 t.Fatal("expected equal") 519 } 520 if rootNodes[2].Path != "out3" { 521 t.Fatal("expected equal") 522 } 523 if rootNodes[3].Path != "out4/foo" { 524 t.Fatal("expected equal") 525 } 526 if rootNodes[0].PathDecanonicalized() != "out\\out1" { 527 t.Fatal("expected equal") 528 } 529 if rootNodes[1].PathDecanonicalized() != "out\\out2/out3\\out4" { 530 t.Fatal("expected equal") 531 } 532 if rootNodes[2].PathDecanonicalized() != "out3" { 533 t.Fatal("expected equal") 534 } 535 if rootNodes[3].PathDecanonicalized() != "out4\\foo" { 536 t.Fatal("expected equal") 537 } 538 } 539 540 func TestGraphTest_DyndepLoadTrivial(t *testing.T) { 541 g := NewGraphTest(t) 542 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r in || dd\n dyndep = dd\n", ParseManifestOpts{}) 543 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep\n") 544 545 if !g.GetNode("dd").DyndepPending { 546 t.Fatal("expected true") 547 } 548 if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err != nil { 549 t.Fatal(err) 550 } 551 if g.GetNode("dd").DyndepPending { 552 t.Fatal("expected false") 553 } 554 555 edge := g.GetNode("out").InEdge 556 if 1 != len(edge.Outputs) { 557 t.Fatal("expected equal") 558 } 559 if "out" != edge.Outputs[0].Path { 560 t.Fatal("expected equal") 561 } 562 if 2 != len(edge.Inputs) { 563 t.Fatal(len(edge.Inputs)) 564 } 565 if "in" != edge.Inputs[0].Path { 566 t.Fatal("expected equal") 567 } 568 if "dd" != edge.Inputs[1].Path { 569 t.Fatal("expected equal") 570 } 571 if 0 != edge.ImplicitDeps { 572 t.Fatal("expected equal") 573 } 574 if 1 != edge.OrderOnlyDeps { 575 t.Fatal("expected equal") 576 } 577 if edge.GetBinding("restat") != "" { 578 t.Fatal("expected false") 579 } 580 } 581 582 func TestGraphTest_DyndepLoadImplicit(t *testing.T) { 583 g := NewGraphTest(t) 584 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out1: r in || dd\n dyndep = dd\nbuild out2: r in\n", ParseManifestOpts{}) 585 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out1: dyndep | out2\n") 586 587 if !g.GetNode("dd").DyndepPending { 588 t.Fatal("expected true") 589 } 590 if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err != nil { 591 t.Fatal(err) 592 } 593 if g.GetNode("dd").DyndepPending { 594 t.Fatal("expected false") 595 } 596 597 edge := g.GetNode("out1").InEdge 598 if 1 != len(edge.Outputs) { 599 t.Fatal("expected equal") 600 } 601 if "out1" != edge.Outputs[0].Path { 602 t.Fatal("expected equal") 603 } 604 if 3 != len(edge.Inputs) { 605 t.Fatal("expected equal") 606 } 607 if "in" != edge.Inputs[0].Path { 608 t.Fatal("expected equal") 609 } 610 if "out2" != edge.Inputs[1].Path { 611 t.Fatal(edge.Inputs[1].Path) 612 } 613 if "dd" != edge.Inputs[2].Path { 614 t.Fatal("expected equal") 615 } 616 if 1 != edge.ImplicitDeps { 617 t.Fatal("expected equal") 618 } 619 if 1 != edge.OrderOnlyDeps { 620 t.Fatal("expected equal") 621 } 622 if edge.GetBinding("restat") != "" { 623 t.Fatal("expected false") 624 } 625 } 626 627 func TestGraphTest_DyndepLoadMissingFile(t *testing.T) { 628 g := NewGraphTest(t) 629 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r in || dd\n dyndep = dd\n", ParseManifestOpts{}) 630 631 if !g.GetNode("dd").DyndepPending { 632 t.Fatal("expected true") 633 } 634 if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil { 635 t.Fatal("expected false") 636 } else if err.Error() != "loading 'dd': file does not exist" { 637 t.Fatal(err) 638 } 639 } 640 641 func TestGraphTest_DyndepLoadMissingEntry(t *testing.T) { 642 g := NewGraphTest(t) 643 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r in || dd\n dyndep = dd\n", ParseManifestOpts{}) 644 g.fs.Create("dd", "ninja_dyndep_version = 1\n") 645 646 if !g.GetNode("dd").DyndepPending { 647 t.Fatal("expected true") 648 } 649 if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil { 650 t.Fatal("expected false") 651 } else if err.Error() != "'out' not mentioned in its dyndep file 'dd'" { 652 t.Fatal(err) 653 } 654 } 655 656 func TestGraphTest_DyndepLoadExtraEntry(t *testing.T) { 657 g := NewGraphTest(t) 658 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r in || dd\n dyndep = dd\nbuild out2: r in || dd\n", ParseManifestOpts{}) 659 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep\nbuild out2: dyndep\n") 660 661 if !g.GetNode("dd").DyndepPending { 662 t.Fatal("expected true") 663 } 664 if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil { 665 t.Fatal("expected false") 666 } else if err.Error() != "dyndep file 'dd' mentions output 'out2' whose build statement does not have a dyndep binding for the file" { 667 t.Fatal(err) 668 } 669 } 670 671 func TestGraphTest_DyndepLoadOutputWithMultipleRules1(t *testing.T) { 672 g := NewGraphTest(t) 673 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out1 | out-twice.imp: r in1\nbuild out2: r in2 || dd\n dyndep = dd\n", ParseManifestOpts{}) 674 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out2 | out-twice.imp: dyndep\n") 675 676 if !g.GetNode("dd").DyndepPending { 677 t.Fatal("expected true") 678 } 679 if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil { 680 t.Fatal("expected false") 681 } else if err.Error() != "multiple rules generate out-twice.imp" { 682 t.Fatal(err) 683 } 684 } 685 686 func TestGraphTest_DyndepLoadOutputWithMultipleRules2(t *testing.T) { 687 g := NewGraphTest(t) 688 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out1: r in1 || dd1\n dyndep = dd1\nbuild out2: r in2 || dd2\n dyndep = dd2\n", ParseManifestOpts{}) 689 g.fs.Create("dd1", "ninja_dyndep_version = 1\nbuild out1 | out-twice.imp: dyndep\n") 690 g.fs.Create("dd2", "ninja_dyndep_version = 1\nbuild out2 | out-twice.imp: dyndep\n") 691 692 if !g.GetNode("dd1").DyndepPending { 693 t.Fatal("expected true") 694 } 695 if err := g.scan.LoadDyndeps(g.GetNode("dd1"), DyndepFile{}); err != nil { 696 t.Fatal(err) 697 } 698 if !g.GetNode("dd2").DyndepPending { 699 t.Fatal("expected true") 700 } 701 if err := g.scan.LoadDyndeps(g.GetNode("dd2"), DyndepFile{}); err == nil { 702 t.Fatal("expected false") 703 } else if err.Error() != "multiple rules generate out-twice.imp" { 704 t.Fatal(err) 705 } 706 } 707 708 func TestGraphTest_DyndepLoadMultiple(t *testing.T) { 709 g := NewGraphTest(t) 710 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out1: r in1 || dd\n dyndep = dd\nbuild out2: r in2 || dd\n dyndep = dd\nbuild outNot: r in3 || dd\n", ParseManifestOpts{}) 711 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out1 | out1imp: dyndep | in1imp\nbuild out2: dyndep | in2imp\n restat = 1\n") 712 713 if !g.GetNode("dd").DyndepPending { 714 t.Fatal("expected true") 715 } 716 if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err != nil { 717 t.Fatal(err) 718 } 719 if g.GetNode("dd").DyndepPending { 720 t.Fatal("expected false") 721 } 722 edge1 := g.GetNode("out1").InEdge 723 if 2 != len(edge1.Outputs) { 724 t.Fatal("expected equal") 725 } 726 if "out1" != edge1.Outputs[0].Path { 727 t.Fatal("expected equal") 728 } 729 if "out1imp" != edge1.Outputs[1].Path { 730 t.Fatal("expected equal") 731 } 732 if 1 != edge1.ImplicitOuts { 733 t.Fatal("expected equal") 734 } 735 if 3 != len(edge1.Inputs) { 736 t.Fatal("expected equal") 737 } 738 if "in1" != edge1.Inputs[0].Path { 739 t.Fatal("expected equal") 740 } 741 if "in1imp" != edge1.Inputs[1].Path { 742 t.Fatal(edge1.Inputs[1].Path) 743 } 744 if "dd" != edge1.Inputs[2].Path { 745 t.Fatal("expected equal") 746 } 747 if 1 != edge1.ImplicitDeps { 748 t.Fatal("expected equal") 749 } 750 if 1 != edge1.OrderOnlyDeps { 751 t.Fatal("expected equal") 752 } 753 if edge1.GetBinding("restat") != "" { 754 t.Fatal("expected false") 755 } 756 if edge1 != g.GetNode("out1imp").InEdge { 757 t.Fatal("expected equal") 758 } 759 in1imp := g.GetNode("in1imp") 760 if 1 != len(in1imp.OutEdges) { 761 t.Fatal("expected equal") 762 } 763 if edge1 != in1imp.OutEdges[0] { 764 t.Fatal("expected equal") 765 } 766 767 edge2 := g.GetNode("out2").InEdge 768 if 1 != len(edge2.Outputs) { 769 t.Fatal("expected equal") 770 } 771 if "out2" != edge2.Outputs[0].Path { 772 t.Fatal("expected equal") 773 } 774 if 0 != edge2.ImplicitOuts { 775 t.Fatal("expected equal") 776 } 777 if 3 != len(edge2.Inputs) { 778 t.Fatal("expected equal") 779 } 780 if "in2" != edge2.Inputs[0].Path { 781 t.Fatal("expected equal") 782 } 783 if "in2imp" != edge2.Inputs[1].Path { 784 t.Fatal("expected equal") 785 } 786 if "dd" != edge2.Inputs[2].Path { 787 t.Fatal("expected equal") 788 } 789 if 1 != edge2.ImplicitDeps { 790 t.Fatal("expected equal") 791 } 792 if 1 != edge2.OrderOnlyDeps { 793 t.Fatal("expected equal") 794 } 795 if edge2.GetBinding("restat") == "" { 796 t.Fatal("expected true") 797 } 798 in2imp := g.GetNode("in2imp") 799 if 1 != len(in2imp.OutEdges) { 800 t.Fatal("expected equal") 801 } 802 if edge2 != in2imp.OutEdges[0] { 803 t.Fatal("expected equal") 804 } 805 } 806 807 func TestGraphTest_DyndepFileMissing(t *testing.T) { 808 g := NewGraphTest(t) 809 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r || dd\n dyndep = dd\n", ParseManifestOpts{}) 810 811 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil { 812 t.Fatal("expected false") 813 } else if err.Error() != "loading 'dd': file does not exist" { 814 t.Fatal(err) 815 } 816 } 817 818 func TestGraphTest_DyndepFileError(t *testing.T) { 819 g := NewGraphTest(t) 820 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r || dd\n dyndep = dd\n", ParseManifestOpts{}) 821 g.fs.Create("dd", "ninja_dyndep_version = 1\n") 822 823 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil { 824 t.Fatal("expected false") 825 } else if err.Error() != "'out' not mentioned in its dyndep file 'dd'" { 826 t.Fatal(err) 827 } 828 } 829 830 func TestGraphTest_DyndepImplicitInputNewer(t *testing.T) { 831 g := NewGraphTest(t) 832 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r || dd\n dyndep = dd\n", ParseManifestOpts{}) 833 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep | in\n") 834 g.fs.Create("out", "") 835 g.fs.Tick() 836 g.fs.Create("in", "") 837 838 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 839 t.Fatal(err) 840 } 841 842 if g.GetNode("in").Dirty { 843 t.Fatal("expected false") 844 } 845 if g.GetNode("dd").Dirty { 846 t.Fatal("expected false") 847 } 848 849 // "out" is dirty due to dyndep-specified implicit input 850 if !g.GetNode("out").Dirty { 851 t.Fatal("expected true") 852 } 853 } 854 855 func TestGraphTest_DyndepFileReady(t *testing.T) { 856 g := NewGraphTest(t) 857 g.AssertParse(&g.state, "rule r\n command = unused\nbuild dd: r dd-in\nbuild out: r || dd\n dyndep = dd\n", ParseManifestOpts{}) 858 g.fs.Create("dd-in", "") 859 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep | in\n") 860 g.fs.Create("out", "") 861 g.fs.Tick() 862 g.fs.Create("in", "") 863 864 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 865 t.Fatal(err) 866 } 867 868 if g.GetNode("in").Dirty { 869 t.Fatal("expected false") 870 } 871 if g.GetNode("dd").Dirty { 872 t.Fatal("expected false") 873 } 874 if !g.GetNode("dd").InEdge.OutputsReady { 875 t.Fatal("expected true") 876 } 877 878 // "out" is dirty due to dyndep-specified implicit input 879 if !g.GetNode("out").Dirty { 880 t.Fatal("expected true") 881 } 882 } 883 884 func TestGraphTest_DyndepFileNotClean(t *testing.T) { 885 g := NewGraphTest(t) 886 g.AssertParse(&g.state, "rule r\n command = unused\nbuild dd: r dd-in\nbuild out: r || dd\n dyndep = dd\n", ParseManifestOpts{}) 887 g.fs.Create("dd", "this-should-not-be-loaded") 888 g.fs.Tick() 889 g.fs.Create("dd-in", "") 890 g.fs.Create("out", "") 891 892 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 893 t.Fatal(err) 894 } 895 896 if !g.GetNode("dd").Dirty { 897 t.Fatal("expected true") 898 } 899 if g.GetNode("dd").InEdge.OutputsReady { 900 t.Fatal("expected false") 901 } 902 903 // "out" is clean but not ready since "dd" is not ready 904 if g.GetNode("out").Dirty { 905 t.Fatal("expected false") 906 } 907 if g.GetNode("out").InEdge.OutputsReady { 908 t.Fatal("expected false") 909 } 910 } 911 912 func TestGraphTest_DyndepFileNotReady(t *testing.T) { 913 g := NewGraphTest(t) 914 g.AssertParse(&g.state, "rule r\n command = unused\nbuild tmp: r\nbuild dd: r dd-in || tmp\nbuild out: r || dd\n dyndep = dd\n", ParseManifestOpts{}) 915 g.fs.Create("dd", "this-should-not-be-loaded") 916 g.fs.Create("dd-in", "") 917 g.fs.Tick() 918 g.fs.Create("out", "") 919 920 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 921 t.Fatal(err) 922 } 923 924 if g.GetNode("dd").Dirty { 925 t.Fatal("expected false") 926 } 927 if g.GetNode("dd").InEdge.OutputsReady { 928 t.Fatal("expected false") 929 } 930 if g.GetNode("out").Dirty { 931 t.Fatal("expected false") 932 } 933 if g.GetNode("out").InEdge.OutputsReady { 934 t.Fatal("expected false") 935 } 936 } 937 938 func TestGraphTest_DyndepFileSecondNotReady(t *testing.T) { 939 g := NewGraphTest(t) 940 g.AssertParse(&g.state, "rule r\n command = unused\nbuild dd1: r dd1-in\nbuild dd2-in: r || dd1\n dyndep = dd1\nbuild dd2: r dd2-in\nbuild out: r || dd2\n dyndep = dd2\n", ParseManifestOpts{}) 941 g.fs.Create("dd1", "") 942 g.fs.Create("dd2", "") 943 g.fs.Create("dd2-in", "") 944 g.fs.Tick() 945 g.fs.Create("dd1-in", "") 946 g.fs.Create("out", "") 947 948 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil { 949 t.Fatal(err) 950 } 951 952 if !g.GetNode("dd1").Dirty { 953 t.Fatal("expected true") 954 } 955 if g.GetNode("dd1").InEdge.OutputsReady { 956 t.Fatal("expected false") 957 } 958 if g.GetNode("dd2").Dirty { 959 t.Fatal("expected false") 960 } 961 if g.GetNode("dd2").InEdge.OutputsReady { 962 t.Fatal("expected false") 963 } 964 if g.GetNode("out").Dirty { 965 t.Fatal("expected false") 966 } 967 if g.GetNode("out").InEdge.OutputsReady { 968 t.Fatal("expected false") 969 } 970 } 971 972 func TestGraphTest_DyndepFileCircular(t *testing.T) { 973 g := NewGraphTest(t) 974 g.AssertParse(&g.state, "rule r\n command = unused\nbuild out: r in || dd\n depfile = out.d\n dyndep = dd\nbuild in: r circ\n", ParseManifestOpts{}) 975 g.fs.Create("out.d", "out: inimp\n") 976 g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out | circ: dyndep\n") 977 g.fs.Create("out", "") 978 979 edge := g.GetNode("out").InEdge 980 if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil { 981 t.Fatal("expected false") 982 } else if err.Error() != "dependency cycle: circ -> in -> circ" { 983 t.Fatal(err) 984 } 985 986 // Verify that "out.d" was loaded exactly once despite 987 // circular reference discovered from dyndep file. 988 if 3 != len(edge.Inputs) { 989 t.Fatal("expected equal") 990 } 991 if "in" != edge.Inputs[0].Path { 992 t.Fatal("expected equal") 993 } 994 if "inimp" != edge.Inputs[1].Path { 995 t.Fatal("expected equal") 996 } 997 if "dd" != edge.Inputs[2].Path { 998 t.Fatal("expected equal") 999 } 1000 if 1 != edge.ImplicitDeps { 1001 t.Fatal("expected equal") 1002 } 1003 if 1 != edge.OrderOnlyDeps { 1004 t.Fatal("expected equal") 1005 } 1006 } 1007 1008 func TestGraphTest_Validation(t *testing.T) { 1009 g := NewGraphTest(t) 1010 g.AssertParse(&g.state, "build out: cat in |@ validate\nbuild validate: cat in\n", ParseManifestOpts{}) 1011 1012 g.fs.Create("in", "") 1013 validationNodes, err := g.scan.RecomputeDirty(g.GetNode("out")) 1014 if err != nil { 1015 t.Fatal(err) 1016 } 1017 1018 if len(validationNodes) != 1 || validationNodes[0].Path != "validate" { 1019 t.Fatal(validationNodes) 1020 } 1021 1022 if !g.GetNode("out").Dirty { 1023 t.Fatal("expected dirty") 1024 } 1025 if !g.GetNode("validate").Dirty { 1026 t.Fatal("expected dirty") 1027 } 1028 } 1029 1030 // Check that phony's dependencies' mtimes are propagated. 1031 func TestGraphTest_PhonyDepsMtimes(t *testing.T) { 1032 g := NewGraphTest(t) 1033 g.AssertParse(&g.state, "rule touch\n command = touch $out\nbuild in_ph: phony in1\nbuild out1: touch in_ph\n", ParseManifestOpts{}) 1034 g.fs.Create("in1", "") 1035 g.fs.Create("out1", "") 1036 out1 := g.GetNode("out1") 1037 in1 := g.GetNode("in1") 1038 1039 if _, err := g.scan.RecomputeDirty(out1); err != nil { 1040 t.Fatal(err) 1041 } 1042 if out1.Dirty { 1043 t.Fatal("expected true") 1044 } 1045 1046 // Get the mtime of out1 1047 if err := in1.Stat(&g.fs); err != nil { 1048 t.Fatal(err) 1049 } 1050 if err := out1.Stat(&g.fs); err != nil { 1051 t.Fatal(err) 1052 } 1053 out1Mtime1 := out1.MTime 1054 in1Mtime1 := in1.MTime 1055 1056 // Touch in1. This should cause out1 to be dirty 1057 g.state.Reset() 1058 g.fs.Tick() 1059 g.fs.Create("in1", "") 1060 1061 if err := in1.Stat(&g.fs); err != nil { 1062 t.Fatal(err) 1063 } 1064 if in1.MTime <= in1Mtime1 { 1065 t.Fatal("expected greater") 1066 } 1067 1068 if _, err := g.scan.RecomputeDirty(out1); err != nil { 1069 t.Fatal(err) 1070 } 1071 if in1.MTime <= in1Mtime1 { 1072 t.Fatal("expected greater") 1073 } 1074 if out1.MTime != out1Mtime1 { 1075 t.Fatal("expected equal") 1076 } 1077 if !out1.Dirty { 1078 t.Fatal("expected true") 1079 } 1080 }