github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/build_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 "errors" 19 "fmt" 20 "os" 21 "path/filepath" 22 "runtime" 23 "sort" 24 "strconv" 25 "strings" 26 "testing" 27 28 "github.com/google/go-cmp/cmp" 29 ) 30 31 // Fixture for tests involving Plan. 32 // Though Plan doesn't use State, it's useful to have one around 33 // to create Nodes and Edges. 34 type PlanTest struct { 35 StateTestWithBuiltinRules 36 plan plan 37 } 38 39 func NewPlanTest(t *testing.T) *PlanTest { 40 return &PlanTest{ 41 StateTestWithBuiltinRules: NewStateTestWithBuiltinRules(t), 42 plan: newPlan(nil), 43 } 44 } 45 46 // Because FindWork does not return Edges in any sort of predictable order, 47 // provide a means to get available Edges in order and in a format which is 48 // easy to write tests around. 49 func (p *PlanTest) FindWorkSorted(count int) []*Edge { 50 var out []*Edge 51 for i := 0; i < count; i++ { 52 if !p.plan.moreToDo() { 53 p.t.Fatal("expected true") 54 } 55 edge := p.plan.findWork() 56 if edge == nil { 57 p.t.Fatal("expected true") 58 } 59 out = append(out, edge) 60 } 61 if p.plan.findWork() != nil { 62 p.t.Fatal("expected false") 63 } 64 sort.Slice(out, func(i, j int) bool { 65 return out[i].Outputs[0].Path < out[j].Outputs[0].Path 66 }) 67 return out 68 } 69 70 func TestPlanTest_Basic(t *testing.T) { 71 p := NewPlanTest(t) 72 p.AssertParse(&p.state, "build out: cat mid\nbuild mid: cat in\n", ParseManifestOpts{}) 73 p.GetNode("mid").Dirty = true 74 p.GetNode("out").Dirty = true 75 if do, err := p.plan.addTarget(p.GetNode("out")); !do || err != nil { 76 t.Fatal(do, err) 77 } 78 if !p.plan.moreToDo() { 79 t.Fatal("expected true") 80 } 81 82 edge := p.plan.findWork() 83 if edge == nil { 84 t.Fatalf("plan is inconsistent: %#v", p.plan) 85 } 86 if "in" != edge.Inputs[0].Path { 87 t.Fatal("expected equal") 88 } 89 if "mid" != edge.Outputs[0].Path { 90 t.Fatal("expected equal") 91 } 92 93 if e := p.plan.findWork(); e != nil { 94 t.Fatalf("%#v", e) 95 } 96 97 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 98 t.Fatal(err) 99 } 100 101 edge = p.plan.findWork() 102 if edge == nil { 103 t.Fatal("expected true") 104 } 105 if "mid" != edge.Inputs[0].Path { 106 t.Fatal("expected equal") 107 } 108 if "out" != edge.Outputs[0].Path { 109 t.Fatal("expected equal") 110 } 111 112 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 113 t.Fatal(err) 114 } 115 116 if p.plan.moreToDo() { 117 t.Fatal("expected false") 118 } 119 edge = p.plan.findWork() 120 if edge != nil { 121 t.Fatal("expected equal") 122 } 123 } 124 125 // Test that two outputs from one rule can be handled as inputs to the next. 126 func TestPlanTest_DoubleOutputDirect(t *testing.T) { 127 p := NewPlanTest(t) 128 p.AssertParse(&p.state, "build out: cat mid1 mid2\nbuild mid1 mid2: cat in\n", ParseManifestOpts{}) 129 p.GetNode("mid1").Dirty = true 130 p.GetNode("mid2").Dirty = true 131 p.GetNode("out").Dirty = true 132 133 if do, err := p.plan.addTarget(p.GetNode("out")); !do || err != nil { 134 t.Fatal(do, err) 135 } 136 if !p.plan.moreToDo() { 137 t.Fatal("expected true") 138 } 139 140 edge := p.plan.findWork() 141 if edge == nil { 142 t.Fatal("expected true") 143 } // cat in 144 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 145 t.Fatal(err) 146 } 147 148 edge = p.plan.findWork() 149 if edge == nil { 150 t.Fatal("expected true") 151 } // cat mid1 mid2 152 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 153 t.Fatal(err) 154 } 155 156 edge = p.plan.findWork() 157 if edge != nil { 158 t.Fatal("expected false") 159 } // done 160 } 161 162 // Test that two outputs from one rule can eventually be routed to another. 163 func TestPlanTest_DoubleOutputIndirect(t *testing.T) { 164 p := NewPlanTest(t) 165 p.AssertParse(&p.state, "build out: cat b1 b2\nbuild b1: cat a1\nbuild b2: cat a2\nbuild a1 a2: cat in\n", ParseManifestOpts{}) 166 p.GetNode("a1").Dirty = true 167 p.GetNode("a2").Dirty = true 168 p.GetNode("b1").Dirty = true 169 p.GetNode("b2").Dirty = true 170 p.GetNode("out").Dirty = true 171 if do, err := p.plan.addTarget(p.GetNode("out")); !do || err != nil { 172 t.Fatal(do, err) 173 } 174 if !p.plan.moreToDo() { 175 t.Fatal("expected true") 176 } 177 178 edge := p.plan.findWork() 179 if edge == nil { 180 t.Fatal("expected true") 181 } // cat in 182 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 183 t.Fatal(err) 184 } 185 186 edge = p.plan.findWork() 187 if edge == nil { 188 t.Fatal("expected true") 189 } // cat a1 190 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 191 t.Fatal(err) 192 } 193 194 edge = p.plan.findWork() 195 if edge == nil { 196 t.Fatal("expected true") 197 } // cat a2 198 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 199 t.Fatal(err) 200 } 201 202 edge = p.plan.findWork() 203 if edge == nil { 204 t.Fatal("expected true") 205 } // cat b1 b2 206 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 207 t.Fatal(err) 208 } 209 210 edge = p.plan.findWork() 211 if edge != nil { 212 t.Fatal("expected false") 213 } // done 214 } 215 216 // Test that two edges from one output can both execute. 217 func TestPlanTest_DoubleDependent(t *testing.T) { 218 p := NewPlanTest(t) 219 p.AssertParse(&p.state, "build out: cat a1 a2\nbuild a1: cat mid\nbuild a2: cat mid\nbuild mid: cat in\n", ParseManifestOpts{}) 220 p.GetNode("mid").Dirty = true 221 p.GetNode("a1").Dirty = true 222 p.GetNode("a2").Dirty = true 223 p.GetNode("out").Dirty = true 224 225 if do, err := p.plan.addTarget(p.GetNode("out")); !do || err != nil { 226 t.Fatal(do, err) 227 } 228 if !p.plan.moreToDo() { 229 t.Fatal("expected true") 230 } 231 232 edge := p.plan.findWork() 233 if edge == nil { 234 t.Fatal("expected true") 235 } // cat in 236 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 237 t.Fatal(err) 238 } 239 240 edge = p.plan.findWork() 241 if edge == nil { 242 t.Fatal("expected true") 243 } // cat mid 244 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 245 t.Fatal(err) 246 } 247 248 edge = p.plan.findWork() 249 if edge == nil { 250 t.Fatal("expected true") 251 } // cat mid 252 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 253 t.Fatal(err) 254 } 255 256 edge = p.plan.findWork() 257 if edge == nil { 258 t.Fatal("expected true") 259 } // cat a1 a2 260 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 261 t.Fatal(err) 262 } 263 264 edge = p.plan.findWork() 265 if edge != nil { 266 t.Fatal("expected false") 267 } // done 268 } 269 270 func (p *PlanTest) TestPoolWithDepthOne(testCase string) { 271 p.AssertParse(&p.state, testCase, ParseManifestOpts{}) 272 p.GetNode("out1").Dirty = true 273 p.GetNode("out2").Dirty = true 274 if do, err := p.plan.addTarget(p.GetNode("out1")); !do || err != nil { 275 p.t.Fatal(do, err) 276 } 277 if do, err := p.plan.addTarget(p.GetNode("out2")); !do || err != nil { 278 p.t.Fatal(do, err) 279 } 280 if !p.plan.moreToDo() { 281 p.t.Fatal("expected true") 282 } 283 284 edge := p.plan.findWork() 285 if edge == nil { 286 p.t.Fatal("expected true") 287 } 288 if "in" != edge.Inputs[0].Path { 289 p.t.Fatal("expected equal") 290 } 291 if "out1" != edge.Outputs[0].Path { 292 p.t.Fatal("expected equal") 293 } 294 295 // This will be false since poolcat is serialized 296 if p.plan.findWork() != nil { 297 p.t.Fatal("expected false") 298 } 299 300 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 301 p.t.Fatal(err) 302 } 303 304 edge = p.plan.findWork() 305 if edge == nil { 306 p.t.Fatal("expected true") 307 } 308 if "in" != edge.Inputs[0].Path { 309 p.t.Fatal("expected equal") 310 } 311 if "out2" != edge.Outputs[0].Path { 312 p.t.Fatal("expected equal") 313 } 314 315 if p.plan.findWork() != nil { 316 p.t.Fatal("expected false") 317 } 318 319 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 320 p.t.Fatal(err) 321 } 322 323 if p.plan.moreToDo() { 324 p.t.Fatal("expected false") 325 } 326 edge = p.plan.findWork() 327 if edge != nil { 328 p.t.Fatal("expected equal") 329 } 330 } 331 332 func TestPlanTest_PoolWithDepthOne(t *testing.T) { 333 p := NewPlanTest(t) 334 p.TestPoolWithDepthOne("pool foobar\n depth = 1\nrule poolcat\n command = cat $in > $out\n pool = foobar\nbuild out1: poolcat in\nbuild out2: poolcat in\n") 335 } 336 337 func TestPlanTest_ConsolePool(t *testing.T) { 338 p := NewPlanTest(t) 339 p.TestPoolWithDepthOne("rule poolcat\n command = cat $in > $out\n pool = console\nbuild out1: poolcat in\nbuild out2: poolcat in\n") 340 } 341 342 func TestPlanTest_PoolsWithDepthTwo(t *testing.T) { 343 p := NewPlanTest(t) 344 p.AssertParse(&p.state, "pool foobar\n depth = 2\npool bazbin\n depth = 2\nrule foocat\n command = cat $in > $out\n pool = foobar\nrule bazcat\n command = cat $in > $out\n pool = bazbin\nbuild out1: foocat in\nbuild out2: foocat in\nbuild out3: foocat in\nbuild outb1: bazcat in\nbuild outb2: bazcat in\nbuild outb3: bazcat in\n pool =\nbuild allTheThings: cat out1 out2 out3 outb1 outb2 outb3\n", ParseManifestOpts{}) 345 // Mark all the out* nodes dirty 346 for i := 0; i < 3; i++ { 347 p.GetNode(fmt.Sprintf("out%d", i+1)).Dirty = true 348 p.GetNode(fmt.Sprintf("outb%d", i+1)).Dirty = true 349 } 350 p.GetNode("allTheThings").Dirty = true 351 352 if do, err := p.plan.addTarget(p.GetNode("allTheThings")); !do || err != nil { 353 t.Fatal(do, err) 354 } 355 356 edges := p.FindWorkSorted(5) 357 358 for i := 0; i < 4; i++ { 359 edge := edges[i] 360 if "in" != edge.Inputs[0].Path { 361 t.Fatal("expected equal") 362 } 363 baseName := "outb" 364 if i < 2 { 365 baseName = "out" 366 } 367 if want := fmt.Sprintf("%s%d", baseName, 1+(i%2)); want != edge.Outputs[0].Path { 368 t.Fatal(want) 369 } 370 } 371 372 // outb3 is exempt because it has an empty pool 373 edge := edges[4] 374 if edge == nil { 375 t.Fatal("expected true") 376 } 377 if "in" != edge.Inputs[0].Path { 378 t.Fatal("expected equal") 379 } 380 if "outb3" != edge.Outputs[0].Path { 381 t.Fatal("expected equal") 382 } 383 384 // finish out1 385 if err := p.plan.edgeFinished(edges[0], edgeSucceeded); err != nil { 386 t.Fatal(err) 387 } 388 edges = edges[1:] 389 390 // out3 should be available 391 out3 := p.plan.findWork() 392 if out3 == nil { 393 t.Fatal("expected true") 394 } 395 if "in" != out3.Inputs[0].Path { 396 t.Fatal("expected equal") 397 } 398 if "out3" != out3.Outputs[0].Path { 399 t.Fatal("expected equal") 400 } 401 402 if p.plan.findWork() != nil { 403 t.Fatal("expected false") 404 } 405 406 if err := p.plan.edgeFinished(out3, edgeSucceeded); err != nil { 407 t.Fatal(err) 408 } 409 410 if p.plan.findWork() != nil { 411 t.Fatal("expected false") 412 } 413 414 for _, it := range edges { 415 if err := p.plan.edgeFinished(it, edgeSucceeded); err != nil { 416 t.Fatal(err) 417 } 418 } 419 420 last := p.plan.findWork() 421 if last == nil { 422 t.Fatal("expected true") 423 } 424 if "allTheThings" != last.Outputs[0].Path { 425 t.Fatal("expected equal") 426 } 427 428 if err := p.plan.edgeFinished(last, edgeSucceeded); err != nil { 429 t.Fatal(err) 430 } 431 432 if p.plan.moreToDo() { 433 t.Fatal("expected false") 434 } 435 if p.plan.findWork() != nil { 436 t.Fatal("expected false") 437 } 438 } 439 440 func TestPlanTest_PoolWithRedundantEdges(t *testing.T) { 441 p := NewPlanTest(t) 442 p.AssertParse(&p.state, "pool compile\n depth = 1\nrule gen_foo\n command = touch foo.cpp\nrule gen_bar\n command = touch bar.cpp\nrule echo\n command = echo $out > $out\nbuild foo.cpp.obj: echo foo.cpp || foo.cpp\n pool = compile\nbuild bar.cpp.obj: echo bar.cpp || bar.cpp\n pool = compile\nbuild libfoo.a: echo foo.cpp.obj bar.cpp.obj\nbuild foo.cpp: gen_foo\nbuild bar.cpp: gen_bar\nbuild all: phony libfoo.a\n", ParseManifestOpts{}) 443 p.GetNode("foo.cpp").Dirty = true 444 p.GetNode("foo.cpp.obj").Dirty = true 445 p.GetNode("bar.cpp").Dirty = true 446 p.GetNode("bar.cpp.obj").Dirty = true 447 p.GetNode("libfoo.a").Dirty = true 448 p.GetNode("all").Dirty = true 449 if do, err := p.plan.addTarget(p.GetNode("all")); !do || err != nil { 450 t.Fatal(do, err) 451 } 452 if !p.plan.moreToDo() { 453 t.Fatal("expected true") 454 } 455 456 initialEdges := p.FindWorkSorted(2) 457 458 edge := initialEdges[1] // Foo first 459 if "foo.cpp" != edge.Outputs[0].Path { 460 t.Fatal("expected equal") 461 } 462 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 463 t.Fatal(err) 464 } 465 466 edge = p.plan.findWork() 467 if edge == nil { 468 t.Fatal("expected true") 469 } 470 if p.plan.findWork() != nil { 471 t.Fatal("expected false") 472 } 473 if "foo.cpp" != edge.Inputs[0].Path { 474 t.Fatal("expected equal") 475 } 476 if "foo.cpp" != edge.Inputs[1].Path { 477 t.Fatal("expected equal") 478 } 479 if "foo.cpp.obj" != edge.Outputs[0].Path { 480 t.Fatal("expected equal") 481 } 482 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 483 t.Fatal(err) 484 } 485 486 edge = initialEdges[0] // Now for bar 487 if "bar.cpp" != edge.Outputs[0].Path { 488 t.Fatal("expected equal") 489 } 490 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 491 t.Fatal(err) 492 } 493 494 edge = p.plan.findWork() 495 if edge == nil { 496 t.Fatal("expected true") 497 } 498 if p.plan.findWork() != nil { 499 t.Fatal("expected false") 500 } 501 if "bar.cpp" != edge.Inputs[0].Path { 502 t.Fatal(edge.Inputs[0].Path) 503 } 504 if "bar.cpp" != edge.Inputs[1].Path { 505 t.Fatal("expected equal") 506 } 507 if "bar.cpp.obj" != edge.Outputs[0].Path { 508 t.Fatal("expected equal") 509 } 510 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 511 t.Fatal(err) 512 } 513 514 edge = p.plan.findWork() 515 if edge == nil { 516 t.Fatal("expected true") 517 } 518 if p.plan.findWork() != nil { 519 t.Fatal("expected false") 520 } 521 if "foo.cpp.obj" != edge.Inputs[0].Path { 522 t.Fatal("expected equal") 523 } 524 if "bar.cpp.obj" != edge.Inputs[1].Path { 525 t.Fatal("expected equal") 526 } 527 if "libfoo.a" != edge.Outputs[0].Path { 528 t.Fatal("expected equal") 529 } 530 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 531 t.Fatal(err) 532 } 533 534 edge = p.plan.findWork() 535 if edge == nil { 536 t.Fatal("expected true") 537 } 538 if p.plan.findWork() != nil { 539 t.Fatal("expected false") 540 } 541 if "libfoo.a" != edge.Inputs[0].Path { 542 t.Fatal("expected equal") 543 } 544 if "all" != edge.Outputs[0].Path { 545 t.Fatal("expected equal") 546 } 547 if err := p.plan.edgeFinished(edge, edgeSucceeded); err != nil { 548 t.Fatal(err) 549 } 550 551 edge = p.plan.findWork() 552 if edge != nil { 553 t.Fatal("expected false") 554 } 555 if p.plan.moreToDo() { 556 t.Fatal("expected false") 557 } 558 } 559 560 func TestPlanTest_PoolWithFailingEdge(t *testing.T) { 561 p := NewPlanTest(t) 562 p.AssertParse(&p.state, "pool foobar\n depth = 1\nrule poolcat\n command = cat $in > $out\n pool = foobar\nbuild out1: poolcat in\nbuild out2: poolcat in\n", ParseManifestOpts{}) 563 p.GetNode("out1").Dirty = true 564 p.GetNode("out2").Dirty = true 565 if do, err := p.plan.addTarget(p.GetNode("out1")); !do || err != nil { 566 t.Fatal(do, err) 567 } 568 if do, err := p.plan.addTarget(p.GetNode("out2")); !do || err != nil { 569 t.Fatal(do, err) 570 } 571 if !p.plan.moreToDo() { 572 t.Fatal("expected true") 573 } 574 575 edge := p.plan.findWork() 576 if edge == nil { 577 t.Fatal("expected true") 578 } 579 if "in" != edge.Inputs[0].Path { 580 t.Fatal("expected equal") 581 } 582 if "out1" != edge.Outputs[0].Path { 583 t.Fatal("expected equal") 584 } 585 586 // This will be false since poolcat is serialized 587 if p.plan.findWork() != nil { 588 t.Fatal("expected false") 589 } 590 591 if err := p.plan.edgeFinished(edge, edgeFailed); err != nil { 592 t.Fatal(err) 593 } 594 595 edge = p.plan.findWork() 596 if edge == nil { 597 t.Fatal("expected true") 598 } 599 if "in" != edge.Inputs[0].Path { 600 t.Fatal("expected equal") 601 } 602 if "out2" != edge.Outputs[0].Path { 603 t.Fatal("expected equal") 604 } 605 606 if p.plan.findWork() != nil { 607 t.Fatal("expected false") 608 } 609 610 if err := p.plan.edgeFinished(edge, edgeFailed); err != nil { 611 t.Fatal(err) 612 } 613 614 if !p.plan.moreToDo() { 615 t.Fatal("expected true") 616 } // Jobs have failed 617 edge = p.plan.findWork() 618 if edge != nil { 619 t.Fatal("expected equal") 620 } 621 } 622 623 type statusFake struct{} 624 625 func (s *statusFake) PlanHasTotalEdges(total int) {} 626 func (s *statusFake) BuildEdgeStarted(edge *Edge, startTimeMillis int32) {} 627 func (s *statusFake) BuildEdgeFinished(edge *Edge, endTimeMillis int32, success bool, output string) { 628 } 629 func (s *statusFake) BuildLoadDyndeps() {} 630 func (s *statusFake) BuildStarted() {} 631 func (s *statusFake) BuildFinished() {} 632 func (s *statusFake) Info(msg string, i ...interface{}) {} 633 func (s *statusFake) Warning(msg string, i ...interface{}) {} 634 func (s *statusFake) Error(msg string, i ...interface{}) {} 635 636 type BuildTestBase struct { 637 StateTestWithBuiltinRules 638 config BuildConfig 639 commandRunner FakeCommandRunner 640 fs VirtualFileSystem 641 status Status 642 } 643 644 func NewBuildTestBase(t *testing.T) *BuildTestBase { 645 b := &BuildTestBase{ 646 StateTestWithBuiltinRules: NewStateTestWithBuiltinRules(t), 647 config: NewBuildConfig(), 648 fs: NewVirtualFileSystem(), 649 status: &statusFake{}, 650 } 651 b.config.Verbosity = Quiet 652 b.commandRunner = NewFakeCommandRunner(t, &b.fs) 653 b.AssertParse(&b.state, "build cat1: cat in1\nbuild cat2: cat in1 in2\nbuild cat12: cat cat1 cat2\n", ParseManifestOpts{}) 654 b.fs.Create("in1", "") 655 b.fs.Create("in2", "") 656 return b 657 } 658 659 func (b *BuildTestBase) IsPathDead(s string) bool { 660 return false 661 } 662 663 // Rebuild target in the 'working tree' (fs). 664 // State of commandRunner and logs contents (if specified) ARE MODIFIED. 665 // Handy to check for NOOP builds, and higher-level rebuild tests. 666 func (b *BuildTestBase) RebuildTarget(target, manifest, logPath, depsPath string, state *State) { 667 pstate := state 668 if pstate == nil { 669 localState := NewState() 670 pstate = &localState 671 } 672 b.AddCatRule(pstate) 673 b.AssertParse(pstate, manifest, ParseManifestOpts{}) 674 675 var pbuildLog *BuildLog 676 if logPath != "" { 677 buildLog := NewBuildLog() 678 defer buildLog.Close() 679 if s, err := buildLog.Load(logPath); !((s == LoadSuccess && err == nil) || (s == LoadNotFound && os.IsNotExist(err))) { 680 b.t.Fatalf("%s = %d: %s", logPath, s, err) 681 } 682 if err := buildLog.OpenForWrite(logPath, b); err != nil { 683 b.t.Fatal(err) 684 } 685 pbuildLog = &buildLog 686 } 687 688 var pdepsLog *DepsLog 689 if depsPath != "" { 690 pdepsLog = &DepsLog{} 691 defer pdepsLog.Close() 692 if s, err := pdepsLog.Load(depsPath, pstate); !((s == LoadSuccess && err == nil) || (s == LoadNotFound && os.IsNotExist(err))) { 693 b.t.Fatalf("%s = %d: %s", depsPath, s, err) 694 } 695 if err := pdepsLog.OpenForWrite(depsPath); err != nil { 696 b.t.Fatal(err) 697 } 698 } 699 700 builder := NewBuilder(pstate, &b.config, pbuildLog, pdepsLog, &b.fs, b.status, 0) 701 if _, err := builder.addTargetName(target); err != nil { 702 b.t.Fatal(err) 703 } 704 705 b.commandRunner.commandsRan = nil 706 builder.commandRunner = &b.commandRunner 707 if !builder.AlreadyUpToDate() { 708 if err := builder.Build(); err != nil { 709 b.t.Fatal(err) 710 } 711 } 712 } 713 714 // Fake implementation of CommandRunner, useful for tests. 715 type FakeCommandRunner struct { 716 t *testing.T 717 commandsRan []string 718 activeEdges []*Edge 719 maxActiveEdges uint 720 fs *VirtualFileSystem 721 } 722 723 func NewFakeCommandRunner(t *testing.T, fs *VirtualFileSystem) FakeCommandRunner { 724 return FakeCommandRunner{ 725 t: t, 726 maxActiveEdges: 1, 727 fs: fs, 728 } 729 } 730 731 // CommandRunner impl 732 func (f *FakeCommandRunner) CanRunMore() bool { 733 return len(f.activeEdges) < int(f.maxActiveEdges) 734 } 735 736 func (f *FakeCommandRunner) StartCommand(edge *Edge) bool { 737 cmd := edge.EvaluateCommand(false) 738 //f.t.Logf("StartCommand(%s)", cmd) 739 if len(f.activeEdges) > int(f.maxActiveEdges) { 740 f.t.Fatal("oops") 741 } 742 found := false 743 for _, a := range f.activeEdges { 744 if a == edge { 745 found = true 746 break 747 } 748 } 749 if found { 750 f.t.Fatalf("running same edge twice") 751 } 752 f.commandsRan = append(f.commandsRan, cmd) 753 if edge.Rule.Name == "cat" || edge.Rule.Name == "cat_rsp" || edge.Rule.Name == "cat_rsp_out" || edge.Rule.Name == "cc" || edge.Rule.Name == "cp_multi_msvc" || edge.Rule.Name == "cp_multi_gcc" || edge.Rule.Name == "touch" || edge.Rule.Name == "touch-interrupt" || edge.Rule.Name == "touch-fail-tick2" { 754 for _, out := range edge.Outputs { 755 f.fs.Create(out.Path, "") 756 } 757 } else if edge.Rule.Name == "true" || edge.Rule.Name == "fail" || edge.Rule.Name == "interrupt" || edge.Rule.Name == "console" { 758 // Don't do anything. 759 } else if edge.Rule.Name == "cp" { 760 if len(edge.Inputs) == 0 { 761 f.t.Fatal("oops") 762 } 763 if len(edge.Outputs) != 1 { 764 f.t.Fatalf("%#v", edge.Outputs) 765 } 766 content, err := f.fs.ReadFile(edge.Inputs[0].Path) 767 if err == nil { 768 // ReadFile append a zero byte, strip it when writing back. 769 c := content 770 if len(c) != 0 { 771 c = c[:len(c)-1] 772 } 773 f.fs.WriteFile(edge.Outputs[0].Path, string(c)) 774 } 775 } else if edge.Rule.Name == "touch-implicit-dep-out" { 776 dep := edge.GetBinding("test_dependency") 777 f.fs.Create(dep, "") 778 f.fs.Tick() 779 for _, out := range edge.Outputs { 780 f.fs.Create(out.Path, "") 781 } 782 } else if edge.Rule.Name == "touch-out-implicit-dep" { 783 dep := edge.GetBinding("test_dependency") 784 for _, out := range edge.Outputs { 785 f.fs.Create(out.Path, "") 786 } 787 f.fs.Tick() 788 f.fs.Create(dep, "") 789 } else if edge.Rule.Name == "generate-depfile" { 790 dep := edge.GetBinding("test_dependency") 791 depfile := edge.GetUnescapedDepfile() 792 contents := "" 793 for _, out := range edge.Outputs { 794 contents += out.Path + ": " + dep + "\n" 795 f.fs.Create(out.Path, "") 796 } 797 f.fs.Create(depfile, contents) 798 } else { 799 fmt.Printf("unknown command\n") 800 return false 801 } 802 803 f.activeEdges = append(f.activeEdges, edge) 804 805 // Allow tests to control the order by the name of the first output. 806 sort.Slice(f.activeEdges, func(i, j int) bool { 807 return f.activeEdges[i].Outputs[0].Path < f.activeEdges[j].Outputs[0].Path 808 }) 809 return true 810 } 811 812 func (f *FakeCommandRunner) WaitForCommand(result *Result) bool { 813 if len(f.activeEdges) == 0 { 814 return false 815 } 816 817 // All active edges were already completed immediately when started, 818 // so we can pick any edge here. Pick the last edge. Tests can 819 // control the order of edges by the name of the first output. 820 edgeIter := len(f.activeEdges) - 1 821 822 edge := f.activeEdges[edgeIter] 823 result.Edge = edge 824 825 if edge.Rule.Name == "interrupt" || edge.Rule.Name == "touch-interrupt" { 826 result.ExitCode = ExitInterrupted 827 return true 828 } 829 830 if edge.Rule.Name == "console" { 831 if edge.Pool == ConsolePool { 832 result.ExitCode = ExitSuccess 833 } else { 834 result.ExitCode = ExitFailure 835 } 836 copy(f.activeEdges[edgeIter:], f.activeEdges[edgeIter+1:]) 837 f.activeEdges = f.activeEdges[:len(f.activeEdges)-1] 838 return true 839 } 840 841 if edge.Rule.Name == "cp_multi_msvc" { 842 prefix := edge.GetBinding("msvc_deps_prefix") 843 for _, in := range edge.Inputs { 844 result.Output += prefix + in.Path + "\n" 845 } 846 } 847 848 if edge.Rule.Name == "fail" || (edge.Rule.Name == "touch-fail-tick2" && f.fs.now == 2) { 849 result.ExitCode = ExitFailure 850 } else { 851 result.ExitCode = ExitSuccess 852 } 853 854 // Provide a way for test cases to verify when an edge finishes that 855 // some other edge is still active. This is useful for test cases 856 // covering behavior involving multiple active edges. 857 verifyActiveEdge := edge.GetBinding("verify_active_edge") 858 if verifyActiveEdge != "" { 859 verifyActiveEdgeFound := false 860 for _, i := range f.activeEdges { 861 if len(i.Outputs) != 0 && i.Outputs[0].Path == verifyActiveEdge { 862 verifyActiveEdgeFound = true 863 } 864 } 865 if !verifyActiveEdgeFound { 866 f.t.Fatal("expected true") 867 } 868 } 869 870 copy(f.activeEdges[edgeIter:], f.activeEdges[edgeIter+1:]) 871 f.activeEdges = f.activeEdges[:len(f.activeEdges)-1] 872 return true 873 } 874 875 func (f *FakeCommandRunner) GetActiveEdges() []*Edge { 876 return f.activeEdges 877 } 878 879 func (f *FakeCommandRunner) Abort() { 880 f.activeEdges = nil 881 } 882 883 type BuildTest struct { 884 *BuildTestBase 885 builder *Builder 886 } 887 888 func NewBuildTest(t *testing.T) *BuildTest { 889 b := &BuildTest{ 890 BuildTestBase: NewBuildTestBase(t), 891 } 892 b.builder = NewBuilder(&b.state, &b.config, nil, nil, &b.fs, b.status, 0) 893 b.builder.commandRunner = &b.commandRunner 894 // TODO(maruel): Only do it for tests that write to disk. 895 CreateTempDirAndEnter(t) 896 return b 897 } 898 899 // Mark a path dirty. 900 func (b *BuildTest) Dirty(path string) { 901 node := b.GetNode(path) 902 node.Dirty = true 903 904 // If it's an input file, mark that we've already stat()ed it and 905 // it's missing. 906 if node.InEdge == nil { 907 if node.MTime == -1 { 908 node.MTime = 0 909 } 910 node.Exists = ExistenceStatusMissing 911 } 912 } 913 914 func TestBuildTest_NoWork(t *testing.T) { 915 b := NewBuildTest(t) 916 if !b.builder.AlreadyUpToDate() { 917 t.Fatal("expected true") 918 } 919 } 920 921 func TestBuildTest_OneStep(t *testing.T) { 922 b := NewBuildTest(t) 923 // Given a dirty target with one ready input, 924 // we should rebuild the target. 925 b.Dirty("cat1") 926 if _, err := b.builder.addTargetName("cat1"); err != nil { 927 t.Fatal(err) 928 } 929 if err := b.builder.Build(); err != nil { 930 t.Fatal() 931 } 932 933 wantCommands := []string{"cat in1 > cat1"} 934 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 935 t.Fatal(diff) 936 } 937 } 938 939 func TestBuildTest_OneStep2(t *testing.T) { 940 b := NewBuildTest(t) 941 // Given a target with one dirty input, 942 // we should rebuild the target. 943 b.Dirty("cat1") 944 if _, err := b.builder.addTargetName("cat1"); err != nil { 945 t.Fatal(err) 946 } 947 if err := b.builder.Build(); err != nil { 948 t.Fatal(err) 949 } 950 951 wantCommands := []string{"cat in1 > cat1"} 952 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 953 t.Fatal(diff) 954 } 955 } 956 957 func TestBuildTest_TwoStep(t *testing.T) { 958 b := NewBuildTest(t) 959 if _, err := b.builder.addTargetName("cat12"); err != nil { 960 t.Fatal(err) 961 } 962 if err := b.builder.Build(); err != nil { 963 t.Fatal(err) 964 } 965 if 3 != len(b.commandRunner.commandsRan) { 966 t.Fatal("expected equal") 967 } 968 // Depending on how the pointers work out, we could've ran 969 // the first two commands in either order. 970 if !(b.commandRunner.commandsRan[0] == "cat in1 > cat1" && b.commandRunner.commandsRan[1] == "cat in1 in2 > cat2") || (b.commandRunner.commandsRan[1] == "cat in1 > cat1" && b.commandRunner.commandsRan[0] == "cat in1 in2 > cat2") { 971 t.Fatal("expected true") 972 } 973 974 if "cat cat1 cat2 > cat12" != b.commandRunner.commandsRan[2] { 975 t.Fatal("expected equal") 976 } 977 978 b.fs.Tick() 979 980 // Modifying in2 requires rebuilding one intermediate file 981 // and the final file. 982 b.fs.Create("in2", "") 983 b.state.Reset() 984 if _, err := b.builder.addTargetName("cat12"); err != nil { 985 t.Fatal(err) 986 } 987 if err := b.builder.Build(); err != nil { 988 t.Fatal() 989 } 990 if 5 != len(b.commandRunner.commandsRan) { 991 t.Fatal("expected equal") 992 } 993 if "cat in1 in2 > cat2" != b.commandRunner.commandsRan[3] { 994 t.Fatal("expected equal") 995 } 996 if "cat cat1 cat2 > cat12" != b.commandRunner.commandsRan[4] { 997 t.Fatal("expected equal") 998 } 999 } 1000 1001 func TestBuildTest_TwoOutputs(t *testing.T) { 1002 b := NewBuildTest(t) 1003 b.AssertParse(&b.state, "rule touch\n command = touch $out\nbuild out1 out2: touch in.txt\n", ParseManifestOpts{}) 1004 1005 b.fs.Create("in.txt", "") 1006 1007 if _, err := b.builder.addTargetName("out1"); err != nil { 1008 t.Fatal(err) 1009 } 1010 if err := b.builder.Build(); err != nil { 1011 t.Fatal(err) 1012 } 1013 wantCommands := []string{"touch out1 out2"} 1014 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 1015 t.Fatal(diff) 1016 } 1017 } 1018 1019 func TestBuildTest_ImplicitOutput(t *testing.T) { 1020 b := NewBuildTest(t) 1021 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nbuild out | out.imp: touch in.txt\n", ParseManifestOpts{}) 1022 b.fs.Create("in.txt", "") 1023 1024 if _, err := b.builder.addTargetName("out.imp"); err != nil { 1025 t.Fatal(err) 1026 } 1027 if err := b.builder.Build(); err != nil { 1028 t.Fatal(err) 1029 } 1030 wantCommands := []string{"touch out out.imp"} 1031 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 1032 t.Fatal(diff) 1033 } 1034 } 1035 1036 // Test case from 1037 // https://github.com/ninja-build/ninja/issues/148 1038 func TestBuildTest_MultiOutIn(t *testing.T) { 1039 b := NewBuildTest(t) 1040 b.AssertParse(&b.state, "rule touch\n command = touch $out\nbuild in1 otherfile: touch in\nbuild out: touch in | in1\n", ParseManifestOpts{}) 1041 1042 b.fs.Create("in", "") 1043 b.fs.Tick() 1044 b.fs.Create("in1", "") 1045 1046 if _, err := b.builder.addTargetName("out"); err != nil { 1047 t.Fatal(err) 1048 } 1049 if err := b.builder.Build(); err != nil { 1050 t.Fatal(err) 1051 } 1052 } 1053 1054 func TestBuildTest_Chain(t *testing.T) { 1055 b := NewBuildTest(t) 1056 b.AssertParse(&b.state, "build c2: cat c1\nbuild c3: cat c2\nbuild c4: cat c3\nbuild c5: cat c4\n", ParseManifestOpts{}) 1057 1058 b.fs.Create("c1", "") 1059 1060 if _, err := b.builder.addTargetName("c5"); err != nil { 1061 t.Fatal(err) 1062 } 1063 if err := b.builder.Build(); err != nil { 1064 t.Fatal(err) 1065 } 1066 if 4 != len(b.commandRunner.commandsRan) { 1067 t.Fatal("expected equal") 1068 } 1069 1070 b.commandRunner.commandsRan = nil 1071 b.state.Reset() 1072 if _, err := b.builder.addTargetName("c5"); err != nil { 1073 t.Fatal(err) 1074 } 1075 if !b.builder.AlreadyUpToDate() { 1076 t.Fatal("expected true") 1077 } 1078 1079 b.fs.Tick() 1080 1081 b.fs.Create("c3", "") 1082 b.commandRunner.commandsRan = nil 1083 b.state.Reset() 1084 if _, err := b.builder.addTargetName("c5"); err != nil { 1085 t.Fatal(err) 1086 } 1087 if b.builder.AlreadyUpToDate() { 1088 t.Fatal("expected false") 1089 } 1090 if err := b.builder.Build(); err != nil { 1091 t.Fatal(err) 1092 } 1093 if 2 != len(b.commandRunner.commandsRan) { 1094 t.Fatal("expected equal") 1095 } // 3->4, 4->5 1096 } 1097 1098 func TestBuildTest_MissingInput(t *testing.T) { 1099 b := NewBuildTest(t) 1100 // Input is referenced by build file, but no rule for it. 1101 b.Dirty("in1") 1102 if n, err := b.builder.addTargetName("cat1"); n != nil || err == nil { 1103 t.Fatal("expected failure") 1104 } else if err.Error() != "'in1', needed by 'cat1', missing and no known rule to make it" { 1105 t.Fatal(err) 1106 } 1107 } 1108 1109 func TestBuildTest_MissingTarget(t *testing.T) { 1110 b := NewBuildTest(t) 1111 // Target is not referenced by build file. 1112 if _, err := b.builder.addTargetName("meow"); err == nil { 1113 t.Fatal("expected false") 1114 } else if err.Error() != "unknown target: 'meow'" { 1115 t.Fatal(err) 1116 } 1117 } 1118 1119 func TestBuildTest_MissingInputTarget(t *testing.T) { 1120 b := NewBuildTest(t) 1121 // Target is a missing input file 1122 b.Dirty("in1") 1123 if _, err := b.builder.addTargetName("in1"); err == nil { 1124 t.Fatal("unexpected success") 1125 } else if err.Error() != "'in1' missing and no known rule to make it" { 1126 t.Fatal(err) 1127 } 1128 } 1129 1130 func TestBuildTest_MakeDirs(t *testing.T) { 1131 b := NewBuildTest(t) 1132 1133 p := filepath.Join("subdir", "dir2", "file") 1134 b.AssertParse(&b.state, "build "+p+": cat in1\n", ParseManifestOpts{}) 1135 if _, err := b.builder.addTargetName("subdir/dir2/file"); err != nil { 1136 t.Fatal(err) 1137 } 1138 1139 if err := b.builder.Build(); err != nil { 1140 t.Fatal(err) 1141 } 1142 wantMade := map[string]struct{}{ 1143 "subdir": {}, 1144 filepath.Join("subdir", "dir2"): {}, 1145 } 1146 if diff := cmp.Diff(wantMade, b.fs.directoriesMade); diff != "" { 1147 t.Fatal(diff) 1148 } 1149 } 1150 1151 func TestBuildTest_DepFileMissing(t *testing.T) { 1152 b := NewBuildTest(t) 1153 b.AssertParse(&b.state, "rule cc\n command = cc $in\n depfile = $out.d\nbuild fo$ o.o: cc foo.c\n", ParseManifestOpts{}) 1154 b.fs.Create("foo.c", "") 1155 1156 if _, err := b.builder.addTargetName("fo o.o"); err != nil { 1157 t.Fatal(err) 1158 } 1159 if 1 != len(b.fs.filesRead) { 1160 t.Fatal("expected equal") 1161 } 1162 if "fo o.o.d" != b.fs.filesRead[0] { 1163 t.Fatal("expected equal") 1164 } 1165 } 1166 1167 func TestBuildTest_DepFileOK(t *testing.T) { 1168 b := NewBuildTest(t) 1169 origEdges := len(b.state.Edges) 1170 b.AssertParse(&b.state, "rule cc\n command = cc $in\n depfile = $out.d\nbuild foo.o: cc foo.c\n", ParseManifestOpts{}) 1171 edge := b.state.Edges[len(b.state.Edges)-1] 1172 1173 b.fs.Create("foo.c", "") 1174 b.GetNode("bar.h").Dirty = true // Mark bar.h as missing. 1175 b.fs.Create("foo.o.d", "foo.o: blah.h bar.h\n") 1176 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1177 t.Fatal("expected true") 1178 } 1179 if 1 != len(b.fs.filesRead) { 1180 t.Fatal("expected equal") 1181 } 1182 if "foo.o.d" != b.fs.filesRead[0] { 1183 t.Fatal("expected equal") 1184 } 1185 1186 // Expect three new edges: one generating foo.o, and two more from 1187 // loading the depfile. 1188 if origEdges+3 != len(b.state.Edges) { 1189 t.Fatal("expected equal") 1190 } 1191 // Expect our edge to now have three inputs: foo.c and two headers. 1192 if 3 != len(edge.Inputs) { 1193 t.Fatalf("%#v", edge.Inputs) 1194 } 1195 1196 // Expect the command line we generate to only use the original input. 1197 if "cc foo.c" != edge.EvaluateCommand(false) { 1198 t.Fatal("expected equal") 1199 } 1200 } 1201 1202 func TestBuildTest_DepFileParseError(t *testing.T) { 1203 b := NewBuildTest(t) 1204 b.AssertParse(&b.state, "rule cc\n command = cc $in\n depfile = $out.d\nbuild foo.o: cc foo.c\n", ParseManifestOpts{}) 1205 b.fs.Create("foo.c", "") 1206 b.fs.Create("foo.o.d", "randomtext\n") 1207 if _, err := b.builder.addTargetName("foo.o"); err == nil { 1208 t.Fatal("expected false") 1209 } else if err.Error() != "foo.o.d: expected ':' in depfile" { 1210 t.Fatal(err) 1211 } 1212 } 1213 1214 func TestBuildTest_EncounterReadyTwice(t *testing.T) { 1215 b := NewBuildTest(t) 1216 b.AssertParse(&b.state, "rule touch\n command = touch $out\nbuild c: touch\nbuild b: touch || c\nbuild a: touch | b || c\n", ParseManifestOpts{}) 1217 1218 cOut := b.GetNode("c").OutEdges 1219 if 2 != len(cOut) { 1220 t.Fatal("expected equal") 1221 } 1222 if "b" != cOut[0].Outputs[0].Path { 1223 t.Fatal("expected equal") 1224 } 1225 if "a" != cOut[1].Outputs[0].Path { 1226 t.Fatal("expected equal") 1227 } 1228 1229 b.fs.Create("b", "") 1230 if _, err := b.builder.addTargetName("a"); err != nil { 1231 t.Fatal("expected true") 1232 } 1233 1234 if err := b.builder.Build(); err != nil { 1235 t.Fatal(err) 1236 } 1237 if 2 != len(b.commandRunner.commandsRan) { 1238 t.Fatal("expected equal") 1239 } 1240 } 1241 1242 func TestBuildTest_OrderOnlyDeps(t *testing.T) { 1243 b := NewBuildTest(t) 1244 b.AssertParse(&b.state, "rule cc\n command = cc $in\n depfile = $out.d\nbuild foo.o: cc foo.c || otherfile\n", ParseManifestOpts{}) 1245 edge := b.state.Edges[len(b.state.Edges)-1] 1246 1247 b.fs.Create("foo.c", "") 1248 b.fs.Create("otherfile", "") 1249 b.fs.Create("foo.o.d", "foo.o: blah.h bar.h\n") 1250 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1251 t.Fatal(err) 1252 } 1253 1254 // One explicit, two implicit, one order only. 1255 if 4 != len(edge.Inputs) { 1256 t.Fatal("expected equal") 1257 } 1258 if 2 != edge.ImplicitDeps { 1259 t.Fatal("expected equal") 1260 } 1261 if 1 != edge.OrderOnlyDeps { 1262 t.Fatal("expected equal") 1263 } 1264 // Verify the inputs are in the order we expect 1265 // (explicit then implicit then orderonly). 1266 if "foo.c" != edge.Inputs[0].Path { 1267 t.Fatal("expected equal") 1268 } 1269 if "blah.h" != edge.Inputs[1].Path { 1270 t.Fatal("expected equal") 1271 } 1272 if "bar.h" != edge.Inputs[2].Path { 1273 t.Fatal("expected equal") 1274 } 1275 if "otherfile" != edge.Inputs[3].Path { 1276 t.Fatal("expected equal") 1277 } 1278 1279 // Expect the command line we generate to only use the original input. 1280 if "cc foo.c" != edge.EvaluateCommand(false) { 1281 t.Fatal("expected equal") 1282 } 1283 1284 // explicit dep dirty, expect a rebuild. 1285 if err := b.builder.Build(); err != nil { 1286 t.Fatal(err) 1287 } 1288 if 1 != len(b.commandRunner.commandsRan) { 1289 t.Fatal("expected equal") 1290 } 1291 1292 b.fs.Tick() 1293 1294 // Recreate the depfile, as it should have been deleted by the build. 1295 b.fs.Create("foo.o.d", "foo.o: blah.h bar.h\n") 1296 1297 // implicit dep dirty, expect a rebuild. 1298 b.fs.Create("blah.h", "") 1299 b.fs.Create("bar.h", "") 1300 b.commandRunner.commandsRan = nil 1301 b.state.Reset() 1302 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1303 t.Fatal(err) 1304 } 1305 if err := b.builder.Build(); err != nil { 1306 t.Fatal(err) 1307 } 1308 if 1 != len(b.commandRunner.commandsRan) { 1309 t.Fatal("expected equal") 1310 } 1311 1312 b.fs.Tick() 1313 1314 // Recreate the depfile, as it should have been deleted by the build. 1315 b.fs.Create("foo.o.d", "foo.o: blah.h bar.h\n") 1316 1317 // order only dep dirty, no rebuild. 1318 b.fs.Create("otherfile", "") 1319 b.commandRunner.commandsRan = nil 1320 b.state.Reset() 1321 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1322 t.Fatal(err) 1323 } 1324 if !b.builder.AlreadyUpToDate() { 1325 t.Fatal("expected true") 1326 } 1327 1328 // implicit dep missing, expect rebuild. 1329 b.fs.RemoveFile("bar.h") 1330 b.commandRunner.commandsRan = nil 1331 b.state.Reset() 1332 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1333 t.Fatal(err) 1334 } 1335 if err := b.builder.Build(); err != nil { 1336 t.Fatal(err) 1337 } 1338 if 1 != len(b.commandRunner.commandsRan) { 1339 t.Fatal("expected equal") 1340 } 1341 } 1342 1343 func TestBuildTest_RebuildOrderOnlyDeps(t *testing.T) { 1344 b := NewBuildTest(t) 1345 b.AssertParse(&b.state, "rule cc\n command = cc $in\nrule true\n command = true\nbuild oo.h: cc oo.h.in\nbuild foo.o: cc foo.c || oo.h\n", ParseManifestOpts{}) 1346 1347 b.fs.Create("foo.c", "") 1348 b.fs.Create("oo.h.in", "") 1349 1350 // foo.o and order-only dep dirty, build both. 1351 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1352 t.Fatal(err) 1353 } 1354 if err := b.builder.Build(); err != nil { 1355 t.Fatal(err) 1356 } 1357 if 2 != len(b.commandRunner.commandsRan) { 1358 t.Fatal("expected equal") 1359 } 1360 1361 // all clean, no rebuild. 1362 b.commandRunner.commandsRan = nil 1363 b.state.Reset() 1364 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1365 t.Fatal(err) 1366 } 1367 if !b.builder.AlreadyUpToDate() { 1368 t.Fatal("expected true") 1369 } 1370 1371 // order-only dep missing, build it only. 1372 b.fs.RemoveFile("oo.h") 1373 b.commandRunner.commandsRan = nil 1374 b.state.Reset() 1375 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1376 t.Fatal(err) 1377 } 1378 if err := b.builder.Build(); err != nil { 1379 t.Fatal(err) 1380 } 1381 wantCommands := []string{"cc oo.h.in"} 1382 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 1383 t.Fatal(diff) 1384 } 1385 1386 b.fs.Tick() 1387 1388 // order-only dep dirty, build it only. 1389 b.fs.Create("oo.h.in", "") 1390 b.commandRunner.commandsRan = nil 1391 b.state.Reset() 1392 if _, err := b.builder.addTargetName("foo.o"); err != nil { 1393 t.Fatal(err) 1394 } 1395 if err := b.builder.Build(); err != nil { 1396 t.Fatal(err) 1397 } 1398 wantCommands = []string{"cc oo.h.in"} 1399 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 1400 t.Fatal(diff) 1401 } 1402 } 1403 1404 func TestBuildTest_DepFileCanonicalize(t *testing.T) { 1405 if runtime.GOOS != "windows" { 1406 t.Skip("windows only") 1407 } 1408 t.Skip("TODO") 1409 b := NewBuildTest(t) 1410 origEdges := len(b.state.Edges) 1411 if origEdges != 3 { 1412 t.Fatal(origEdges) 1413 } 1414 b.AssertParse(&b.state, "rule cc\n command = cc $in\n depfile = $out.d\nbuild gen/stuff\\things/foo.o: cc x\\y/z\\foo.c\n", ParseManifestOpts{}) 1415 edge := b.state.Edges[len(b.state.Edges)-1] 1416 1417 b.fs.Create("x/y/z/foo.c", "") 1418 b.GetNode("bar.h").Dirty = true // Mark bar.h as missing. 1419 // Note, different slashes from manifest. 1420 b.fs.Create("gen/stuff\\things/foo.o.d", "gen\\stuff\\things\\foo.o: blah.h bar.h\n") 1421 if _, err := b.builder.addTargetName("gen/stuff/things/foo.o"); err != nil { 1422 t.Fatal(err) 1423 } 1424 // The depfile path does not get Canonicalize as it seems unnecessary. 1425 wantReads := []string{"gen/stuff\\things/foo.o.d"} 1426 if diff := cmp.Diff(wantReads, b.fs.filesRead); diff != "" { 1427 t.Fatal(diff) 1428 } 1429 1430 // Expect three new edges: one generating foo.o, and two more from 1431 // loading the depfile. 1432 if origEdges+3 != len(b.state.Edges) { 1433 t.Fatal(len(b.state.Edges)) 1434 } 1435 // Expect our edge to now have three inputs: foo.c and two headers. 1436 if 3 != len(edge.Inputs) { 1437 t.Fatal(len(edge.Inputs)) 1438 } 1439 1440 // Expect the command line we generate to only use the original input, and 1441 // using the slashes from the manifest. 1442 if got := edge.EvaluateCommand(false); got != "cc x\\y/z\\foo.c" { 1443 t.Fatalf("%q", got) 1444 } 1445 } 1446 1447 func TestBuildTest_Phony(t *testing.T) { 1448 b := NewBuildTest(t) 1449 b.AssertParse(&b.state, "build out: cat bar.cc\nbuild all: phony out\n", ParseManifestOpts{}) 1450 b.fs.Create("bar.cc", "") 1451 1452 if _, err := b.builder.addTargetName("all"); err != nil { 1453 t.Fatal(err) 1454 } 1455 1456 // Only one command to run, because phony runs no command. 1457 if b.builder.AlreadyUpToDate() { 1458 t.Fatal("expected false") 1459 } 1460 if err := b.builder.Build(); err != nil { 1461 t.Fatal(err) 1462 } 1463 if 1 != len(b.commandRunner.commandsRan) { 1464 t.Fatal("expected equal") 1465 } 1466 } 1467 1468 func TestBuildTest_PhonyNoWork(t *testing.T) { 1469 b := NewBuildTest(t) 1470 b.AssertParse(&b.state, "build out: cat bar.cc\nbuild all: phony out\n", ParseManifestOpts{}) 1471 b.fs.Create("bar.cc", "") 1472 b.fs.Create("out", "") 1473 1474 if _, err := b.builder.addTargetName("all"); err != nil { 1475 t.Fatal(err) 1476 } 1477 if !b.builder.AlreadyUpToDate() { 1478 t.Fatal("expected true") 1479 } 1480 } 1481 1482 // Test a self-referencing phony. Ideally this should not work, but 1483 // ninja 1.7 and below tolerated and CMake 2.8.12.x and 3.0.x both 1484 // incorrectly produce it. We tolerate it for compatibility. 1485 func TestBuildTest_PhonySelfReference(t *testing.T) { 1486 b := NewBuildTest(t) 1487 b.AssertParse(&b.state, "build a: phony a\n", ParseManifestOpts{Quiet: true}) 1488 1489 if _, err := b.builder.addTargetName("a"); err != nil { 1490 t.Fatal(err) 1491 } 1492 if !b.builder.AlreadyUpToDate() { 1493 t.Fatal("expected true") 1494 } 1495 } 1496 1497 // There are 6 different cases for phony rules: 1498 // 1499 // 1. output edge does not exist, inputs are not real 1500 // 2. output edge does not exist, no inputs 1501 // 3. output edge does not exist, inputs are real, newest mtime is M 1502 // 4. output edge is real, inputs are not real 1503 // 5. output edge is real, no inputs 1504 // 6. output edge is real, inputs are real, newest mtime is M 1505 // 1506 // Expected results : 1507 // 1. Edge is marked as clean, mtime is newest mtime of dependents. 1508 // Touching inputs will cause dependents to rebuild. 1509 // 2. Edge is marked as dirty, causing dependent edges to always rebuild 1510 // 3. Edge is marked as clean, mtime is newest mtime of dependents. 1511 // Touching inputs will cause dependents to rebuild. 1512 // 4. Edge is marked as clean, mtime is newest mtime of dependents. 1513 // Touching inputs will cause dependents to rebuild. 1514 // 5. Edge is marked as dirty, causing dependent edges to always rebuild 1515 // 6. Edge is marked as clean, mtime is newest mtime of dependents. 1516 // Touching inputs will cause dependents to rebuild. 1517 func PhonyUseCase(t *testing.T, i int) { 1518 b := NewBuildTest(t) 1519 b.AssertParse(&b.state, "rule touch\n command = touch $out\nbuild notreal: phony blank\nbuild phony1: phony notreal\nbuild phony2: phony\nbuild phony3: phony blank\nbuild phony4: phony notreal\nbuild phony5: phony\nbuild phony6: phony blank\n\nbuild test1: touch phony1\nbuild test2: touch phony2\nbuild test3: touch phony3\nbuild test4: touch phony4\nbuild test5: touch phony5\nbuild test6: touch phony6\n", ParseManifestOpts{}) 1520 1521 // Set up test. 1522 b.builder.commandRunner = nil // BuildTest owns the CommandRunner 1523 b.builder.commandRunner = &b.commandRunner 1524 1525 b.fs.Create("blank", "") // a "real" file 1526 if _, err := b.builder.addTargetName("test1"); err != nil { 1527 t.Fatal(err) 1528 } 1529 if _, err := b.builder.addTargetName("test2"); err != nil { 1530 t.Fatal(err) 1531 } 1532 if _, err := b.builder.addTargetName("test3"); err != nil { 1533 t.Fatal(err) 1534 } 1535 if _, err := b.builder.addTargetName("test4"); err != nil { 1536 t.Fatal(err) 1537 } 1538 if _, err := b.builder.addTargetName("test5"); err != nil { 1539 t.Fatal(err) 1540 } 1541 if _, err := b.builder.addTargetName("test6"); err != nil { 1542 t.Fatal(err) 1543 } 1544 if err := b.builder.Build(); err != nil { 1545 t.Fatal(err) 1546 } 1547 1548 ci := strconv.Itoa(i) 1549 1550 // Tests 1, 3, 4, and 6 should rebuild when the input is updated. 1551 if i != 2 && i != 5 { 1552 testNode := b.GetNode("test" + ci) 1553 phonyNode := b.GetNode("phony" + ci) 1554 inputNode := b.GetNode("blank") 1555 1556 b.state.Reset() 1557 startTime := b.fs.now 1558 1559 // Build number 1 1560 if _, err := b.builder.addTargetName("test" + ci); err != nil { 1561 t.Fatal(err) 1562 } 1563 if !b.builder.AlreadyUpToDate() { 1564 if err := b.builder.Build(); err != nil { 1565 t.Fatal(err) 1566 } 1567 } 1568 1569 // Touch the input file 1570 b.state.Reset() 1571 b.commandRunner.commandsRan = nil 1572 b.fs.Tick() 1573 b.fs.Create("blank", "") // a "real" file 1574 if _, err := b.builder.addTargetName("test" + ci); err != nil { 1575 t.Fatal(err) 1576 } 1577 1578 // Second build, expect testN edge to be rebuilt 1579 // and phonyN node's mtime to be updated. 1580 if b.builder.AlreadyUpToDate() { 1581 t.Fatal("expected false") 1582 } 1583 if err := b.builder.Build(); err != nil { 1584 t.Fatal(err) 1585 } 1586 wantCommands := []string{"touch test" + ci} 1587 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 1588 t.Fatal(diff) 1589 } 1590 if !b.builder.AlreadyUpToDate() { 1591 t.Fatal("expected true") 1592 } 1593 1594 inputTime := inputNode.MTime 1595 1596 if phonyNode.Exists == ExistenceStatusExists { 1597 t.Fatal("expected false") 1598 } 1599 if phonyNode.Dirty { 1600 t.Fatal("expected false") 1601 } 1602 1603 if phonyNode.MTime <= startTime { 1604 t.Fatal("expected greater") 1605 } 1606 if phonyNode.MTime != inputTime { 1607 t.Fatal("expected equal") 1608 } 1609 if err := testNode.Stat(&b.fs); err != nil { 1610 t.Fatal(err) 1611 } 1612 if testNode.Exists != ExistenceStatusExists { 1613 t.Fatal("expected true") 1614 } 1615 if testNode.MTime <= startTime { 1616 t.Fatal("expected greater") 1617 } 1618 } else { 1619 // Tests 2 and 5: Expect dependents to always rebuild. 1620 1621 b.state.Reset() 1622 b.commandRunner.commandsRan = nil 1623 b.fs.Tick() 1624 b.commandRunner.commandsRan = nil 1625 if _, err := b.builder.addTargetName("test" + ci); err != nil { 1626 t.Fatal(err) 1627 } 1628 if b.builder.AlreadyUpToDate() { 1629 t.Fatal("expected false") 1630 } 1631 if err := b.builder.Build(); err != nil { 1632 t.Fatal(err) 1633 } 1634 wantCommands := []string{"touch test" + ci} 1635 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 1636 t.Fatal(diff) 1637 } 1638 1639 b.state.Reset() 1640 b.commandRunner.commandsRan = nil 1641 if _, err := b.builder.addTargetName("test" + ci); err != nil { 1642 t.Fatal(err) 1643 } 1644 if b.builder.AlreadyUpToDate() { 1645 t.Fatal("expected false") 1646 } 1647 if err := b.builder.Build(); err != nil { 1648 t.Fatal(err) 1649 } 1650 wantCommands = []string{"touch test" + ci} 1651 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 1652 t.Fatal(diff) 1653 } 1654 } 1655 } 1656 1657 func TestBuildTest_PhonyUseCase(t *testing.T) { 1658 for i := 1; i < 7; i++ { 1659 t.Run(strconv.Itoa(i), func(t *testing.T) { PhonyUseCase(t, i) }) 1660 } 1661 } 1662 1663 func TestBuildTest_Fail(t *testing.T) { 1664 b := NewBuildTest(t) 1665 b.AssertParse(&b.state, "rule fail\n command = fail\nbuild out1: fail\n", ParseManifestOpts{}) 1666 1667 if _, err := b.builder.addTargetName("out1"); err != nil { 1668 t.Fatal(err) 1669 } 1670 1671 if err := b.builder.Build(); err == nil { 1672 t.Fatal("expected false") 1673 } else if err.Error() != "subcommand failed" { 1674 t.Fatal(err) 1675 } 1676 if 1 != len(b.commandRunner.commandsRan) { 1677 t.Fatal("expected equal") 1678 } 1679 } 1680 1681 func TestBuildTest_SwallowFailures(t *testing.T) { 1682 b := NewBuildTest(t) 1683 b.AssertParse(&b.state, "rule fail\n command = fail\nbuild out1: fail\nbuild out2: fail\nbuild out3: fail\nbuild all: phony out1 out2 out3\n", ParseManifestOpts{}) 1684 1685 // Swallow two failures, die on the third. 1686 b.config.FailuresAllowed = 3 1687 1688 if _, err := b.builder.addTargetName("all"); err != nil { 1689 t.Fatal(err) 1690 } 1691 1692 err := b.builder.Build() 1693 if err == nil { 1694 t.Fatal("expected error") 1695 } else if err.Error() != "subcommands failed" { 1696 t.Fatal(err) 1697 } 1698 if 3 != len(b.commandRunner.commandsRan) { 1699 t.Fatal("expected equal") 1700 } 1701 } 1702 1703 func TestBuildTest_SwallowFailuresLimit(t *testing.T) { 1704 b := NewBuildTest(t) 1705 b.AssertParse(&b.state, "rule fail\n command = fail\nbuild out1: fail\nbuild out2: fail\nbuild out3: fail\nbuild final: cat out1 out2 out3\n", ParseManifestOpts{}) 1706 1707 // Swallow ten failures; we should stop before building final. 1708 b.config.FailuresAllowed = 11 1709 1710 if _, err := b.builder.addTargetName("final"); err != nil { 1711 t.Fatal(err) 1712 } 1713 1714 if err := b.builder.Build(); err == nil { 1715 t.Fatal("expected false") 1716 } else if err.Error() != "cannot make progress due to previous errors" { 1717 t.Fatal(err) 1718 } 1719 if 3 != len(b.commandRunner.commandsRan) { 1720 t.Fatal("expected equal") 1721 } 1722 } 1723 1724 func TestBuildTest_SwallowFailuresPool(t *testing.T) { 1725 b := NewBuildTest(t) 1726 b.AssertParse(&b.state, "pool failpool\n depth = 1\nrule fail\n command = fail\n pool = failpool\nbuild out1: fail\nbuild out2: fail\nbuild out3: fail\nbuild final: cat out1 out2 out3\n", ParseManifestOpts{}) 1727 1728 // Swallow ten failures; we should stop before building final. 1729 b.config.FailuresAllowed = 11 1730 1731 if _, err := b.builder.addTargetName("final"); err != nil { 1732 t.Fatal(err) 1733 } 1734 1735 if err := b.builder.Build(); err == nil { 1736 t.Fatal("expected false") 1737 } else if err.Error() != "cannot make progress due to previous errors" { 1738 t.Fatal(err) 1739 } 1740 if 3 != len(b.commandRunner.commandsRan) { 1741 t.Fatal("expected equal") 1742 } 1743 } 1744 1745 func TestBuildTest_PoolEdgesReadyButNotWanted(t *testing.T) { 1746 b := NewBuildTest(t) 1747 b.fs.Create("x", "") 1748 1749 manifest := "pool some_pool\n depth = 4\nrule touch\n command = touch $out\n pool = some_pool\nrule cc\n command = touch grit\n\nbuild B.d.stamp: cc | x\nbuild C.stamp: touch B.d.stamp\nbuild final.stamp: touch || C.stamp\n" 1750 1751 b.RebuildTarget("final.stamp", manifest, "", "", nil) 1752 1753 b.fs.RemoveFile("B.d.stamp") 1754 1755 saveState := NewState() 1756 b.RebuildTarget("final.stamp", manifest, "", "", &saveState) 1757 if saveState.Pools["some_pool"].currentUse < 0 { 1758 t.Fatal("expected greater or equal") 1759 } 1760 } 1761 1762 type BuildWithLogTest struct { 1763 *BuildTest 1764 buildLog BuildLog 1765 } 1766 1767 func NewBuildWithLogTest(t *testing.T) *BuildWithLogTest { 1768 b := &BuildWithLogTest{ 1769 BuildTest: NewBuildTest(t), 1770 buildLog: NewBuildLog(), 1771 } 1772 t.Cleanup(func() { 1773 b.buildLog.Close() 1774 }) 1775 b.builder.scan.buildLog = &b.buildLog 1776 return b 1777 } 1778 1779 func TestBuildWithLogTest_ImplicitGeneratedOutOfDate(t *testing.T) { 1780 b := NewBuildWithLogTest(t) 1781 b.AssertParse(&b.state, "rule touch\n command = touch $out\n generator = 1\nbuild out.imp: touch | in\n", ParseManifestOpts{}) 1782 b.fs.Create("out.imp", "") 1783 b.fs.Tick() 1784 b.fs.Create("in", "") 1785 1786 if _, err := b.builder.addTargetName("out.imp"); err != nil { 1787 t.Fatal(err) 1788 } 1789 if b.builder.AlreadyUpToDate() { 1790 t.Fatal("expected false") 1791 } 1792 1793 if !b.GetNode("out.imp").Dirty { 1794 t.Fatal("expected true") 1795 } 1796 } 1797 1798 func TestBuildWithLogTest_ImplicitGeneratedOutOfDate2(t *testing.T) { 1799 b := NewBuildWithLogTest(t) 1800 b.AssertParse(&b.state, "rule touch-implicit-dep-out\n command = touch $test_dependency ; sleep 1 ; touch $out\n generator = 1\nbuild out.imp: touch-implicit-dep-out | inimp inimp2\n test_dependency = inimp\n", ParseManifestOpts{}) 1801 b.fs.Create("inimp", "") 1802 b.fs.Create("out.imp", "") 1803 b.fs.Tick() 1804 b.fs.Create("inimp2", "") 1805 b.fs.Tick() 1806 1807 if _, err := b.builder.addTargetName("out.imp"); err != nil { 1808 t.Fatal(err) 1809 } 1810 if b.builder.AlreadyUpToDate() { 1811 t.Fatal("expected false") 1812 } 1813 1814 if err := b.builder.Build(); err != nil { 1815 t.Fatal(err) 1816 } 1817 if !b.builder.AlreadyUpToDate() { 1818 t.Fatal("expected true") 1819 } 1820 1821 b.commandRunner.commandsRan = nil 1822 b.state.Reset() 1823 b.builder.cleanup() 1824 b.builder.plan.Reset() 1825 1826 if _, err := b.builder.addTargetName("out.imp"); err != nil { 1827 t.Fatal(err) 1828 } 1829 if !b.builder.AlreadyUpToDate() { 1830 t.Fatal("expected true") 1831 } 1832 if b.GetNode("out.imp").Dirty { 1833 t.Fatal("expected false") 1834 } 1835 } 1836 1837 func TestBuildWithLogTest_NotInLogButOnDisk(t *testing.T) { 1838 b := NewBuildWithLogTest(t) 1839 b.AssertParse(&b.state, "rule cc\n command = cc\nbuild out1: cc in\n", ParseManifestOpts{}) 1840 1841 // Create input/output that would be considered up to date when 1842 // not considering the command line hash. 1843 b.fs.Create("in", "") 1844 b.fs.Create("out1", "") 1845 1846 // Because it's not in the log, it should not be up-to-date until 1847 // we build again. 1848 if _, err := b.builder.addTargetName("out1"); err != nil { 1849 t.Fatal(err) 1850 } 1851 if b.builder.AlreadyUpToDate() { 1852 t.Fatal("expected false") 1853 } 1854 1855 b.commandRunner.commandsRan = nil 1856 b.state.Reset() 1857 1858 if _, err := b.builder.addTargetName("out1"); err != nil { 1859 t.Fatal(err) 1860 } 1861 if err := b.builder.Build(); err != nil { 1862 t.Fatal(err) 1863 } 1864 if !b.builder.AlreadyUpToDate() { 1865 t.Fatal("expected true") 1866 } 1867 } 1868 1869 func TestBuildWithLogTest_RebuildAfterFailure(t *testing.T) { 1870 b := NewBuildWithLogTest(t) 1871 b.AssertParse(&b.state, "rule touch-fail-tick2\n command = touch-fail-tick2\nbuild out1: touch-fail-tick2 in\n", ParseManifestOpts{}) 1872 1873 b.fs.Create("in", "") 1874 1875 // Run once successfully to get out1 in the log 1876 if _, err := b.builder.addTargetName("out1"); err != nil { 1877 t.Fatal(err) 1878 } 1879 if err := b.builder.Build(); err != nil { 1880 t.Fatal(err) 1881 } 1882 if 1 != len(b.commandRunner.commandsRan) { 1883 t.Fatal("expected equal") 1884 } 1885 1886 b.commandRunner.commandsRan = nil 1887 b.state.Reset() 1888 b.builder.cleanup() 1889 b.builder.plan.Reset() 1890 1891 b.fs.Tick() 1892 b.fs.Create("in", "") 1893 1894 // Run again with a failure that updates the output file timestamp 1895 if _, err := b.builder.addTargetName("out1"); err != nil { 1896 t.Fatal(err) 1897 } 1898 if err := b.builder.Build(); err == nil { 1899 t.Fatal("expected false") 1900 } else if err.Error() != "subcommand failed" { 1901 t.Fatal(err) 1902 } 1903 if 1 != len(b.commandRunner.commandsRan) { 1904 t.Fatal("expected equal") 1905 } 1906 1907 b.commandRunner.commandsRan = nil 1908 b.state.Reset() 1909 b.builder.cleanup() 1910 b.builder.plan.Reset() 1911 1912 b.fs.Tick() 1913 1914 // Run again, should rerun even though the output file is up to date on disk 1915 if _, err := b.builder.addTargetName("out1"); err != nil { 1916 t.Fatal(err) 1917 } 1918 if b.builder.AlreadyUpToDate() { 1919 t.Fatal("expected false") 1920 } 1921 if err := b.builder.Build(); err != nil { 1922 t.Fatal(err) 1923 } 1924 if 1 != len(b.commandRunner.commandsRan) { 1925 t.Fatal("expected equal") 1926 } 1927 } 1928 1929 func TestBuildWithLogTest_RebuildWithNoInputs(t *testing.T) { 1930 b := NewBuildWithLogTest(t) 1931 b.AssertParse(&b.state, "rule touch\n command = touch\nbuild out1: touch\nbuild out2: touch in\n", ParseManifestOpts{}) 1932 1933 b.fs.Create("in", "") 1934 1935 if _, err := b.builder.addTargetName("out1"); err != nil { 1936 t.Fatal(err) 1937 } 1938 if _, err := b.builder.addTargetName("out2"); err != nil { 1939 t.Fatal(err) 1940 } 1941 if err := b.builder.Build(); err != nil { 1942 t.Fatal(err) 1943 } 1944 if 2 != len(b.commandRunner.commandsRan) { 1945 t.Fatal("expected equal") 1946 } 1947 1948 b.commandRunner.commandsRan = nil 1949 b.state.Reset() 1950 1951 b.fs.Tick() 1952 1953 b.fs.Create("in", "") 1954 1955 if _, err := b.builder.addTargetName("out1"); err != nil { 1956 t.Fatal(err) 1957 } 1958 if _, err := b.builder.addTargetName("out2"); err != nil { 1959 t.Fatal(err) 1960 } 1961 if err := b.builder.Build(); err != nil { 1962 t.Fatal(err) 1963 } 1964 if 1 != len(b.commandRunner.commandsRan) { 1965 t.Fatal("expected equal") 1966 } 1967 } 1968 1969 func TestBuildWithLogTest_RestatTest(t *testing.T) { 1970 b := NewBuildWithLogTest(t) 1971 b.AssertParse(&b.state, "rule true\n command = true\n restat = 1\nrule cc\n command = cc\n restat = 1\nbuild out1: cc in\nbuild out2: true out1\nbuild out3: cat out2\n", ParseManifestOpts{}) 1972 1973 b.fs.Create("out1", "") 1974 b.fs.Create("out2", "") 1975 b.fs.Create("out3", "") 1976 1977 b.fs.Tick() 1978 1979 b.fs.Create("in", "") 1980 1981 // Do a pre-build so that there's commands in the log for the outputs, 1982 // otherwise, the lack of an entry in the build log will cause out3 to rebuild 1983 // regardless of restat. 1984 if _, err := b.builder.addTargetName("out3"); err != nil { 1985 t.Fatal(err) 1986 } 1987 if err := b.builder.Build(); err != nil { 1988 t.Fatal(err) 1989 } 1990 if 3 != len(b.commandRunner.commandsRan) { 1991 t.Fatal("expected equal") 1992 } 1993 if 3 != b.builder.plan.commandEdges { 1994 t.Fatal("expected equal") 1995 } 1996 b.commandRunner.commandsRan = nil 1997 b.state.Reset() 1998 1999 b.fs.Tick() 2000 2001 b.fs.Create("in", "") 2002 // "cc" touches out1, so we should build out2. But because "true" does not 2003 // touch out2, we should cancel the build of out3. 2004 if _, err := b.builder.addTargetName("out3"); err != nil { 2005 t.Fatal(err) 2006 } 2007 if err := b.builder.Build(); err != nil { 2008 t.Fatal(err) 2009 } 2010 if 2 != len(b.commandRunner.commandsRan) { 2011 t.Fatal("expected equal") 2012 } 2013 2014 // If we run again, it should be a no-op, because the build log has recorded 2015 // that we've already built out2 with an input timestamp of 2 (from out1). 2016 b.commandRunner.commandsRan = nil 2017 b.state.Reset() 2018 if _, err := b.builder.addTargetName("out3"); err != nil { 2019 t.Fatal(err) 2020 } 2021 if !b.builder.AlreadyUpToDate() { 2022 t.Fatal("expected true") 2023 } 2024 2025 b.fs.Tick() 2026 2027 b.fs.Create("in", "") 2028 2029 // The build log entry should not, however, prevent us from rebuilding out2 2030 // if out1 changes. 2031 b.commandRunner.commandsRan = nil 2032 b.state.Reset() 2033 if _, err := b.builder.addTargetName("out3"); err != nil { 2034 t.Fatal(err) 2035 } 2036 if err := b.builder.Build(); err != nil { 2037 t.Fatal(err) 2038 } 2039 if 2 != len(b.commandRunner.commandsRan) { 2040 t.Fatal("expected equal") 2041 } 2042 } 2043 2044 func TestBuildWithLogTest_RestatMissingFile(t *testing.T) { 2045 b := NewBuildWithLogTest(t) 2046 // If a restat rule doesn't create its output, and the output didn't 2047 // exist before the rule was run, consider that behavior equivalent 2048 // to a rule that doesn't modify its existent output file. 2049 2050 b.AssertParse(&b.state, "rule true\n command = true\n restat = 1\nrule cc\n command = cc\nbuild out1: true in\nbuild out2: cc out1\n", ParseManifestOpts{}) 2051 2052 b.fs.Create("in", "") 2053 b.fs.Create("out2", "") 2054 2055 // Do a pre-build so that there's commands in the log for the outputs, 2056 // otherwise, the lack of an entry in the build log will cause out2 to rebuild 2057 // regardless of restat. 2058 if _, err := b.builder.addTargetName("out2"); err != nil { 2059 t.Fatal(err) 2060 } 2061 if err := b.builder.Build(); err != nil { 2062 t.Fatal(err) 2063 } 2064 b.commandRunner.commandsRan = nil 2065 b.state.Reset() 2066 2067 b.fs.Tick() 2068 b.fs.Create("in", "") 2069 b.fs.Create("out2", "") 2070 2071 // Run a build, expect only the first command to run. 2072 // It doesn't touch its output (due to being the "true" command), so 2073 // we shouldn't run the dependent build. 2074 if _, err := b.builder.addTargetName("out2"); err != nil { 2075 t.Fatal(err) 2076 } 2077 if err := b.builder.Build(); err != nil { 2078 t.Fatal(err) 2079 } 2080 if 1 != len(b.commandRunner.commandsRan) { 2081 t.Fatal("expected equal") 2082 } 2083 } 2084 2085 func TestBuildWithLogTest_RestatSingleDependentOutputDirty(t *testing.T) { 2086 b := NewBuildWithLogTest(t) 2087 b.AssertParse(&b.state, "rule true\n command = true\n restat = 1\nrule touch\n command = touch\nbuild out1: true in\nbuild out2 out3: touch out1\nbuild out4: touch out2\n", ParseManifestOpts{}) 2088 2089 // Create the necessary files 2090 b.fs.Create("in", "") 2091 2092 if _, err := b.builder.addTargetName("out4"); err != nil { 2093 t.Fatal(err) 2094 } 2095 if err := b.builder.Build(); err != nil { 2096 t.Fatal(err) 2097 } 2098 if 3 != len(b.commandRunner.commandsRan) { 2099 t.Fatal("expected equal") 2100 } 2101 2102 b.fs.Tick() 2103 b.fs.Create("in", "") 2104 b.fs.RemoveFile("out3") 2105 2106 // Since "in" is missing, out1 will be built. Since "out3" is missing, 2107 // out2 and out3 will be built even though "in" is not touched when built. 2108 // Then, since out2 is rebuilt, out4 should be rebuilt -- the restat on the 2109 // "true" rule should not lead to the "touch" edge writing out2 and out3 being 2110 // cleard. 2111 b.commandRunner.commandsRan = nil 2112 b.state.Reset() 2113 if _, err := b.builder.addTargetName("out4"); err != nil { 2114 t.Fatal(err) 2115 } 2116 if err := b.builder.Build(); err != nil { 2117 t.Fatal(err) 2118 } 2119 if 3 != len(b.commandRunner.commandsRan) { 2120 t.Fatal("expected equal") 2121 } 2122 } 2123 2124 // Test scenario, in which an input file is removed, but output isn't changed 2125 // https://github.com/ninja-build/ninja/issues/295 2126 func TestBuildWithLogTest_RestatMissingInput(t *testing.T) { 2127 b := NewBuildWithLogTest(t) 2128 b.AssertParse(&b.state, "rule true\n command = true\n depfile = $out.d\n restat = 1\nrule cc\n command = cc\nbuild out1: true in\nbuild out2: cc out1\n", ParseManifestOpts{}) 2129 2130 // Create all necessary files 2131 b.fs.Create("in", "") 2132 2133 // The implicit dependencies and the depfile itself 2134 // are newer than the output 2135 restatMtime := b.fs.Tick() 2136 b.fs.Create("out1.d", "out1: will.be.deleted restat.file\n") 2137 b.fs.Create("will.be.deleted", "") 2138 b.fs.Create("restat.file", "") 2139 2140 // Run the build, out1 and out2 get built 2141 if _, err := b.builder.addTargetName("out2"); err != nil { 2142 t.Fatal(err) 2143 } 2144 if err := b.builder.Build(); err != nil { 2145 t.Fatal(err) 2146 } 2147 if 2 != len(b.commandRunner.commandsRan) { 2148 t.Fatal("expected equal") 2149 } 2150 2151 // See that an entry in the logfile is created, capturing 2152 // the right mtime 2153 logEntry := b.buildLog.Entries["out1"] 2154 if nil == logEntry { 2155 t.Fatal("expected true") 2156 } 2157 if restatMtime != logEntry.mtime { 2158 t.Fatal("expected equal") 2159 } 2160 2161 // Now remove a file, referenced from depfile, so that target becomes 2162 // dirty, but the output does not change 2163 b.fs.RemoveFile("will.be.deleted") 2164 2165 // Trigger the build again - only out1 gets built 2166 b.commandRunner.commandsRan = nil 2167 b.state.Reset() 2168 if _, err := b.builder.addTargetName("out2"); err != nil { 2169 t.Fatal(err) 2170 } 2171 if err := b.builder.Build(); err != nil { 2172 t.Fatal(err) 2173 } 2174 if 1 != len(b.commandRunner.commandsRan) { 2175 t.Fatal("expected equal") 2176 } 2177 2178 // Check that the logfile entry remains correctly set 2179 logEntry = b.buildLog.Entries["out1"] 2180 if nil == logEntry { 2181 t.Fatal("expected true") 2182 } 2183 if restatMtime != logEntry.mtime { 2184 t.Fatal("expected equal") 2185 } 2186 } 2187 2188 func TestBuildWithLogTest_GeneratedPlainDepfileMtime(t *testing.T) { 2189 b := NewBuildWithLogTest(t) 2190 b.AssertParse(&b.state, "rule generate-depfile\n command = touch $out ; echo \"$out: $test_dependency\" > $depfile\nbuild out: generate-depfile\n test_dependency = inimp\n depfile = out.d\n", ParseManifestOpts{}) 2191 b.fs.Create("inimp", "") 2192 b.fs.Tick() 2193 2194 if _, err := b.builder.addTargetName("out"); err != nil { 2195 t.Fatal(err) 2196 } 2197 if b.builder.AlreadyUpToDate() { 2198 t.Fatal("expected false") 2199 } 2200 2201 if err := b.builder.Build(); err != nil { 2202 t.Fatal(err) 2203 } 2204 if !b.builder.AlreadyUpToDate() { 2205 t.Fatal("expected true") 2206 } 2207 2208 b.commandRunner.commandsRan = nil 2209 b.state.Reset() 2210 b.builder.cleanup() 2211 b.builder.plan.Reset() 2212 2213 if _, err := b.builder.addTargetName("out"); err != nil { 2214 t.Fatal(err) 2215 } 2216 if !b.builder.AlreadyUpToDate() { 2217 t.Fatal("expected true") 2218 } 2219 } 2220 2221 func NewBuildDryRunTest(t *testing.T) *BuildWithLogTest { 2222 b := NewBuildWithLogTest(t) 2223 b.config.DryRun = true 2224 return b 2225 } 2226 2227 func TestBuildDryRun_AllCommandsShown(t *testing.T) { 2228 b := NewBuildDryRunTest(t) 2229 b.AssertParse(&b.state, "rule true\n command = true\n restat = 1\nrule cc\n command = cc\n restat = 1\nbuild out1: cc in\nbuild out2: true out1\nbuild out3: cat out2\n", ParseManifestOpts{}) 2230 2231 b.fs.Create("out1", "") 2232 b.fs.Create("out2", "") 2233 b.fs.Create("out3", "") 2234 2235 b.fs.Tick() 2236 2237 b.fs.Create("in", "") 2238 2239 // "cc" touches out1, so we should build out2. But because "true" does not 2240 // touch out2, we should cancel the build of out3. 2241 if _, err := b.builder.addTargetName("out3"); err != nil { 2242 t.Fatal(err) 2243 } 2244 if err := b.builder.Build(); err != nil { 2245 t.Fatal(err) 2246 } 2247 if 3 != len(b.commandRunner.commandsRan) { 2248 t.Fatal("expected equal") 2249 } 2250 } 2251 2252 // Test that RSP files are created when & where appropriate and deleted after 2253 // successful execution. 2254 func TestBuildTest_RspFileSuccess(t *testing.T) { 2255 b := NewBuildTest(t) 2256 b.AssertParse(&b.state, "rule cat_rsp\n command = cat $rspfile > $out\n rspfile = $rspfile\n rspfile_content = $long_command\nrule cat_rsp_out\n command = cat $rspfile > $out\n rspfile = $out.rsp\n rspfile_content = $long_command\nbuild out1: cat in\nbuild out2: cat_rsp in\n rspfile = out 2.rsp\n long_command = Some very long command\nbuild out$ 3: cat_rsp_out in\n long_command = Some very long command\n", ParseManifestOpts{}) 2257 2258 b.fs.Create("out1", "") 2259 b.fs.Create("out2", "") 2260 b.fs.Create("out 3", "") 2261 2262 b.fs.Tick() 2263 2264 b.fs.Create("in", "") 2265 2266 if _, err := b.builder.addTargetName("out1"); err != nil { 2267 t.Fatal(err) 2268 } 2269 if _, err := b.builder.addTargetName("out2"); err != nil { 2270 t.Fatal(err) 2271 } 2272 if _, err := b.builder.addTargetName("out 3"); err != nil { 2273 t.Fatal(err) 2274 } 2275 2276 wantCreated := map[string]struct{}{ 2277 "in": {}, 2278 "in1": {}, 2279 "in2": {}, 2280 "out 3": {}, 2281 "out1": {}, 2282 "out2": {}, 2283 } 2284 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 2285 t.Fatal(diff) 2286 } 2287 wantRemoved := map[string]struct{}{} 2288 if diff := cmp.Diff(wantRemoved, b.fs.filesRemoved); diff != "" { 2289 t.Fatal(diff) 2290 } 2291 2292 if err := b.builder.Build(); err != nil { 2293 t.Fatal(err) 2294 } 2295 if 3 != len(b.commandRunner.commandsRan) { 2296 t.Fatal(b.commandRunner.commandsRan) 2297 } 2298 2299 // The RSP files were created 2300 wantCreated["out 2.rsp"] = struct{}{} 2301 wantCreated["out 3.rsp"] = struct{}{} 2302 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 2303 t.Fatal(diff) 2304 } 2305 2306 // The RSP files were removed 2307 wantRemoved["out 2.rsp"] = struct{}{} 2308 wantRemoved["out 3.rsp"] = struct{}{} 2309 if diff := cmp.Diff(wantRemoved, b.fs.filesRemoved); diff != "" { 2310 t.Fatal(diff) 2311 } 2312 } 2313 2314 // Test that RSP file is created but not removed for commands, which fail 2315 func TestBuildTest_RspFileFailure(t *testing.T) { 2316 b := NewBuildTest(t) 2317 b.AssertParse(&b.state, "rule fail\n command = fail\n rspfile = $rspfile\n rspfile_content = $long_command\nbuild out: fail in\n rspfile = out.rsp\n long_command = Another very long command\n", ParseManifestOpts{}) 2318 2319 b.fs.Create("out", "") 2320 b.fs.Tick() 2321 b.fs.Create("in", "") 2322 2323 if _, err := b.builder.addTargetName("out"); err != nil { 2324 t.Fatal(err) 2325 } 2326 2327 wantCreated := map[string]struct{}{ 2328 "in": {}, 2329 "in1": {}, 2330 "in2": {}, 2331 "out": {}, 2332 } 2333 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 2334 t.Fatal(diff) 2335 } 2336 wantRemoved := map[string]struct{}{} 2337 if diff := cmp.Diff(wantRemoved, b.fs.filesRemoved); diff != "" { 2338 t.Fatal(diff) 2339 } 2340 2341 if err := b.builder.Build(); err == nil { 2342 t.Fatal("expected false") 2343 } else if err.Error() != "subcommand failed" { 2344 t.Fatal(err) 2345 } 2346 wantCommand := []string{"fail"} 2347 if diff := cmp.Diff(wantCommand, b.commandRunner.commandsRan); diff != "" { 2348 t.Fatal(diff) 2349 } 2350 2351 // The RSP file was created 2352 wantCreated["out.rsp"] = struct{}{} 2353 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 2354 t.Fatal(diff) 2355 } 2356 2357 // The RSP file was NOT removed 2358 if diff := cmp.Diff(wantRemoved, b.fs.filesRemoved); diff != "" { 2359 t.Fatal(diff) 2360 } 2361 2362 // The RSP file contains what it should 2363 if c, err := b.fs.ReadFile("out.rsp"); err != nil || string(c) != "Another very long command\x00" { 2364 t.Fatal(c, err) 2365 } 2366 } 2367 2368 // Test that contents of the RSP file behaves like a regular part of 2369 // command line, i.e. triggers a rebuild if changed 2370 func TestBuildWithLogTest_RspFileCmdLineChange(t *testing.T) { 2371 b := NewBuildWithLogTest(t) 2372 b.AssertParse(&b.state, "rule cat_rsp\n command = cat $rspfile > $out\n rspfile = $rspfile\n rspfile_content = $long_command\nbuild out: cat_rsp in\n rspfile = out.rsp\n long_command = Original very long command\n", ParseManifestOpts{}) 2373 2374 b.fs.Create("out", "") 2375 b.fs.Tick() 2376 b.fs.Create("in", "") 2377 2378 if _, err := b.builder.addTargetName("out"); err != nil { 2379 t.Fatal(err) 2380 } 2381 2382 // 1. Build for the 1st time (-> populate log) 2383 if err := b.builder.Build(); err != nil { 2384 t.Fatal(err) 2385 } 2386 wantCommand := []string{"cat out.rsp > out"} 2387 if diff := cmp.Diff(wantCommand, b.commandRunner.commandsRan); diff != "" { 2388 t.Fatal(diff) 2389 } 2390 2391 // 2. Build again (no change) 2392 b.commandRunner.commandsRan = nil 2393 b.state.Reset() 2394 if _, err := b.builder.addTargetName("out"); err != nil { 2395 t.Fatal(err) 2396 } 2397 if !b.builder.AlreadyUpToDate() { 2398 t.Fatal("expected true") 2399 } 2400 2401 // 3. Alter the entry in the logfile 2402 // (to simulate a change in the command line between 2 builds) 2403 logEntry := b.buildLog.Entries["out"] 2404 if nil == logEntry { 2405 t.Fatal("expected true") 2406 } 2407 b.AssertHash("cat out.rsp > out;rspfile=Original very long command", logEntry.commandHash) 2408 logEntry.commandHash++ // Change the command hash to something else. 2409 // Now expect the target to be rebuilt 2410 b.commandRunner.commandsRan = nil 2411 b.state.Reset() 2412 if _, err := b.builder.addTargetName("out"); err != nil { 2413 t.Fatal(err) 2414 } 2415 if err := b.builder.Build(); err != nil { 2416 t.Fatal(err) 2417 } 2418 if diff := cmp.Diff(wantCommand, b.commandRunner.commandsRan); diff != "" { 2419 t.Fatal(diff) 2420 } 2421 } 2422 2423 func TestBuildTest_InterruptCleanup(t *testing.T) { 2424 b := NewBuildTest(t) 2425 b.AssertParse(&b.state, "rule interrupt\n command = interrupt\nrule touch-interrupt\n command = touch-interrupt\nbuild out1: interrupt in1\nbuild out2: touch-interrupt in2\n", ParseManifestOpts{}) 2426 2427 b.fs.Create("out1", "") 2428 b.fs.Create("out2", "") 2429 b.fs.Tick() 2430 b.fs.Create("in1", "") 2431 b.fs.Create("in2", "") 2432 2433 // An untouched output of an interrupted command should be retained. 2434 if _, err := b.builder.addTargetName("out1"); err != nil { 2435 t.Fatal(err) 2436 } 2437 if err := b.builder.Build(); err == nil { 2438 t.Fatal("expected false") 2439 } else if err.Error() != "interrupted by user" { 2440 t.Fatal(err) 2441 } 2442 b.builder.cleanup() 2443 if mtime, err := b.fs.Stat("out1"); mtime <= 0 || err != nil { 2444 t.Fatal(mtime, err) 2445 } 2446 2447 // A touched output of an interrupted command should be deleted. 2448 if _, err := b.builder.addTargetName("out2"); err != nil { 2449 t.Fatal(err) 2450 } 2451 if err := b.builder.Build(); err == nil { 2452 t.Fatal("expected false") 2453 } else if err.Error() != "interrupted by user" { 2454 t.Fatal(err) 2455 } 2456 b.builder.cleanup() 2457 if mtime, err := b.fs.Stat("out2"); mtime != 0 || err != nil { 2458 t.Fatal(mtime, err) 2459 } 2460 } 2461 2462 func TestBuildTest_StatFailureAbortsBuild(t *testing.T) { 2463 b := NewBuildTest(t) 2464 tooLongToStat := strings.Repeat("i", 400) 2465 b.AssertParse(&b.state, ("build " + tooLongToStat + ": cat in\n"), ParseManifestOpts{}) 2466 b.fs.Create("in", "") 2467 2468 // This simulates a stat failure: 2469 b.fs.files[tooLongToStat] = Entry{ 2470 mtime: -1, 2471 statError: errors.New("stat failed"), 2472 } 2473 2474 if _, err := b.builder.addTargetName(tooLongToStat); err == nil { 2475 t.Fatal("expected false") 2476 } else if err.Error() != "stat failed" { 2477 t.Fatal(err) 2478 } 2479 } 2480 2481 func TestBuildTest_PhonyWithNoInputs(t *testing.T) { 2482 b := NewBuildTest(t) 2483 b.AssertParse(&b.state, "build nonexistent: phony\nbuild out1: cat || nonexistent\nbuild out2: cat nonexistent\n", ParseManifestOpts{}) 2484 b.fs.Create("out1", "") 2485 b.fs.Create("out2", "") 2486 2487 // out1 should be up to date even though its input is dirty, because its 2488 // order-only dependency has nothing to do. 2489 if _, err := b.builder.addTargetName("out1"); err != nil { 2490 t.Fatal(err) 2491 } 2492 if !b.builder.AlreadyUpToDate() { 2493 t.Fatal("expected true") 2494 } 2495 2496 // out2 should still be out of date though, because its input is dirty. 2497 b.commandRunner.commandsRan = nil 2498 b.state.Reset() 2499 if _, err := b.builder.addTargetName("out2"); err != nil { 2500 t.Fatal(err) 2501 } 2502 if err := b.builder.Build(); err != nil { 2503 t.Fatal(err) 2504 } 2505 if 1 != len(b.commandRunner.commandsRan) { 2506 t.Fatal("expected equal") 2507 } 2508 } 2509 2510 func TestBuildTest_DepsGccWithEmptyDepfileErrorsOut(t *testing.T) { 2511 b := NewBuildTest(t) 2512 b.AssertParse(&b.state, "rule cc\n command = cc\n deps = gcc\nbuild out: cc\n", ParseManifestOpts{}) 2513 b.Dirty("out") 2514 2515 if _, err := b.builder.addTargetName("out"); err != nil { 2516 t.Fatal(err) 2517 } 2518 if b.builder.AlreadyUpToDate() { 2519 t.Fatal("expected false") 2520 } 2521 2522 if err := b.builder.Build(); err == nil { 2523 t.Fatal("expected false") 2524 } else if err.Error() != "subcommand failed" { 2525 t.Fatal(err) 2526 } 2527 if 1 != len(b.commandRunner.commandsRan) { 2528 t.Fatal("expected equal") 2529 } 2530 } 2531 2532 func TestBuildTest_FailedDepsParse(t *testing.T) { 2533 b := NewBuildTest(t) 2534 b.AssertParse(&b.state, "build bad_deps.o: cat in1\n deps = gcc\n depfile = in1.d\n", ParseManifestOpts{}) 2535 2536 if _, err := b.builder.addTargetName("bad_deps.o"); err != nil { 2537 t.Fatal(err) 2538 } 2539 2540 // These deps will fail to parse, as they should only have one 2541 // path to the left of the colon. 2542 b.fs.Create("in1.d", "AAA BBB") 2543 2544 if err := b.builder.Build(); err == nil { 2545 t.Fatal("expected false") 2546 } else if err.Error() != "subcommand failed" { 2547 t.Fatal(err) 2548 } 2549 } 2550 2551 type BuildWithQueryDepsLogTest struct { 2552 *BuildTestBase 2553 log DepsLog 2554 builder *Builder 2555 } 2556 2557 func NewBuildWithQueryDepsLogTest(t *testing.T) *BuildWithQueryDepsLogTest { 2558 b := &BuildWithQueryDepsLogTest{ 2559 BuildTestBase: NewBuildTestBase(t), 2560 } 2561 CreateTempDirAndEnter(t) 2562 if err := b.log.OpenForWrite("ninja_deps"); err != nil { 2563 t.Fatal(err) 2564 } 2565 t.Cleanup(func() { 2566 if err2 := b.log.Close(); err2 != nil { 2567 t.Error(err2) 2568 } 2569 }) 2570 b.builder = NewBuilder(&b.state, &b.config, nil, &b.log, &b.fs, b.status, 0) 2571 b.builder.commandRunner = &b.commandRunner 2572 return b 2573 } 2574 2575 // Test a MSVC-style deps log with multiple outputs. 2576 func TestBuildWithQueryDepsLogTest_TwoOutputsDepFileMSVC(t *testing.T) { 2577 b := NewBuildWithQueryDepsLogTest(t) 2578 b.AssertParse(&b.state, "rule cp_multi_msvc\n command = echo 'using $in' && for file in $out; do cp $in $$file; done\n deps = msvc\n msvc_deps_prefix = using \nbuild out1 out2: cp_multi_msvc in1\n", ParseManifestOpts{}) 2579 2580 if _, err := b.builder.addTargetName("out1"); err != nil { 2581 t.Fatal(err) 2582 } 2583 if err := b.builder.Build(); err != nil { 2584 t.Fatal(err) 2585 } 2586 wantCommands := []string{"echo 'using in1' && for file in out1 out2; do cp in1 $file; done"} 2587 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 2588 t.Fatal(diff) 2589 } 2590 2591 out1Node := b.state.Paths["out1"] 2592 out1Deps := b.log.GetDeps(out1Node) 2593 if 1 != len(out1Deps.Nodes) { 2594 t.Fatal("expected equal") 2595 } 2596 if "in1" != out1Deps.Nodes[0].Path { 2597 t.Fatal("expected equal") 2598 } 2599 2600 out2Node := b.state.Paths["out2"] 2601 out2Deps := b.log.GetDeps(out2Node) 2602 if 1 != len(out2Deps.Nodes) { 2603 t.Fatal("expected equal") 2604 } 2605 if "in1" != out2Deps.Nodes[0].Path { 2606 t.Fatal("expected equal") 2607 } 2608 } 2609 2610 // Test a GCC-style deps log with multiple outputs. 2611 func TestBuildWithQueryDepsLogTest_TwoOutputsDepFileGCCOneLine(t *testing.T) { 2612 b := NewBuildWithQueryDepsLogTest(t) 2613 b.AssertParse(&b.state, "rule cp_multi_gcc\n command = echo '$out: $in' > in.d && for file in $out; do cp in1 $$file; done\n deps = gcc\n depfile = in.d\nbuild out1 out2: cp_multi_gcc in1 in2\n", ParseManifestOpts{}) 2614 2615 if _, err := b.builder.addTargetName("out1"); err != nil { 2616 t.Fatal(err) 2617 } 2618 b.fs.Create("in.d", "out1 out2: in1 in2") 2619 if err := b.builder.Build(); err != nil { 2620 t.Fatal(err) 2621 } 2622 wantCommands := []string{"echo 'out1 out2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done"} 2623 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 2624 t.Fatal(diff) 2625 } 2626 2627 out1Node := b.state.Paths["out1"] 2628 out1Deps := b.log.GetDeps(out1Node) 2629 if 2 != len(out1Deps.Nodes) { 2630 t.Fatal("expected equal") 2631 } 2632 if "in1" != out1Deps.Nodes[0].Path { 2633 t.Fatal("expected equal") 2634 } 2635 if "in2" != out1Deps.Nodes[1].Path { 2636 t.Fatal("expected equal") 2637 } 2638 2639 out2Node := b.state.Paths["out2"] 2640 out2Deps := b.log.GetDeps(out2Node) 2641 if 2 != len(out2Deps.Nodes) { 2642 t.Fatal("expected equal") 2643 } 2644 if "in1" != out2Deps.Nodes[0].Path { 2645 t.Fatal("expected equal") 2646 } 2647 if "in2" != out2Deps.Nodes[1].Path { 2648 t.Fatal("expected equal") 2649 } 2650 } 2651 2652 // Test a GCC-style deps log with multiple outputs using a line per input. 2653 func TestBuildWithQueryDepsLogTest_TwoOutputsDepFileGCCMultiLineInput(t *testing.T) { 2654 b := NewBuildWithQueryDepsLogTest(t) 2655 b.AssertParse(&b.state, "rule cp_multi_gcc\n command = echo '$out: in1\\n$out: in2' > in.d && for file in $out; do cp in1 $$file; done\n deps = gcc\n depfile = in.d\nbuild out1 out2: cp_multi_gcc in1 in2\n", ParseManifestOpts{}) 2656 2657 if _, err := b.builder.addTargetName("out1"); err != nil { 2658 t.Fatal(err) 2659 } 2660 b.fs.Create("in.d", "out1 out2: in1\nout1 out2: in2") 2661 if err := b.builder.Build(); err != nil { 2662 t.Fatal(err) 2663 } 2664 wantCommands := []string{"echo 'out1 out2: in1\\nout1 out2: in2' > in.d && for file in out1 out2; do cp in1 $file; done"} 2665 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 2666 t.Fatal(diff) 2667 } 2668 2669 out1Node := b.state.Paths["out1"] 2670 out1Deps := b.log.GetDeps(out1Node) 2671 if 2 != len(out1Deps.Nodes) { 2672 t.Fatal("expected equal") 2673 } 2674 if "in1" != out1Deps.Nodes[0].Path { 2675 t.Fatal("expected equal") 2676 } 2677 if "in2" != out1Deps.Nodes[1].Path { 2678 t.Fatal("expected equal") 2679 } 2680 2681 out2Node := b.state.Paths["out2"] 2682 out2Deps := b.log.GetDeps(out2Node) 2683 if 2 != len(out2Deps.Nodes) { 2684 t.Fatal("expected equal") 2685 } 2686 if "in1" != out2Deps.Nodes[0].Path { 2687 t.Fatal("expected equal") 2688 } 2689 if "in2" != out2Deps.Nodes[1].Path { 2690 t.Fatal("expected equal") 2691 } 2692 } 2693 2694 // Test a GCC-style deps log with multiple outputs using a line per output. 2695 func TestBuildWithQueryDepsLogTest_TwoOutputsDepFileGCCMultiLineOutput(t *testing.T) { 2696 b := NewBuildWithQueryDepsLogTest(t) 2697 b.AssertParse(&b.state, "rule cp_multi_gcc\n command = echo 'out1: $in\\nout2: $in' > in.d && for file in $out; do cp in1 $$file; done\n deps = gcc\n depfile = in.d\nbuild out1 out2: cp_multi_gcc in1 in2\n", ParseManifestOpts{}) 2698 2699 if _, err := b.builder.addTargetName("out1"); err != nil { 2700 t.Fatal(err) 2701 } 2702 b.fs.Create("in.d", "out1: in1 in2\nout2: in1 in2") 2703 if err := b.builder.Build(); err != nil { 2704 t.Fatal(err) 2705 } 2706 wantCommands := []string{"echo 'out1: in1 in2\\nout2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done"} 2707 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 2708 t.Fatal(diff) 2709 } 2710 2711 out1Node := b.state.Paths["out1"] 2712 out1Deps := b.log.GetDeps(out1Node) 2713 if 2 != len(out1Deps.Nodes) { 2714 t.Fatal("expected equal") 2715 } 2716 if "in1" != out1Deps.Nodes[0].Path { 2717 t.Fatal("expected equal") 2718 } 2719 if "in2" != out1Deps.Nodes[1].Path { 2720 t.Fatal("expected equal") 2721 } 2722 2723 out2Node := b.state.Paths["out2"] 2724 out2Deps := b.log.GetDeps(out2Node) 2725 if 2 != len(out2Deps.Nodes) { 2726 t.Fatal("expected equal") 2727 } 2728 if "in1" != out2Deps.Nodes[0].Path { 2729 t.Fatal("expected equal") 2730 } 2731 if "in2" != out2Deps.Nodes[1].Path { 2732 t.Fatal("expected equal") 2733 } 2734 } 2735 2736 // Test a GCC-style deps log with multiple outputs mentioning only the main output. 2737 func TestBuildWithQueryDepsLogTest_TwoOutputsDepFileGCCOnlyMainOutput(t *testing.T) { 2738 b := NewBuildWithQueryDepsLogTest(t) 2739 b.AssertParse(&b.state, "rule cp_multi_gcc\n command = echo 'out1: $in' > in.d && for file in $out; do cp in1 $$file; done\n deps = gcc\n depfile = in.d\nbuild out1 out2: cp_multi_gcc in1 in2\n", ParseManifestOpts{}) 2740 2741 if _, err := b.builder.addTargetName("out1"); err != nil { 2742 t.Fatal(err) 2743 } 2744 b.fs.Create("in.d", "out1: in1 in2") 2745 if err := b.builder.Build(); err != nil { 2746 t.Fatal(err) 2747 } 2748 wantCommand := []string{"echo 'out1: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done"} 2749 if diff := cmp.Diff(wantCommand, b.commandRunner.commandsRan); diff != "" { 2750 t.Fatal(diff) 2751 } 2752 2753 out1Node := b.state.Paths["out1"] 2754 out1Deps := b.log.GetDeps(out1Node) 2755 if 2 != len(out1Deps.Nodes) { 2756 t.Fatal("expected equal") 2757 } 2758 if "in1" != out1Deps.Nodes[0].Path { 2759 t.Fatal("expected equal") 2760 } 2761 if "in2" != out1Deps.Nodes[1].Path { 2762 t.Fatal("expected equal") 2763 } 2764 2765 out2Node := b.state.Paths["out2"] 2766 out2Deps := b.log.GetDeps(out2Node) 2767 if 2 != len(out2Deps.Nodes) { 2768 t.Fatal("expected equal") 2769 } 2770 if "in1" != out2Deps.Nodes[0].Path { 2771 t.Fatal("expected equal") 2772 } 2773 if "in2" != out2Deps.Nodes[1].Path { 2774 t.Fatal("expected equal") 2775 } 2776 } 2777 2778 // Test a GCC-style deps log with multiple outputs mentioning only the secondary output. 2779 func TestBuildWithQueryDepsLogTest_TwoOutputsDepFileGCCOnlySecondaryOutput(t *testing.T) { 2780 b := NewBuildWithQueryDepsLogTest(t) 2781 // Note: This ends up short-circuiting the node creation due to the primary 2782 // output not being present, but it should still work. 2783 b.AssertParse(&b.state, "rule cp_multi_gcc\n command = echo 'out2: $in' > in.d && for file in $out; do cp in1 $$file; done\n deps = gcc\n depfile = in.d\nbuild out1 out2: cp_multi_gcc in1 in2\n", ParseManifestOpts{}) 2784 2785 if _, err := b.builder.addTargetName("out1"); err != nil { 2786 t.Fatal(err) 2787 } 2788 b.fs.Create("in.d", "out2: in1 in2") 2789 if err := b.builder.Build(); err != nil { 2790 t.Fatal(err) 2791 } 2792 wantCommand := []string{"echo 'out2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done"} 2793 if diff := cmp.Diff(wantCommand, b.commandRunner.commandsRan); diff != "" { 2794 t.Fatal(diff) 2795 } 2796 2797 out1Node := b.state.Paths["out1"] 2798 out1Deps := b.log.GetDeps(out1Node) 2799 if 2 != len(out1Deps.Nodes) { 2800 t.Fatal("expected equal") 2801 } 2802 if "in1" != out1Deps.Nodes[0].Path { 2803 t.Fatal("expected equal") 2804 } 2805 if "in2" != out1Deps.Nodes[1].Path { 2806 t.Fatal("expected equal") 2807 } 2808 2809 out2Node := b.state.Paths["out2"] 2810 out2Deps := b.log.GetDeps(out2Node) 2811 if 2 != len(out2Deps.Nodes) { 2812 t.Fatal("expected equal") 2813 } 2814 if "in1" != out2Deps.Nodes[0].Path { 2815 t.Fatal("expected equal") 2816 } 2817 if "in2" != out2Deps.Nodes[1].Path { 2818 t.Fatal("expected equal") 2819 } 2820 } 2821 2822 // Tests of builds involving deps logs necessarily must span 2823 // multiple builds. We reuse methods on BuildTest but not the 2824 // b.builder it sets up, because we want pristine objects for 2825 // each build. 2826 func NewBuildWithDepsLogTest(t *testing.T) *BuildTest { 2827 b := NewBuildTest(t) 2828 CreateTempDirAndEnter(t) 2829 return b 2830 } 2831 2832 // Run a straightforward build where the deps log is used. 2833 func TestBuildWithDepsLogTest_Straightforward(t *testing.T) { 2834 b := NewBuildWithDepsLogTest(t) 2835 // Note: in1 was created by the superclass SetUp(). 2836 manifest := "build out: cat in1\n deps = gcc\n depfile = in1.d\n" 2837 { 2838 state := NewState() 2839 b.AddCatRule(&state) 2840 b.AssertParse(&state, manifest, ParseManifestOpts{}) 2841 2842 // Run the build once, everything should be ok. 2843 depsLog := DepsLog{} 2844 defer depsLog.Close() 2845 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 2846 t.Fatal(err) 2847 } 2848 2849 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 2850 builder.commandRunner = &b.commandRunner 2851 if _, err := builder.addTargetName("out"); err != nil { 2852 t.Fatal(err) 2853 } 2854 b.fs.Create("in1.d", "out: in2") 2855 if err := builder.Build(); err != nil { 2856 t.Fatal(err) 2857 } 2858 2859 // The deps file should have been removed. 2860 if mtime, err := b.fs.Stat("in1.d"); mtime != 0 || err != nil { 2861 t.Fatal(mtime, err) 2862 } 2863 // Recreate it for the next step. 2864 b.fs.Create("in1.d", "out: in2") 2865 depsLog.Close() 2866 builder.commandRunner = nil 2867 } 2868 2869 { 2870 state := NewState() 2871 b.AddCatRule(&state) 2872 b.AssertParse(&state, manifest, ParseManifestOpts{}) 2873 2874 // Touch the file only mentioned in the deps. 2875 b.fs.Tick() 2876 b.fs.Create("in2", "") 2877 2878 // Run the build again. 2879 depsLog := DepsLog{} 2880 defer depsLog.Close() 2881 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 2882 t.Fatal(s, err) 2883 } 2884 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 2885 t.Fatal(err) 2886 } 2887 2888 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 2889 builder.commandRunner = &b.commandRunner 2890 b.commandRunner.commandsRan = nil 2891 if _, err := builder.addTargetName("out"); err != nil { 2892 t.Fatal(err) 2893 } 2894 if err := builder.Build(); err != nil { 2895 t.Fatal(err) 2896 } 2897 2898 // We should have rebuilt the output due to in2 being 2899 // out of date. 2900 if 1 != len(b.commandRunner.commandsRan) { 2901 t.Fatal("expected equal") 2902 } 2903 2904 builder.commandRunner = nil 2905 } 2906 } 2907 2908 // Verify that obsolete dependency info causes a rebuild. 2909 // 1) Run a successful build where everything has time t, record deps. 2910 // 2) Move input/output to time t+1 -- despite files in alignment, 2911 // should still need to rebuild due to deps at older time. 2912 func TestBuildWithDepsLogTest_ObsoleteDeps(t *testing.T) { 2913 b := NewBuildWithDepsLogTest(t) 2914 // Note: in1 was created by the superclass SetUp(). 2915 manifest := "build out: cat in1\n deps = gcc\n depfile = in1.d\n" 2916 { 2917 // Run an ordinary build that gathers dependencies. 2918 b.fs.Create("in1", "") 2919 b.fs.Create("in1.d", "out: ") 2920 2921 state := NewState() 2922 b.AddCatRule(&state) 2923 b.AssertParse(&state, manifest, ParseManifestOpts{}) 2924 2925 // Run the build once, everything should be ok. 2926 depsLog := DepsLog{} 2927 defer depsLog.Close() 2928 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 2929 t.Fatal(err) 2930 } 2931 2932 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 2933 builder.commandRunner = &b.commandRunner 2934 if _, err := builder.addTargetName("out"); err != nil { 2935 t.Fatal(err) 2936 } 2937 if err := builder.Build(); err != nil { 2938 t.Fatal(err) 2939 } 2940 2941 builder.commandRunner = nil 2942 } 2943 2944 // Push all files one tick forward so that only the deps are out 2945 // of date. 2946 b.fs.Tick() 2947 b.fs.Create("in1", "") 2948 b.fs.Create("out", "") 2949 2950 // The deps file should have been removed, so no need to timestamp it. 2951 if mtime, err := b.fs.Stat("in1.d"); mtime != 0 || err != nil { 2952 t.Fatal(mtime, err) 2953 } 2954 2955 { 2956 state := NewState() 2957 b.AddCatRule(&state) 2958 b.AssertParse(&state, manifest, ParseManifestOpts{}) 2959 2960 depsLog := DepsLog{} 2961 defer depsLog.Close() 2962 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 2963 t.Fatal(s, err) 2964 } 2965 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 2966 t.Fatal(err) 2967 } 2968 2969 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 2970 builder.commandRunner = &b.commandRunner 2971 b.commandRunner.commandsRan = nil 2972 if _, err := builder.addTargetName("out"); err != nil { 2973 t.Fatal(err) 2974 } 2975 2976 // Recreate the deps file here because the build expects them to exist. 2977 b.fs.Create("in1.d", "out: ") 2978 2979 if err := builder.Build(); err != nil { 2980 t.Fatal(err) 2981 } 2982 2983 // We should have rebuilt the output due to the deps being 2984 // out of date. 2985 if 1 != len(b.commandRunner.commandsRan) { 2986 t.Fatal("expected equal") 2987 } 2988 2989 builder.commandRunner = nil 2990 } 2991 } 2992 2993 func TestBuildWithDepsLogTest_DepsIgnoredInDryRun(t *testing.T) { 2994 b := NewBuildWithDepsLogTest(t) 2995 manifest := "build out: cat in1\n deps = gcc\n depfile = in1.d\n" 2996 2997 b.fs.Create("out", "") 2998 b.fs.Tick() 2999 b.fs.Create("in1", "") 3000 3001 state := NewState() 3002 b.AddCatRule(&state) 3003 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3004 3005 // The deps log is NULL in dry runs. 3006 b.config.DryRun = true 3007 builder := NewBuilder(&state, &b.config, nil, nil, &b.fs, b.status, 0) 3008 builder.commandRunner = &b.commandRunner 3009 b.commandRunner.commandsRan = nil 3010 3011 if _, err := builder.addTargetName("out"); err != nil { 3012 t.Fatal(err) 3013 } 3014 if err := builder.Build(); err != nil { 3015 t.Fatal(err) 3016 } 3017 if 1 != len(b.commandRunner.commandsRan) { 3018 t.Fatal("expected equal") 3019 } 3020 3021 builder.commandRunner = nil 3022 } 3023 3024 // Check that a restat rule generating a header cancels compilations correctly. 3025 func TestBuildTest_RestatDepfileDependency(t *testing.T) { 3026 b := NewBuildTest(t) 3027 b.AssertParse(&b.state, "rule true\n command = true\n restat = 1\nbuild header.h: true header.in\nbuild out: cat in1\n depfile = in1.d\n", ParseManifestOpts{}) // Would be "write if out-of-date" in reality 3028 3029 b.fs.Create("header.h", "") 3030 b.fs.Create("in1.d", "out: header.h") 3031 b.fs.Tick() 3032 b.fs.Create("header.in", "") 3033 3034 if _, err := b.builder.addTargetName("out"); err != nil { 3035 t.Fatal(err) 3036 } 3037 if err := b.builder.Build(); err != nil { 3038 t.Fatal(err) 3039 } 3040 } 3041 3042 // Check that a restat rule generating a header cancels compilations correctly, 3043 // depslog case. 3044 func TestBuildWithDepsLogTest_RestatDepfileDependencyDepsLog(t *testing.T) { 3045 b := NewBuildWithDepsLogTest(t) 3046 // Note: in1 was created by the superclass SetUp(). 3047 manifest := "rule true\n command = true\n restat = 1\nbuild header.h: true header.in\nbuild out: cat in1\n deps = gcc\n depfile = in1.d\n" // Would be "write if out-of-date" in reality. 3048 { 3049 state := NewState() 3050 b.AddCatRule(&state) 3051 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3052 3053 // Run the build once, everything should be ok. 3054 depsLog := DepsLog{} 3055 defer depsLog.Close() 3056 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3057 t.Fatal(err) 3058 } 3059 3060 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 3061 builder.commandRunner = &b.commandRunner 3062 if _, err := builder.addTargetName("out"); err != nil { 3063 t.Fatal(err) 3064 } 3065 b.fs.Create("in1.d", "out: header.h") 3066 if err := builder.Build(); err != nil { 3067 t.Fatal(err) 3068 } 3069 3070 depsLog.Close() 3071 builder.commandRunner = nil 3072 } 3073 3074 { 3075 state := NewState() 3076 b.AddCatRule(&state) 3077 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3078 3079 // Touch the input of the restat rule. 3080 b.fs.Tick() 3081 b.fs.Create("header.in", "") 3082 3083 // Run the build again. 3084 depsLog := DepsLog{} 3085 defer depsLog.Close() 3086 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 3087 t.Fatal(s, err) 3088 } 3089 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3090 t.Fatal(err) 3091 } 3092 3093 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 3094 builder.commandRunner = &b.commandRunner 3095 b.commandRunner.commandsRan = nil 3096 if _, err := builder.addTargetName("out"); err != nil { 3097 t.Fatal(err) 3098 } 3099 if err := builder.Build(); err != nil { 3100 t.Fatal(err) 3101 } 3102 3103 // Rule "true" should have run again, but the build of "out" should have 3104 // been cancelled due to restat propagating through the depfile header. 3105 if 1 != len(b.commandRunner.commandsRan) { 3106 t.Fatal("expected equal") 3107 } 3108 3109 builder.commandRunner = nil 3110 } 3111 } 3112 3113 func TestBuildWithDepsLogTest_DepFileOKDepsLog(t *testing.T) { 3114 b := NewBuildWithDepsLogTest(t) 3115 manifest := "rule cc\n command = cc $in\n depfile = $out.d\n deps = gcc\nbuild fo$ o.o: cc foo.c\n" 3116 3117 b.fs.Create("foo.c", "") 3118 3119 { 3120 state := NewState() 3121 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3122 3123 // Run the build once, everything should be ok. 3124 depsLog := DepsLog{} 3125 defer depsLog.Close() 3126 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3127 t.Fatal(err) 3128 } 3129 3130 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 3131 builder.commandRunner = &b.commandRunner 3132 if _, err := builder.addTargetName("fo o.o"); err != nil { 3133 t.Fatal(err) 3134 } 3135 b.fs.Create("fo o.o.d", "fo\\ o.o: blah.h bar.h\n") 3136 if err := builder.Build(); err != nil { 3137 t.Fatal(err) 3138 } 3139 3140 depsLog.Close() 3141 builder.commandRunner = nil 3142 } 3143 3144 { 3145 state := NewState() 3146 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3147 3148 depsLog := DepsLog{} 3149 defer depsLog.Close() 3150 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 3151 t.Fatal(s, err) 3152 } 3153 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3154 t.Fatal(err) 3155 } 3156 3157 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 3158 builder.commandRunner = &b.commandRunner 3159 3160 edge := state.Edges[len(state.Edges)-1] 3161 3162 state.GetNode("bar.h", 0).Dirty = true // Mark bar.h as missing. 3163 if _, err := builder.addTargetName("fo o.o"); err != nil { 3164 t.Fatal(err) 3165 } 3166 3167 // Expect three new edges: one generating fo o.o, and two more from 3168 // loading the depfile. 3169 if 3 != len(state.Edges) { 3170 t.Fatal("expected equal") 3171 } 3172 // Expect our edge to now have three inputs: foo.c and two headers. 3173 if 3 != len(edge.Inputs) { 3174 t.Fatal("expected equal") 3175 } 3176 3177 // Expect the command line we generate to only use the original input. 3178 if "cc foo.c" != edge.EvaluateCommand(false) { 3179 t.Fatal("expected equal") 3180 } 3181 3182 depsLog.Close() 3183 builder.commandRunner = nil 3184 } 3185 } 3186 3187 func TestBuildWithDepsLogTest_DiscoveredDepDuringBuildChanged(t *testing.T) { 3188 b := NewBuildWithDepsLogTest(t) 3189 manifest := "rule touch-out-implicit-dep\n command = touch $out ; sleep 1 ; touch $test_dependency\nrule generate-depfile\n command = touch $out ; echo \"$out: $test_dependency\" > $depfile\nbuild out1: touch-out-implicit-dep in1\n test_dependency = inimp\nbuild out2: generate-depfile in1 || out1\n test_dependency = inimp\n depfile = out2.d\n deps = gcc\n" 3190 3191 b.fs.Create("in1", "") 3192 b.fs.Tick() 3193 3194 buildLog := NewBuildLog() 3195 defer buildLog.Close() 3196 3197 { 3198 state := NewState() 3199 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3200 3201 depsLog := DepsLog{} 3202 defer depsLog.Close() 3203 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3204 t.Fatal(err) 3205 } 3206 3207 builder := NewBuilder(&state, &b.config, &buildLog, &depsLog, &b.fs, b.status, 0) 3208 builder.commandRunner = &b.commandRunner 3209 if _, err := builder.addTargetName("out2"); err != nil { 3210 t.Fatal(err) 3211 } 3212 if builder.AlreadyUpToDate() { 3213 t.Fatal("expected false") 3214 } 3215 3216 if err := builder.Build(); err != nil { 3217 t.Fatal(err) 3218 } 3219 if !builder.AlreadyUpToDate() { 3220 t.Fatal("expected true") 3221 } 3222 3223 depsLog.Close() 3224 builder.commandRunner = nil 3225 } 3226 3227 b.fs.Tick() 3228 b.fs.Create("in1", "") 3229 3230 { 3231 state := NewState() 3232 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3233 3234 depsLog := DepsLog{} 3235 defer depsLog.Close() 3236 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 3237 t.Fatal(s, err) 3238 } 3239 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3240 t.Fatal(err) 3241 } 3242 3243 builder := NewBuilder(&state, &b.config, &buildLog, &depsLog, &b.fs, b.status, 0) 3244 builder.commandRunner = &b.commandRunner 3245 if _, err := builder.addTargetName("out2"); err != nil { 3246 t.Fatal(err) 3247 } 3248 if builder.AlreadyUpToDate() { 3249 t.Fatal("expected false") 3250 } 3251 3252 if err := builder.Build(); err != nil { 3253 t.Fatal(err) 3254 } 3255 if !builder.AlreadyUpToDate() { 3256 t.Fatal("expected true") 3257 } 3258 3259 depsLog.Close() 3260 builder.commandRunner = nil 3261 } 3262 3263 b.fs.Tick() 3264 3265 { 3266 state := NewState() 3267 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3268 3269 depsLog := DepsLog{} 3270 defer depsLog.Close() 3271 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 3272 t.Fatal(s, err) 3273 } 3274 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3275 t.Fatal(err) 3276 } 3277 3278 builder := NewBuilder(&state, &b.config, &buildLog, &depsLog, &b.fs, b.status, 0) 3279 builder.commandRunner = &b.commandRunner 3280 if _, err := builder.addTargetName("out2"); err != nil { 3281 t.Fatal(err) 3282 } 3283 if !builder.AlreadyUpToDate() { 3284 t.Fatal("expected true") 3285 } 3286 3287 depsLog.Close() 3288 builder.commandRunner = nil 3289 } 3290 } 3291 3292 func TestBuildWithDepsLogTest_DepFileDepsLogCanonicalize(t *testing.T) { 3293 if runtime.GOOS != "windows" { 3294 t.Skip("windows only") 3295 } 3296 b := NewBuildWithDepsLogTest(t) 3297 manifest := "rule cc\n command = cc $in\n depfile = $out.d\n deps = gcc\nbuild a/b\\c\\d/e/fo$ o.o: cc x\\y/z\\foo.c\n" 3298 3299 b.fs.Create("x/y/z/foo.c", "") 3300 3301 { 3302 state := NewState() 3303 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3304 3305 // Run the build once, everything should be ok. 3306 depsLog := DepsLog{} 3307 defer depsLog.Close() 3308 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3309 t.Fatal(err) 3310 } 3311 3312 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 3313 builder.commandRunner = &b.commandRunner 3314 if _, err := builder.addTargetName("a/b/c/d/e/fo o.o"); err != nil { 3315 t.Fatal(err) 3316 } 3317 // Note, different slashes from manifest. 3318 b.fs.Create("a/b\\c\\d/e/fo o.o.d", "a\\b\\c\\d\\e\\fo\\ o.o: blah.h bar.h\n") 3319 if err := builder.Build(); err != nil { 3320 t.Fatal(err) 3321 } 3322 3323 depsLog.Close() 3324 builder.commandRunner = nil 3325 } 3326 3327 { 3328 state := NewState() 3329 b.AssertParse(&state, manifest, ParseManifestOpts{}) 3330 3331 depsLog := DepsLog{} 3332 defer depsLog.Close() 3333 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 3334 t.Fatal(s, err) 3335 } 3336 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 3337 t.Fatal(err) 3338 } 3339 3340 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 3341 builder.commandRunner = &b.commandRunner 3342 3343 edge := state.Edges[len(state.Edges)-1] 3344 3345 state.GetNode("bar.h", 0).Dirty = true // Mark bar.h as missing. 3346 if _, err := builder.addTargetName("a/b/c/d/e/fo o.o"); err != nil { 3347 t.Fatal(err) 3348 } 3349 3350 // Expect three new edges: one generating fo o.o, and two more from 3351 // loading the depfile. 3352 if 3 != len(state.Edges) { 3353 t.Fatal("expected equal") 3354 } 3355 // Expect our edge to now have three inputs: foo.c and two headers. 3356 if 3 != len(edge.Inputs) { 3357 t.Fatal("expected equal") 3358 } 3359 3360 // Expect the command line we generate to only use the original input. 3361 // Note, slashes from manifest, not .d. 3362 if "cc x\\y/z\\foo.c" != edge.EvaluateCommand(false) { 3363 t.Fatal("expected equal") 3364 } 3365 3366 depsLog.Close() 3367 builder.commandRunner = nil 3368 } 3369 } 3370 3371 // Check that a restat rule doesn't clear an edge if the depfile is missing. 3372 // Follows from: https://github.com/ninja-build/ninja/issues/603 3373 func TestBuildTest_RestatMissingDepfile(t *testing.T) { 3374 b := NewBuildTest(t) 3375 manifest := "rule true\n command = true\n restat = 1\nbuild header.h: true header.in\nbuild out: cat header.h\n depfile = out.d\n" // Would be "write if out-of-date" in reality. 3376 3377 b.fs.Create("header.h", "") 3378 b.fs.Tick() 3379 b.fs.Create("out", "") 3380 b.fs.Create("header.in", "") 3381 3382 // Normally, only 'header.h' would be rebuilt, as 3383 // its rule doesn't touch the output and has 'restat=1' set. 3384 // But we are also missing the depfile for 'out', 3385 // which should force its command to run anyway! 3386 b.RebuildTarget("out", manifest, "", "", nil) 3387 if 2 != len(b.commandRunner.commandsRan) { 3388 t.Fatal("expected equal") 3389 } 3390 } 3391 3392 // Check that a restat rule doesn't clear an edge if the deps are missing. 3393 // https://github.com/ninja-build/ninja/issues/603 3394 func TestBuildWithDepsLogTest_RestatMissingDepfileDepslog(t *testing.T) { 3395 b := NewBuildWithDepsLogTest(t) 3396 manifest := "rule true\n command = true\n restat = 1\nbuild header.h: true header.in\nbuild out: cat header.h\n deps = gcc\n depfile = out.d\n" // Would be "write if out-of-date" in reality. 3397 3398 // Build once to populate ninja deps logs from out.d 3399 b.fs.Create("header.in", "") 3400 b.fs.Create("out.d", "out: header.h") 3401 b.fs.Create("header.h", "") 3402 3403 b.RebuildTarget("out", manifest, "build_log", "ninja_deps", nil) 3404 if 2 != len(b.commandRunner.commandsRan) { 3405 t.Fatal("expected equal") 3406 } 3407 3408 // Sanity: this rebuild should be NOOP 3409 b.RebuildTarget("out", manifest, "build_log", "ninja_deps", nil) 3410 if 0 != len(b.commandRunner.commandsRan) { 3411 t.Fatalf("Expected no command; %#v", b.commandRunner.commandsRan) 3412 } 3413 3414 // Touch 'header.in', blank dependencies log (create a different one). 3415 // Building header.h triggers 'restat' outputs cleanup. 3416 // Validate that out is rebuilt netherless, as deps are missing. 3417 b.fs.Tick() 3418 b.fs.Create("header.in", "") 3419 3420 // (switch to a new blank depsLog "ninja_deps2") 3421 b.RebuildTarget("out", manifest, "build_log", "ninja_deps2", nil) 3422 if 2 != len(b.commandRunner.commandsRan) { 3423 t.Fatal("expected equal") 3424 } 3425 3426 // Sanity: this build should be NOOP 3427 b.RebuildTarget("out", manifest, "build_log", "ninja_deps2", nil) 3428 if 0 != len(b.commandRunner.commandsRan) { 3429 t.Fatal("expected equal") 3430 } 3431 3432 // Check that invalidating deps by target timestamp also works here 3433 // Repeat the test but touch target instead of blanking the log. 3434 b.fs.Tick() 3435 b.fs.Create("header.in", "") 3436 b.fs.Create("out", "") 3437 b.RebuildTarget("out", manifest, "build_log", "ninja_deps2", nil) 3438 if 2 != len(b.commandRunner.commandsRan) { 3439 t.Fatal("expected equal") 3440 } 3441 3442 // And this build should be NOOP again 3443 b.RebuildTarget("out", manifest, "build_log", "ninja_deps2", nil) 3444 if 0 != len(b.commandRunner.commandsRan) { 3445 t.Fatal("expected equal") 3446 } 3447 } 3448 3449 func TestBuildTest_WrongOutputInDepfileCausesRebuild(t *testing.T) { 3450 b := NewBuildTest(t) 3451 manifest := "rule cc\n command = cc $in\n depfile = $out.d\nbuild foo.o: cc foo.c\n" 3452 3453 b.fs.Create("foo.c", "") 3454 b.fs.Create("foo.o", "") 3455 b.fs.Create("header.h", "") 3456 b.fs.Create("foo.o.d", "bar.o.d: header.h\n") 3457 3458 b.RebuildTarget("foo.o", manifest, "build_log", "ninja_deps", nil) 3459 if 1 != len(b.commandRunner.commandsRan) { 3460 t.Fatal("expected equal") 3461 } 3462 } 3463 3464 func TestBuildTest_Console(t *testing.T) { 3465 b := NewBuildTest(t) 3466 b.AssertParse(&b.state, "rule console\n command = console\n pool = console\nbuild cons: console in.txt\n", ParseManifestOpts{}) 3467 3468 b.fs.Create("in.txt", "") 3469 3470 if _, err := b.builder.addTargetName("cons"); err != nil { 3471 t.Fatal(err) 3472 } 3473 if err := b.builder.Build(); err != nil { 3474 t.Fatal(err) 3475 } 3476 if 1 != len(b.commandRunner.commandsRan) { 3477 t.Fatal("expected equal") 3478 } 3479 } 3480 3481 func TestBuildTest_DyndepMissingAndNoRule(t *testing.T) { 3482 b := NewBuildTest(t) 3483 // Verify that we can diagnose when a dyndep file is missing and 3484 // has no rule to build it. 3485 b.AssertParse(&b.state, "rule touch\n command = touch $out\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3486 3487 if _, err := b.builder.addTargetName("out"); err == nil { 3488 t.Fatal("expected false") 3489 } else if err.Error() != "loading 'dd': file does not exist" { 3490 t.Fatal(err) 3491 } 3492 } 3493 3494 func TestBuildTest_DyndepReadyImplicitConnection(t *testing.T) { 3495 b := NewBuildTest(t) 3496 // Verify that a dyndep file can be loaded immediately to discover 3497 // that one edge has an implicit output that is also an implicit 3498 // input of another edge. 3499 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nbuild tmp: touch || dd\n dyndep = dd\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3500 b.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out | out.imp: dyndep | tmp.imp\nbuild tmp | tmp.imp: dyndep\n") 3501 3502 if _, err := b.builder.addTargetName("out"); err != nil { 3503 t.Fatal(err) 3504 } 3505 if err := b.builder.Build(); err != nil { 3506 t.Fatal(err) 3507 } 3508 wantCommands := []string{"touch tmp tmp.imp", "touch out out.imp"} 3509 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3510 t.Fatal(diff) 3511 } 3512 } 3513 3514 func TestBuildTest_DyndepReadySyntaxError(t *testing.T) { 3515 b := NewBuildTest(t) 3516 // Verify that a dyndep file can be loaded immediately to discover 3517 // and reject a syntax error in it. 3518 b.AssertParse(&b.state, "rule touch\n command = touch $out\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3519 b.fs.Create("dd", "build out: dyndep\n") 3520 3521 if _, err := b.builder.addTargetName("out"); err == nil { 3522 t.Fatal("expected false") 3523 } else if err.Error() != "dd:1: expected 'ninja_dyndep_version = ...'\n" { 3524 t.Fatal(err) 3525 } 3526 } 3527 3528 func TestBuildTest_DyndepReadyCircular(t *testing.T) { 3529 b := NewBuildTest(t) 3530 // Verify that a dyndep file can be loaded immediately to discover 3531 // and reject a circular dependency. 3532 b.AssertParse(&b.state, "rule r\n command = unused\nbuild out: r in || dd\n dyndep = dd\nbuild in: r circ\n", ParseManifestOpts{}) 3533 b.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out | circ: dyndep\n") 3534 b.fs.Create("out", "") 3535 3536 if _, err := b.builder.addTargetName("out"); err == nil { 3537 t.Fatal("expected false") 3538 } else if err.Error() != "dependency cycle: circ -> in -> circ" { 3539 t.Fatal(err) 3540 } 3541 } 3542 3543 func TestBuildTest_DyndepBuild(t *testing.T) { 3544 b := NewBuildTest(t) 3545 // Verify that a dyndep file can be built and loaded to discover nothing. 3546 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3547 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out: dyndep\n") 3548 3549 if _, err := b.builder.addTargetName("out"); err != nil { 3550 t.Fatal(err) 3551 } 3552 3553 wantCreated := map[string]struct{}{"dd-in": {}, "in1": {}, "in2": {}} 3554 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 3555 t.Fatal(diff) 3556 } 3557 3558 if err := b.builder.Build(); err != nil { 3559 t.Fatal(err) 3560 } 3561 3562 wantCommand := []string{"cp dd-in dd", "touch out"} 3563 if diff := cmp.Diff(wantCommand, b.commandRunner.commandsRan); diff != "" { 3564 t.Fatal(diff) 3565 } 3566 wantFilesRead := []string{"dd-in", "dd"} 3567 if diff := cmp.Diff(wantFilesRead, b.fs.filesRead); diff != "" { 3568 t.Fatal(diff) 3569 } 3570 wantCreated["dd"] = struct{}{} 3571 wantCreated["out"] = struct{}{} 3572 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 3573 t.Fatal(diff) 3574 } 3575 } 3576 3577 func TestBuildTest_DyndepBuildSyntaxError(t *testing.T) { 3578 b := NewBuildTest(t) 3579 // Verify that a dyndep file can be built and loaded to discover 3580 // and reject a syntax error in it. 3581 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3582 b.fs.Create("dd-in", "build out: dyndep\n") 3583 3584 if _, err := b.builder.addTargetName("out"); err != nil { 3585 t.Fatal(err) 3586 } 3587 3588 if err := b.builder.Build(); err == nil { 3589 t.Fatal("expected false") 3590 } else if err.Error() != "dd:1: expected 'ninja_dyndep_version = ...'\n" { 3591 t.Fatal(err) 3592 } 3593 } 3594 3595 func TestBuildTest_DyndepBuildUnrelatedOutput(t *testing.T) { 3596 b := NewBuildTest(t) 3597 // Verify that a dyndep file can have dependents that do not specify 3598 // it as their dyndep binding. 3599 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild unrelated: touch || dd\nbuild out: touch unrelated || dd\n dyndep = dd\n", ParseManifestOpts{}) 3600 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out: dyndep\n") 3601 b.fs.Tick() 3602 b.fs.Create("out", "") 3603 3604 if _, err := b.builder.addTargetName("out"); err != nil { 3605 t.Fatal(err) 3606 } 3607 3608 if err := b.builder.Build(); err != nil { 3609 t.Fatal(err) 3610 } 3611 wantCommands := []string{"cp dd-in dd", "touch unrelated", "touch out"} 3612 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3613 t.Fatal(diff) 3614 } 3615 } 3616 3617 func TestBuildTest_DyndepBuildDiscoverNewOutput(t *testing.T) { 3618 b := NewBuildTest(t) 3619 // Verify that a dyndep file can be built and loaded to discover 3620 // a new output of an edge. 3621 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild out: touch in || dd\n dyndep = dd\n", ParseManifestOpts{}) 3622 b.fs.Create("in", "") 3623 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out | out.imp: dyndep\n") 3624 b.fs.Tick() 3625 b.fs.Create("out", "") 3626 3627 if _, err := b.builder.addTargetName("out"); err != nil { 3628 t.Fatal(err) 3629 } 3630 3631 if err := b.builder.Build(); err != nil { 3632 t.Fatal(err) 3633 } 3634 wantCommands := []string{"cp dd-in dd", "touch out out.imp"} 3635 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3636 t.Fatal(diff) 3637 } 3638 } 3639 3640 func TestBuildTest_DyndepBuildDiscoverNewOutputWithMultipleRules1(t *testing.T) { 3641 b := NewBuildTest(t) 3642 // Verify that a dyndep file can be built and loaded to discover 3643 // a new output of an edge that is already the output of another edge. 3644 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild out1 | out-twice.imp: touch in\nbuild out2: touch in || dd\n dyndep = dd\n", ParseManifestOpts{}) 3645 b.fs.Create("in", "") 3646 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out2 | out-twice.imp: dyndep\n") 3647 b.fs.Tick() 3648 b.fs.Create("out1", "") 3649 b.fs.Create("out2", "") 3650 3651 if _, err := b.builder.addTargetName("out1"); err != nil { 3652 t.Fatal(err) 3653 } 3654 if _, err := b.builder.addTargetName("out2"); err != nil { 3655 t.Fatal(err) 3656 } 3657 3658 if err := b.builder.Build(); err == nil { 3659 t.Fatal("expected false") 3660 } else if err.Error() != "multiple rules generate out-twice.imp" { 3661 t.Fatal(err) 3662 } 3663 } 3664 3665 func TestBuildTest_DyndepBuildDiscoverNewOutputWithMultipleRules2(t *testing.T) { 3666 b := NewBuildTest(t) 3667 // Verify that a dyndep file can be built and loaded to discover 3668 // a new output of an edge that is already the output of another 3669 // edge also discovered by dyndep. 3670 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd1: cp dd1-in\nbuild out1: touch || dd1\n dyndep = dd1\nbuild dd2: cp dd2-in || dd1\nbuild out2: touch || dd2\n dyndep = dd2\n", ParseManifestOpts{}) // make order predictable for test 3671 b.fs.Create("out1", "") 3672 b.fs.Create("out2", "") 3673 b.fs.Create("dd1-in", "ninja_dyndep_version = 1\nbuild out1 | out-twice.imp: dyndep\n") 3674 b.fs.Create("dd2-in", "") 3675 b.fs.Create("dd2", "ninja_dyndep_version = 1\nbuild out2 | out-twice.imp: dyndep\n") 3676 b.fs.Tick() 3677 b.fs.Create("out1", "") 3678 b.fs.Create("out2", "") 3679 3680 if _, err := b.builder.addTargetName("out1"); err != nil { 3681 t.Fatal(err) 3682 } 3683 if _, err := b.builder.addTargetName("out2"); err != nil { 3684 t.Fatal(err) 3685 } 3686 3687 if err := b.builder.Build(); err == nil { 3688 t.Fatal("expected false") 3689 } else if err.Error() != "multiple rules generate out-twice.imp" { 3690 t.Fatal(err) 3691 } 3692 } 3693 3694 func TestBuildTest_DyndepBuildDiscoverNewInput(t *testing.T) { 3695 b := NewBuildTest(t) 3696 // Verify that a dyndep file can be built and loaded to discover 3697 // a new input to an edge. 3698 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild in: touch\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3699 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out: dyndep | in\n") 3700 b.fs.Tick() 3701 b.fs.Create("out", "") 3702 3703 if _, err := b.builder.addTargetName("out"); err != nil { 3704 t.Fatal(err) 3705 } 3706 3707 if err := b.builder.Build(); err != nil { 3708 t.Fatal(err) 3709 } 3710 wantCommands := []string{"cp dd-in dd", "touch in", "touch out"} 3711 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3712 t.Fatal(diff) 3713 } 3714 } 3715 3716 func TestBuildTest_DyndepBuildDiscoverNewInputWithValidation(t *testing.T) { 3717 b := NewBuildTest(t) 3718 // Verify that a dyndep file cannot contain the |@ validation 3719 // syntax. 3720 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3721 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out: dyndep |@ validation\n") 3722 3723 if _, err := b.builder.addTargetName("out"); err != nil { 3724 t.Fatal(err) 3725 } 3726 3727 err := b.builder.Build() 3728 if err == nil { 3729 t.Fatal("expected false") 3730 } 3731 errFirstLine := strings.SplitN(err.Error(), "\n", 2)[0] 3732 if "dd:2: expected newline, got '|@'" != errFirstLine { 3733 t.Fatal(errFirstLine) 3734 } 3735 } 3736 3737 func TestBuildTest_DyndepBuildDiscoverNewInputWithTransitiveValidation(t *testing.T) { 3738 b := NewBuildTest(t) 3739 // Verify that a dyndep file can be built and loaded to discover 3740 // a new input to an edge that has a validation edge. 3741 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild in: touch |@ validation\nbuild validation: touch in out\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3742 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out: dyndep | in\n") 3743 b.fs.Tick() 3744 b.fs.Create("out", "") 3745 3746 if _, err := b.builder.addTargetName("out"); err != nil { 3747 t.Fatal(err) 3748 } 3749 3750 if err := b.builder.Build(); err != nil { 3751 t.Fatal(err) 3752 } 3753 wantCommands := []string{"cp dd-in dd", "touch in", "touch out", "touch validation"} 3754 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3755 t.Fatal(diff) 3756 } 3757 } 3758 3759 func TestBuildTest_DyndepBuildDiscoverImplicitConnection(t *testing.T) { 3760 b := NewBuildTest(t) 3761 // Verify that a dyndep file can be built and loaded to discover 3762 // that one edge has an implicit output that is also an implicit 3763 // input of another edge. 3764 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild tmp: touch || dd\n dyndep = dd\nbuild out: touch || dd\n dyndep = dd\n", ParseManifestOpts{}) 3765 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out | out.imp: dyndep | tmp.imp\nbuild tmp | tmp.imp: dyndep\n") 3766 3767 if _, err := b.builder.addTargetName("out"); err != nil { 3768 t.Fatal(err) 3769 } 3770 if err := b.builder.Build(); err != nil { 3771 t.Fatal(err) 3772 } 3773 wantCommands := []string{"cp dd-in dd", "touch tmp tmp.imp", "touch out out.imp"} 3774 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3775 t.Fatal(diff) 3776 } 3777 } 3778 3779 func TestBuildTest_DyndepBuildDiscoverOutputAndDepfileInput(t *testing.T) { 3780 // WARNING: I (maruel) am not 100% sure about this test case. 3781 b := NewBuildTest(t) 3782 // Verify that a dyndep file can be built and loaded to discover 3783 // that one edge has an implicit output that is also reported by 3784 // a depfile as an input of another edge. 3785 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild tmp: touch || dd\n dyndep = dd\nbuild out: cp tmp\n depfile = out.d\n", ParseManifestOpts{}) 3786 b.fs.Create("out.d", "out: tmp.imp\n") 3787 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild tmp | tmp.imp: dyndep\n") 3788 3789 if _, err := b.builder.addTargetName("out"); err != nil { 3790 t.Fatal(err) 3791 } 3792 3793 // Loading the depfile gave tmp.imp a phony input edge. 3794 if b.GetNode("tmp.imp").InEdge.Rule != PhonyRule { 3795 t.Fatal("expected true") 3796 } 3797 3798 wantCreated := map[string]struct{}{ 3799 "dd-in": {}, 3800 "in1": {}, 3801 "in2": {}, 3802 "out.d": {}, 3803 } 3804 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 3805 t.Fatal(diff) 3806 } 3807 3808 if err := b.builder.Build(); err != nil { 3809 t.Fatal(err) 3810 } 3811 3812 // Loading the dyndep file gave tmp.imp a real input edge. 3813 if b.GetNode("tmp.imp").InEdge.Rule == PhonyRule { 3814 t.Fatal("expected false") 3815 } 3816 3817 wantCommands := []string{"cp dd-in dd", "touch tmp tmp.imp", "cp tmp out"} 3818 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3819 t.Fatal(diff) 3820 } 3821 wantCreated["dd"] = struct{}{} 3822 wantCreated["out"] = struct{}{} 3823 wantCreated["tmp"] = struct{}{} 3824 wantCreated["tmp.imp"] = struct{}{} 3825 if diff := cmp.Diff(wantCreated, b.fs.filesCreated); diff != "" { 3826 t.Fatal(diff) 3827 } 3828 if !b.builder.AlreadyUpToDate() { 3829 t.Fatal("expected true") 3830 } 3831 } 3832 3833 func TestBuildTest_DyndepBuildDiscoverNowWantEdge(t *testing.T) { 3834 b := NewBuildTest(t) 3835 // Verify that a dyndep file can be built and loaded to discover 3836 // that an edge is actually wanted due to a missing implicit output. 3837 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild tmp: touch || dd\n dyndep = dd\nbuild out: touch tmp || dd\n dyndep = dd\n", ParseManifestOpts{}) 3838 b.fs.Create("tmp", "") 3839 b.fs.Create("out", "") 3840 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out: dyndep\nbuild tmp | tmp.imp: dyndep\n") 3841 3842 if _, err := b.builder.addTargetName("out"); err != nil { 3843 t.Fatal(err) 3844 } 3845 if err := b.builder.Build(); err != nil { 3846 t.Fatal(err) 3847 } 3848 wantCommands := []string{"cp dd-in dd", "touch tmp tmp.imp", "touch out out.imp"} 3849 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3850 t.Fatal(diff) 3851 } 3852 } 3853 3854 func TestBuildTest_DyndepBuildDiscoverNowWantEdgeAndDependent(t *testing.T) { 3855 t.Skip("TODO") 3856 b := NewBuildTest(t) 3857 // Verify that a dyndep file can be built and loaded to discover 3858 // that an edge and a dependent are actually wanted. 3859 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild tmp: touch || dd\n dyndep = dd\nbuild out: touch tmp\n", ParseManifestOpts{}) 3860 b.fs.Create("tmp", "") 3861 b.fs.Create("out", "") 3862 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild tmp | tmp.imp: dyndep\n") 3863 3864 if _, err := b.builder.addTargetName("out"); err != nil { 3865 t.Fatal(err) 3866 } 3867 3868 // fmt.Printf("State:\n") 3869 // b.state.Dump() 3870 // fmt.Printf("Plan:\n") 3871 // b.builder.plan.Dump() 3872 3873 if err := b.builder.Build(); err != nil { 3874 t.Fatal(err) 3875 } 3876 3877 // fmt.Printf("After:\n") 3878 // fmt.Printf("State:\n") 3879 // b.state.Dump() 3880 // fmt.Printf("Plan:\n") 3881 // b.builder.plan.Dump() 3882 3883 wantCommands := []string{"cp dd-in dd", "touch tmp tmp.imp", "touch out out.imp"} 3884 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3885 t.Fatal(diff) 3886 } 3887 } 3888 3889 func TestBuildTest_DyndepBuildDiscoverCircular(t *testing.T) { 3890 b := NewBuildTest(t) 3891 // Verify that a dyndep file can be built and loaded to discover 3892 // and reject a circular dependency. 3893 b.AssertParse(&b.state, "rule r\n command = unused\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild out: r in || dd\n depfile = out.d\n dyndep = dd\nbuild in: r || dd\n dyndep = dd\n", ParseManifestOpts{}) 3894 b.fs.Create("out.d", "out: inimp\n") 3895 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out | circ: dyndep\nbuild in: dyndep | circ\n") 3896 b.fs.Create("out", "") 3897 3898 if _, err := b.builder.addTargetName("out"); err != nil { 3899 t.Fatal(err) 3900 } 3901 3902 if err := b.builder.Build(); err == nil { 3903 t.Fatal("expected false") 3904 } else if err.Error() != "dependency cycle: circ -> in -> circ" && err.Error() != "dependency cycle: in -> circ -> in" { 3905 // Depending on how the pointers in ready work out, we could have 3906 // discovered the cycle from either starting point. 3907 t.Fatal(err) 3908 } 3909 } 3910 3911 func TestBuildWithLogTest_DyndepBuildDiscoverRestat(t *testing.T) { 3912 b := NewBuildWithLogTest(t) 3913 // Verify that a dyndep file can be built and loaded to discover 3914 // that an edge has a restat binding. 3915 b.AssertParse(&b.state, "rule true\n command = true\nrule cp\n command = cp $in $out\nbuild dd: cp dd-in\nbuild out1: true in || dd\n dyndep = dd\nbuild out2: cat out1\n", ParseManifestOpts{}) 3916 3917 b.fs.Create("out1", "") 3918 b.fs.Create("out2", "") 3919 b.fs.Create("dd-in", "ninja_dyndep_version = 1\nbuild out1: dyndep\n restat = 1\n") 3920 b.fs.Tick() 3921 b.fs.Create("in", "") 3922 3923 // Do a pre-build so that there's commands in the log for the outputs, 3924 // otherwise, the lack of an entry in the build log will cause "out2" to 3925 // rebuild regardless of restat. 3926 if _, err := b.builder.addTargetName("out2"); err != nil { 3927 t.Fatal(err) 3928 } 3929 if err := b.builder.Build(); err != nil { 3930 t.Fatal(err) 3931 } 3932 wantCommands := []string{"cp dd-in dd", "true", "cat out1 > out2"} 3933 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3934 t.Fatal(diff) 3935 } 3936 3937 b.commandRunner.commandsRan = nil 3938 b.state.Reset() 3939 b.fs.Tick() 3940 b.fs.Create("in", "") 3941 3942 // We touched "in", so we should build "out1". But because "true" does not 3943 // touch "out1", we should cancel the build of "out2". 3944 if _, err := b.builder.addTargetName("out2"); err != nil { 3945 t.Fatal(err) 3946 } 3947 if err := b.builder.Build(); err != nil { 3948 t.Fatal(err) 3949 } 3950 wantCommands = []string{"true"} 3951 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 3952 t.Fatal(diff) 3953 } 3954 } 3955 3956 func TestBuildTest_DyndepBuildDiscoverScheduledEdge(t *testing.T) { 3957 b := NewBuildTest(t) 3958 // Verify that a dyndep file can be built and loaded to discover a 3959 // new input that itself is an output from an edge that has already 3960 // been scheduled but not finished. We should not re-schedule it. 3961 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild out1 | out1.imp: touch\nbuild zdd: cp zdd-in\n verify_active_edge = out1\nbuild out2: cp out1 || zdd\n dyndep = zdd\n", ParseManifestOpts{}) // verify out1 is active when zdd is finished 3962 b.fs.Create("zdd-in", "ninja_dyndep_version = 1\nbuild out2: dyndep | out1.imp\n") 3963 3964 // Enable concurrent builds so that we can load the dyndep file 3965 // while another edge is still active. 3966 b.commandRunner.maxActiveEdges = 2 3967 3968 // During the build "out1" and "zdd" should be built concurrently. 3969 // The fake command runner will finish these in reverse order 3970 // of the names of the first outputs, so "zdd" will finish first 3971 // and we will load the dyndep file while the edge for "out1" is 3972 // still active. This will add a new dependency on "out1.imp", 3973 // also produced by the active edge. The builder should not 3974 // re-schedule the already-active edge. 3975 3976 if _, err := b.builder.addTargetName("out1"); err != nil { 3977 t.Fatal(err) 3978 } 3979 if _, err := b.builder.addTargetName("out2"); err != nil { 3980 t.Fatal(err) 3981 } 3982 if err := b.builder.Build(); err != nil { 3983 t.Fatal(err) 3984 } 3985 if 3 != len(b.commandRunner.commandsRan) { 3986 t.Fatal("expected equal") 3987 } 3988 // Depending on how the pointers in ready work out, the first 3989 // two commands may have run in either order. 3990 if !(b.commandRunner.commandsRan[0] == "touch out1 out1.imp" && b.commandRunner.commandsRan[1] == "cp zdd-in zdd") || (b.commandRunner.commandsRan[1] == "touch out1 out1.imp" && b.commandRunner.commandsRan[0] == "cp zdd-in zdd") { 3991 t.Fatal("expected true") 3992 } 3993 if "cp out1 out2" != b.commandRunner.commandsRan[2] { 3994 t.Fatal("expected equal") 3995 } 3996 } 3997 3998 func TestBuildTest_DyndepTwoLevelDirect(t *testing.T) { 3999 b := NewBuildTest(t) 4000 // Verify that a clean dyndep file can depend on a dirty dyndep file 4001 // and be loaded properly after the dirty one is built and loaded. 4002 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd1: cp dd1-in\nbuild out1 | out1.imp: touch || dd1\n dyndep = dd1\nbuild dd2: cp dd2-in || dd1\nbuild out2: touch || dd2\n dyndep = dd2\n", ParseManifestOpts{}) // direct order-only dep on dd1 4003 b.fs.Create("out1.imp", "") 4004 b.fs.Create("out2", "") 4005 b.fs.Create("out2.imp", "") 4006 b.fs.Create("dd1-in", "ninja_dyndep_version = 1\nbuild out1: dyndep\n") 4007 b.fs.Create("dd2-in", "") 4008 b.fs.Create("dd2", "ninja_dyndep_version = 1\nbuild out2 | out2.imp: dyndep | out1.imp\n") 4009 4010 // During the build dd1 should be built and loaded. The RecomputeDirty 4011 // called as a result of loading dd1 should not cause dd2 to be loaded 4012 // because the builder will never get a chance to update the build plan 4013 // to account for dd2. Instead dd2 should only be later loaded once the 4014 // builder recognizes that it is now ready (as its order-only dependency 4015 // on dd1 has been satisfied). This test case verifies that each dyndep 4016 // file is loaded to update the build graph independently. 4017 4018 if _, err := b.builder.addTargetName("out2"); err != nil { 4019 t.Fatal(err) 4020 } 4021 if err := b.builder.Build(); err != nil { 4022 t.Fatal(err) 4023 } 4024 wantCommands := []string{"cp dd1-in dd1", "touch out1 out1.imp", "touch out2 out2.imp"} 4025 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4026 t.Fatal(diff) 4027 } 4028 } 4029 4030 func TestBuildTest_DyndepTwoLevelIndirect(t *testing.T) { 4031 b := NewBuildTest(t) 4032 // Verify that dyndep files can add to an edge new implicit inputs that 4033 // correspond to implicit outputs added to other edges by other dyndep 4034 // files on which they (order-only) depend. 4035 b.AssertParse(&b.state, "rule touch\n command = touch $out $out.imp\nrule cp\n command = cp $in $out\nbuild dd1: cp dd1-in\nbuild out1: touch || dd1\n dyndep = dd1\nbuild dd2: cp dd2-in || out1\nbuild out2: touch || dd2\n dyndep = dd2\n", ParseManifestOpts{}) // indirect order-only dep on dd1 4036 b.fs.Create("out1.imp", "") 4037 b.fs.Create("out2", "") 4038 b.fs.Create("out2.imp", "") 4039 b.fs.Create("dd1-in", "ninja_dyndep_version = 1\nbuild out1 | out1.imp: dyndep\n") 4040 b.fs.Create("dd2-in", "") 4041 b.fs.Create("dd2", "ninja_dyndep_version = 1\nbuild out2 | out2.imp: dyndep | out1.imp\n") 4042 4043 // During the build dd1 should be built and loaded. Then dd2 should 4044 // be built and loaded. Loading dd2 should cause the builder to 4045 // recognize that out2 needs to be built even though it was originally 4046 // clean without dyndep info. 4047 4048 if _, err := b.builder.addTargetName("out2"); err != nil { 4049 t.Fatal(err) 4050 } 4051 if err := b.builder.Build(); err != nil { 4052 t.Fatal(err) 4053 } 4054 wantCommands := []string{"cp dd1-in dd1", "touch out1 out1.imp", "touch out2 out2.imp"} 4055 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4056 t.Fatal(diff) 4057 } 4058 } 4059 4060 func TestBuildTest_DyndepTwoLevelDiscoveredReady(t *testing.T) { 4061 b := NewBuildTest(t) 4062 // Verify that a dyndep file can discover a new input whose 4063 // edge also has a dyndep file that is ready to load immediately. 4064 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd0: cp dd0-in\nbuild dd1: cp dd1-in\nbuild in: touch\nbuild tmp: touch || dd0\n dyndep = dd0\nbuild out: touch || dd1\n dyndep = dd1\n", ParseManifestOpts{}) 4065 b.fs.Create("dd1-in", "ninja_dyndep_version = 1\nbuild out: dyndep | tmp\n") 4066 b.fs.Create("dd0-in", "") 4067 b.fs.Create("dd0", "ninja_dyndep_version = 1\nbuild tmp: dyndep | in\n") 4068 b.fs.Tick() 4069 b.fs.Create("out", "") 4070 4071 if _, err := b.builder.addTargetName("out"); err != nil { 4072 t.Fatal(err) 4073 } 4074 4075 if err := b.builder.Build(); err != nil { 4076 t.Fatal(err) 4077 } 4078 wantCommands := []string{"cp dd1-in dd1", "touch in", "touch tmp", "touch out"} 4079 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4080 t.Fatal(diff) 4081 } 4082 } 4083 4084 func TestBuildTest_DyndepTwoLevelDiscoveredDirty(t *testing.T) { 4085 b := NewBuildTest(t) 4086 // Verify that a dyndep file can discover a new input whose 4087 // edge also has a dyndep file that needs to be built. 4088 b.AssertParse(&b.state, "rule touch\n command = touch $out\nrule cp\n command = cp $in $out\nbuild dd0: cp dd0-in\nbuild dd1: cp dd1-in\nbuild in: touch\nbuild tmp: touch || dd0\n dyndep = dd0\nbuild out: touch || dd1\n dyndep = dd1\n", ParseManifestOpts{}) 4089 b.fs.Create("dd1-in", "ninja_dyndep_version = 1\nbuild out: dyndep | tmp\n") 4090 b.fs.Create("dd0-in", "ninja_dyndep_version = 1\nbuild tmp: dyndep | in\n") 4091 b.fs.Tick() 4092 b.fs.Create("out", "") 4093 4094 if _, err := b.builder.addTargetName("out"); err != nil { 4095 t.Fatal(err) 4096 } 4097 4098 if err := b.builder.Build(); err != nil { 4099 t.Fatal(err) 4100 } 4101 wantCommands := []string{"cp dd1-in dd1", "cp dd0-in dd0", "touch in", "touch tmp", "touch out"} 4102 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4103 t.Fatal(diff) 4104 } 4105 } 4106 4107 func TestBuildTest_Validation(t *testing.T) { 4108 b := NewBuildTest(t) 4109 b.AssertParse(&b.state, "build out: cat in |@ validate\nbuild validate: cat in2\n", ParseManifestOpts{}) 4110 4111 b.fs.Create("in", "") 4112 b.fs.Create("in2", "") 4113 4114 if _, err := b.builder.addTargetName("out"); err != nil { 4115 t.Fatal(err) 4116 } 4117 4118 if err := b.builder.Build(); err != nil { 4119 t.Fatal(err) 4120 } 4121 4122 if len(b.commandRunner.commandsRan) != 2 { 4123 t.Fatal("size") 4124 } 4125 4126 // Test touching "in" only rebuilds "out" ("validate" doesn't depend on 4127 // "out"). 4128 b.fs.Tick() 4129 b.fs.Create("in", "") 4130 4131 b.commandRunner.commandsRan = nil 4132 b.state.Reset() 4133 if _, err := b.builder.addTargetName("out"); err != nil { 4134 t.Fatal(err) 4135 } 4136 4137 if err := b.builder.Build(); err != nil { 4138 t.Fatal(err) 4139 } 4140 4141 wantCommands := []string{"cat in > out"} 4142 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4143 t.Fatal(diff) 4144 } 4145 4146 // Test touching "in2" only rebuilds "validate" ("out" doesn't depend on 4147 // "validate"). 4148 b.fs.Tick() 4149 b.fs.Create("in2", "") 4150 4151 b.commandRunner.commandsRan = nil 4152 b.state.Reset() 4153 if _, err := b.builder.addTargetName("out"); err != nil { 4154 t.Fatal(err) 4155 } 4156 4157 if err := b.builder.Build(); err != nil { 4158 t.Fatal(err) 4159 } 4160 4161 wantCommands = []string{"cat in2 > validate"} 4162 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4163 t.Fatal(diff) 4164 } 4165 } 4166 4167 func TestBuildTest_ValidationDependsOnOutput(t *testing.T) { 4168 b := NewBuildTest(t) 4169 b.AssertParse(&b.state, "build out: cat in |@ validate\nbuild validate: cat in2 | out\n", ParseManifestOpts{}) 4170 4171 b.fs.Create("in", "") 4172 b.fs.Create("in2", "") 4173 if _, err := b.builder.addTargetName("out"); err != nil { 4174 t.Fatal(err) 4175 } 4176 4177 if err := b.builder.Build(); err != nil { 4178 t.Fatal(err) 4179 } 4180 4181 if len(b.commandRunner.commandsRan) != 2 { 4182 t.Fatal(b.commandRunner.commandsRan) 4183 } 4184 4185 // Test touching "in" rebuilds "out" and "validate". 4186 b.fs.Tick() 4187 b.fs.Create("in", "") 4188 4189 b.commandRunner.commandsRan = nil 4190 b.state.Reset() 4191 if _, err := b.builder.addTargetName("out"); err != nil { 4192 t.Fatal(err) 4193 } 4194 4195 if err := b.builder.Build(); err != nil { 4196 t.Fatal(err) 4197 } 4198 4199 if len(b.commandRunner.commandsRan) != 2 { 4200 t.Fatal(b.commandRunner.commandsRan) 4201 } 4202 4203 // Test touching "in2" only rebuilds "validate" ("out" doesn't depend on 4204 // "validate"). 4205 b.fs.Tick() 4206 b.fs.Create("in2", "") 4207 4208 b.commandRunner.commandsRan = nil 4209 b.state.Reset() 4210 if _, err := b.builder.addTargetName("out"); err != nil { 4211 t.Fatal(err) 4212 } 4213 4214 if err := b.builder.Build(); err != nil { 4215 t.Fatal(err) 4216 } 4217 4218 wantCommands := []string{"cat in2 > validate"} 4219 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4220 t.Fatal(diff) 4221 } 4222 } 4223 4224 func TestBuildWithDepsLogTest_ValidationThroughDepfile(t *testing.T) { 4225 b := NewBuildTest(t) 4226 manifest := "build out: cat in |@ validate\nbuild validate: cat in2 | out\nbuild out2: cat in3\n deps = gcc\n depfile = out2.d\n" 4227 4228 { 4229 b.fs.Create("in", "") 4230 b.fs.Create("in2", "") 4231 b.fs.Create("in3", "") 4232 b.fs.Create("out2.d", "out: out") 4233 4234 state := NewState() 4235 b.AddCatRule(&state) 4236 b.AssertParse(&state, manifest, ParseManifestOpts{}) 4237 4238 depsLog := DepsLog{} 4239 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 4240 t.Fatal(err) 4241 } 4242 defer depsLog.Close() 4243 4244 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 4245 builder.commandRunner = &b.commandRunner 4246 4247 if _, err := builder.addTargetName("out2"); err != nil { 4248 t.Fatal(err) 4249 } 4250 4251 if err := builder.Build(); err != nil { 4252 t.Fatal(err) 4253 } 4254 4255 // On the first build, only the out2 command is run. 4256 wantCommands := []string{"cat in3 > out2"} 4257 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4258 t.Fatal(diff) 4259 } 4260 4261 // The deps file should have been removed. 4262 if mtime, err := b.fs.Stat("out2.d"); mtime != 0 || err != nil { 4263 t.Fatal(mtime, err) 4264 } 4265 4266 depsLog.Close() 4267 } 4268 4269 b.fs.Tick() 4270 b.commandRunner.commandsRan = nil 4271 4272 { 4273 b.fs.Create("in2", "") 4274 b.fs.Create("in3", "") 4275 4276 state := NewState() 4277 b.AddCatRule(&state) 4278 b.AssertParse(&state, manifest, ParseManifestOpts{}) 4279 4280 depsLog := DepsLog{} 4281 if s, err := depsLog.Load("ninja_deps", &state); s != LoadSuccess || err != nil { 4282 t.Fatal(s, err) 4283 } 4284 if err := depsLog.OpenForWrite("ninja_deps"); err != nil { 4285 t.Fatal(err) 4286 } 4287 4288 builder := NewBuilder(&state, &b.config, nil, &depsLog, &b.fs, b.status, 0) 4289 builder.commandRunner = &b.commandRunner 4290 4291 if _, err := builder.addTargetName("out2"); err != nil { 4292 t.Fatal(err) 4293 } 4294 4295 if err := builder.Build(); err != nil { 4296 t.Fatal(err) 4297 } 4298 4299 // The out and validate actions should have been run as well as out2. 4300 if len(b.commandRunner.commandsRan) != 3 { 4301 t.Fatal(b.commandRunner.commandsRan) 4302 } 4303 // out has to run first, as both out2 and validate depend on it. 4304 if b.commandRunner.commandsRan[0] != "cat in > out" { 4305 t.Fatal(b.commandRunner.commandsRan) 4306 } 4307 4308 depsLog.Close() 4309 } 4310 } 4311 4312 func TestBuildTest_ValidationCircular(t *testing.T) { 4313 b := NewBuildTest(t) 4314 b.AssertParse(&b.state, "build out: cat in |@ out2\nbuild out2: cat in2 |@ out\n", ParseManifestOpts{}) 4315 b.fs.Create("in", "") 4316 b.fs.Create("in2", "") 4317 4318 if _, err := b.builder.addTargetName("out"); err != nil { 4319 t.Fatal(err) 4320 } 4321 4322 if err := b.builder.Build(); err != nil { 4323 t.Fatal(err) 4324 } 4325 4326 if len(b.commandRunner.commandsRan) != 2 { 4327 t.Fatal(b.commandRunner.commandsRan) 4328 } 4329 4330 // Test touching "in" rebuilds "out". 4331 b.fs.Tick() 4332 b.fs.Create("in", "") 4333 4334 b.commandRunner.commandsRan = nil 4335 b.state.Reset() 4336 if _, err := b.builder.addTargetName("out"); err != nil { 4337 t.Fatal(err) 4338 } 4339 4340 if err := b.builder.Build(); err != nil { 4341 t.Fatal(err) 4342 } 4343 4344 wantCommands := []string{"cat in > out"} 4345 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4346 t.Fatal(diff) 4347 } 4348 4349 // Test touching "in2" rebuilds "out2". 4350 b.fs.Tick() 4351 b.fs.Create("in2", "") 4352 4353 b.commandRunner.commandsRan = nil 4354 b.state.Reset() 4355 if _, err := b.builder.addTargetName("out"); err != nil { 4356 t.Fatal(err) 4357 } 4358 4359 if err := b.builder.Build(); err != nil { 4360 t.Fatal(err) 4361 } 4362 4363 wantCommands = []string{"cat in2 > out2"} 4364 if diff := cmp.Diff(wantCommands, b.commandRunner.commandsRan); diff != "" { 4365 t.Fatal(diff) 4366 } 4367 } 4368 4369 func TestBuildTest_ValidationWithCircularDependency(t *testing.T) { 4370 b := NewBuildTest(t) 4371 b.AssertParse(&b.state, "build out: cat in |@ validate\nbuild validate: cat validate_in | out\nbuild validate_in: cat validate\n", ParseManifestOpts{}) 4372 4373 b.fs.Create("in", "") 4374 4375 if _, err := b.builder.addTargetName("out"); err == nil { 4376 t.Fatal("expected failure") 4377 } else if err.Error() != "dependency cycle: validate -> validate_in -> validate" { 4378 t.Fatal(err) 4379 } 4380 }