1// Copyright 2020 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 15package bazel 16 17import ( 18 "fmt" 19 "reflect" 20 "testing" 21) 22 23func TestAqueryMultiArchGenrule(t *testing.T) { 24 // This input string is retrieved from a real build of bionic-related genrules. 25 const inputString = ` 26{ 27 "artifacts": [{ 28 "id": 1, 29 "pathFragmentId": 1 30 }, { 31 "id": 2, 32 "pathFragmentId": 6 33 }, { 34 "id": 3, 35 "pathFragmentId": 8 36 }, { 37 "id": 4, 38 "pathFragmentId": 12 39 }, { 40 "id": 5, 41 "pathFragmentId": 19 42 }, { 43 "id": 6, 44 "pathFragmentId": 20 45 }, { 46 "id": 7, 47 "pathFragmentId": 21 48 }], 49 "actions": [{ 50 "targetId": 1, 51 "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7", 52 "mnemonic": "Genrule", 53 "configurationId": 1, 54 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"], 55 "environmentVariables": [{ 56 "key": "PATH", 57 "value": "/bin:/usr/bin:/usr/local/bin" 58 }], 59 "inputDepSetIds": [1], 60 "outputIds": [4], 61 "primaryOutputId": 4 62 }, { 63 "targetId": 2, 64 "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826", 65 "mnemonic": "Genrule", 66 "configurationId": 1, 67 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"], 68 "environmentVariables": [{ 69 "key": "PATH", 70 "value": "/bin:/usr/bin:/usr/local/bin" 71 }], 72 "inputDepSetIds": [2], 73 "outputIds": [5], 74 "primaryOutputId": 5 75 }, { 76 "targetId": 3, 77 "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342", 78 "mnemonic": "Genrule", 79 "configurationId": 1, 80 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"], 81 "environmentVariables": [{ 82 "key": "PATH", 83 "value": "/bin:/usr/bin:/usr/local/bin" 84 }], 85 "inputDepSetIds": [3], 86 "outputIds": [6], 87 "primaryOutputId": 6 88 }, { 89 "targetId": 4, 90 "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa", 91 "mnemonic": "Genrule", 92 "configurationId": 1, 93 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"], 94 "environmentVariables": [{ 95 "key": "PATH", 96 "value": "/bin:/usr/bin:/usr/local/bin" 97 }], 98 "inputDepSetIds": [4], 99 "outputIds": [7], 100 "primaryOutputId": 7 101 }], 102 "targets": [{ 103 "id": 1, 104 "label": "@sourceroot//bionic/libc:syscalls-arm", 105 "ruleClassId": 1 106 }, { 107 "id": 2, 108 "label": "@sourceroot//bionic/libc:syscalls-x86", 109 "ruleClassId": 1 110 }, { 111 "id": 3, 112 "label": "@sourceroot//bionic/libc:syscalls-x86_64", 113 "ruleClassId": 1 114 }, { 115 "id": 4, 116 "label": "@sourceroot//bionic/libc:syscalls-arm64", 117 "ruleClassId": 1 118 }], 119 "depSetOfFiles": [{ 120 "id": 1, 121 "directArtifactIds": [1, 2, 3] 122 }, { 123 "id": 2, 124 "directArtifactIds": [1, 2, 3] 125 }, { 126 "id": 3, 127 "directArtifactIds": [1, 2, 3] 128 }, { 129 "id": 4, 130 "directArtifactIds": [1, 2, 3] 131 }], 132 "configuration": [{ 133 "id": 1, 134 "mnemonic": "k8-fastbuild", 135 "platformName": "k8", 136 "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046" 137 }], 138 "ruleClasses": [{ 139 "id": 1, 140 "name": "genrule" 141 }], 142 "pathFragments": [{ 143 "id": 5, 144 "label": ".." 145 }, { 146 "id": 4, 147 "label": "sourceroot", 148 "parentId": 5 149 }, { 150 "id": 3, 151 "label": "bionic", 152 "parentId": 4 153 }, { 154 "id": 2, 155 "label": "libc", 156 "parentId": 3 157 }, { 158 "id": 1, 159 "label": "SYSCALLS.TXT", 160 "parentId": 2 161 }, { 162 "id": 7, 163 "label": "tools", 164 "parentId": 2 165 }, { 166 "id": 6, 167 "label": "gensyscalls.py", 168 "parentId": 7 169 }, { 170 "id": 11, 171 "label": "bazel_tools", 172 "parentId": 5 173 }, { 174 "id": 10, 175 "label": "tools", 176 "parentId": 11 177 }, { 178 "id": 9, 179 "label": "genrule", 180 "parentId": 10 181 }, { 182 "id": 8, 183 "label": "genrule-setup.sh", 184 "parentId": 9 185 }, { 186 "id": 18, 187 "label": "bazel-out" 188 }, { 189 "id": 17, 190 "label": "sourceroot", 191 "parentId": 18 192 }, { 193 "id": 16, 194 "label": "k8-fastbuild", 195 "parentId": 17 196 }, { 197 "id": 15, 198 "label": "bin", 199 "parentId": 16 200 }, { 201 "id": 14, 202 "label": "bionic", 203 "parentId": 15 204 }, { 205 "id": 13, 206 "label": "libc", 207 "parentId": 14 208 }, { 209 "id": 12, 210 "label": "syscalls-arm.S", 211 "parentId": 13 212 }, { 213 "id": 19, 214 "label": "syscalls-x86.S", 215 "parentId": 13 216 }, { 217 "id": 20, 218 "label": "syscalls-x86_64.S", 219 "parentId": 13 220 }, { 221 "id": 21, 222 "label": "syscalls-arm64.S", 223 "parentId": 13 224 }] 225}` 226 actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString)) 227 expectedBuildStatements := []BuildStatement{} 228 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} { 229 expectedBuildStatements = append(expectedBuildStatements, 230 BuildStatement{ 231 Command: fmt.Sprintf( 232 "/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'", 233 arch, arch), 234 OutputPaths: []string{ 235 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch), 236 }, 237 InputPaths: []string{ 238 "../sourceroot/bionic/libc/SYSCALLS.TXT", 239 "../sourceroot/bionic/libc/tools/gensyscalls.py", 240 "../bazel_tools/tools/genrule/genrule-setup.sh", 241 }, 242 Env: []KeyValuePair{ 243 KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"}, 244 }, 245 Mnemonic: "Genrule", 246 }) 247 } 248 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements) 249} 250 251func TestInvalidOutputId(t *testing.T) { 252 const inputString = ` 253{ 254 "artifacts": [{ 255 "id": 1, 256 "pathFragmentId": 1 257 }, { 258 "id": 2, 259 "pathFragmentId": 2 260 }], 261 "actions": [{ 262 "targetId": 1, 263 "actionKey": "x", 264 "mnemonic": "x", 265 "arguments": ["touch", "foo"], 266 "inputDepSetIds": [1], 267 "outputIds": [3], 268 "primaryOutputId": 3 269 }], 270 "depSetOfFiles": [{ 271 "id": 1, 272 "directArtifactIds": [1, 2] 273 }], 274 "pathFragments": [{ 275 "id": 1, 276 "label": "one" 277 }, { 278 "id": 2, 279 "label": "two" 280 }] 281}` 282 283 _, err := AqueryBuildStatements([]byte(inputString)) 284 assertError(t, err, "undefined outputId 3") 285} 286 287func TestInvalidInputDepsetId(t *testing.T) { 288 const inputString = ` 289{ 290 "artifacts": [{ 291 "id": 1, 292 "pathFragmentId": 1 293 }, { 294 "id": 2, 295 "pathFragmentId": 2 296 }], 297 "actions": [{ 298 "targetId": 1, 299 "actionKey": "x", 300 "mnemonic": "x", 301 "arguments": ["touch", "foo"], 302 "inputDepSetIds": [2], 303 "outputIds": [1], 304 "primaryOutputId": 1 305 }], 306 "depSetOfFiles": [{ 307 "id": 1, 308 "directArtifactIds": [1, 2] 309 }], 310 "pathFragments": [{ 311 "id": 1, 312 "label": "one" 313 }, { 314 "id": 2, 315 "label": "two" 316 }] 317}` 318 319 _, err := AqueryBuildStatements([]byte(inputString)) 320 assertError(t, err, "undefined input depsetId 2") 321} 322 323func TestInvalidInputArtifactId(t *testing.T) { 324 const inputString = ` 325{ 326 "artifacts": [{ 327 "id": 1, 328 "pathFragmentId": 1 329 }, { 330 "id": 2, 331 "pathFragmentId": 2 332 }], 333 "actions": [{ 334 "targetId": 1, 335 "actionKey": "x", 336 "mnemonic": "x", 337 "arguments": ["touch", "foo"], 338 "inputDepSetIds": [1], 339 "outputIds": [1], 340 "primaryOutputId": 1 341 }], 342 "depSetOfFiles": [{ 343 "id": 1, 344 "directArtifactIds": [1, 3] 345 }], 346 "pathFragments": [{ 347 "id": 1, 348 "label": "one" 349 }, { 350 "id": 2, 351 "label": "two" 352 }] 353}` 354 355 _, err := AqueryBuildStatements([]byte(inputString)) 356 assertError(t, err, "undefined input artifactId 3") 357} 358 359func TestInvalidPathFragmentId(t *testing.T) { 360 const inputString = ` 361{ 362 "artifacts": [{ 363 "id": 1, 364 "pathFragmentId": 1 365 }, { 366 "id": 2, 367 "pathFragmentId": 2 368 }], 369 "actions": [{ 370 "targetId": 1, 371 "actionKey": "x", 372 "mnemonic": "x", 373 "arguments": ["touch", "foo"], 374 "inputDepSetIds": [1], 375 "outputIds": [1], 376 "primaryOutputId": 1 377 }], 378 "depSetOfFiles": [{ 379 "id": 1, 380 "directArtifactIds": [1, 2] 381 }], 382 "pathFragments": [{ 383 "id": 1, 384 "label": "one" 385 }, { 386 "id": 2, 387 "label": "two", 388 "parentId": 3 389 }] 390}` 391 392 _, err := AqueryBuildStatements([]byte(inputString)) 393 assertError(t, err, "undefined path fragment id 3") 394} 395 396func TestDepfiles(t *testing.T) { 397 const inputString = ` 398{ 399 "artifacts": [{ 400 "id": 1, 401 "pathFragmentId": 1 402 }, { 403 "id": 2, 404 "pathFragmentId": 2 405 }, { 406 "id": 3, 407 "pathFragmentId": 3 408 }], 409 "actions": [{ 410 "targetId": 1, 411 "actionKey": "x", 412 "mnemonic": "x", 413 "arguments": ["touch", "foo"], 414 "inputDepSetIds": [1], 415 "outputIds": [2, 3], 416 "primaryOutputId": 2 417 }], 418 "depSetOfFiles": [{ 419 "id": 1, 420 "directArtifactIds": [1, 2, 3] 421 }], 422 "pathFragments": [{ 423 "id": 1, 424 "label": "one" 425 }, { 426 "id": 2, 427 "label": "two" 428 }, { 429 "id": 3, 430 "label": "two.d" 431 }] 432}` 433 434 actual, err := AqueryBuildStatements([]byte(inputString)) 435 if err != nil { 436 t.Errorf("Unexpected error %q", err) 437 } 438 if expected := 1; len(actual) != expected { 439 t.Fatalf("Expected %d build statements, got %d", expected, len(actual)) 440 } 441 442 bs := actual[0] 443 expectedDepfile := "two.d" 444 if bs.Depfile == nil { 445 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile) 446 } else if *bs.Depfile != expectedDepfile { 447 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile) 448 } 449} 450 451func TestMultipleDepfiles(t *testing.T) { 452 const inputString = ` 453{ 454 "artifacts": [{ 455 "id": 1, 456 "pathFragmentId": 1 457 }, { 458 "id": 2, 459 "pathFragmentId": 2 460 }, { 461 "id": 3, 462 "pathFragmentId": 3 463 }, { 464 "id": 4, 465 "pathFragmentId": 4 466 }], 467 "actions": [{ 468 "targetId": 1, 469 "actionKey": "x", 470 "mnemonic": "x", 471 "arguments": ["touch", "foo"], 472 "inputDepSetIds": [1], 473 "outputIds": [2,3,4], 474 "primaryOutputId": 2 475 }], 476 "depSetOfFiles": [{ 477 "id": 1, 478 "directArtifactIds": [1, 2, 3, 4] 479 }], 480 "pathFragments": [{ 481 "id": 1, 482 "label": "one" 483 }, { 484 "id": 2, 485 "label": "two" 486 }, { 487 "id": 3, 488 "label": "two.d" 489 }, { 490 "id": 4, 491 "label": "other.d" 492 }] 493}` 494 495 _, err := AqueryBuildStatements([]byte(inputString)) 496 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`) 497} 498 499func TestTransitiveInputDepsets(t *testing.T) { 500 // The input aquery for this test comes from a proof-of-concept starlark rule which registers 501 // a single action with many inputs given via a deep depset. 502 const inputString = ` 503{ 504 "artifacts": [{ 505 "id": 1, 506 "pathFragmentId": 1 507 }, { 508 "id": 2, 509 "pathFragmentId": 7 510 }, { 511 "id": 3, 512 "pathFragmentId": 8 513 }, { 514 "id": 4, 515 "pathFragmentId": 9 516 }, { 517 "id": 5, 518 "pathFragmentId": 10 519 }, { 520 "id": 6, 521 "pathFragmentId": 11 522 }, { 523 "id": 7, 524 "pathFragmentId": 12 525 }, { 526 "id": 8, 527 "pathFragmentId": 13 528 }, { 529 "id": 9, 530 "pathFragmentId": 14 531 }, { 532 "id": 10, 533 "pathFragmentId": 15 534 }, { 535 "id": 11, 536 "pathFragmentId": 16 537 }, { 538 "id": 12, 539 "pathFragmentId": 17 540 }, { 541 "id": 13, 542 "pathFragmentId": 18 543 }, { 544 "id": 14, 545 "pathFragmentId": 19 546 }, { 547 "id": 15, 548 "pathFragmentId": 20 549 }, { 550 "id": 16, 551 "pathFragmentId": 21 552 }, { 553 "id": 17, 554 "pathFragmentId": 22 555 }, { 556 "id": 18, 557 "pathFragmentId": 23 558 }, { 559 "id": 19, 560 "pathFragmentId": 24 561 }, { 562 "id": 20, 563 "pathFragmentId": 25 564 }, { 565 "id": 21, 566 "pathFragmentId": 26 567 }], 568 "actions": [{ 569 "targetId": 1, 570 "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50", 571 "mnemonic": "Action", 572 "configurationId": 1, 573 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"], 574 "inputDepSetIds": [1], 575 "outputIds": [21], 576 "primaryOutputId": 21 577 }], 578 "depSetOfFiles": [{ 579 "id": 3, 580 "directArtifactIds": [1, 2, 3, 4, 5] 581 }, { 582 "id": 4, 583 "directArtifactIds": [6, 7, 8, 9, 10] 584 }, { 585 "id": 2, 586 "transitiveDepSetIds": [3, 4], 587 "directArtifactIds": [11, 12, 13, 14, 15] 588 }, { 589 "id": 5, 590 "directArtifactIds": [16, 17, 18, 19] 591 }, { 592 "id": 1, 593 "transitiveDepSetIds": [2, 5], 594 "directArtifactIds": [20] 595 }], 596 "pathFragments": [{ 597 "id": 6, 598 "label": "bazel-out" 599 }, { 600 "id": 5, 601 "label": "sourceroot", 602 "parentId": 6 603 }, { 604 "id": 4, 605 "label": "k8-fastbuild", 606 "parentId": 5 607 }, { 608 "id": 3, 609 "label": "bin", 610 "parentId": 4 611 }, { 612 "id": 2, 613 "label": "testpkg", 614 "parentId": 3 615 }, { 616 "id": 1, 617 "label": "test_1", 618 "parentId": 2 619 }, { 620 "id": 7, 621 "label": "test_2", 622 "parentId": 2 623 }, { 624 "id": 8, 625 "label": "test_3", 626 "parentId": 2 627 }, { 628 "id": 9, 629 "label": "test_4", 630 "parentId": 2 631 }, { 632 "id": 10, 633 "label": "test_5", 634 "parentId": 2 635 }, { 636 "id": 11, 637 "label": "test_6", 638 "parentId": 2 639 }, { 640 "id": 12, 641 "label": "test_7", 642 "parentId": 2 643 }, { 644 "id": 13, 645 "label": "test_8", 646 "parentId": 2 647 }, { 648 "id": 14, 649 "label": "test_9", 650 "parentId": 2 651 }, { 652 "id": 15, 653 "label": "test_10", 654 "parentId": 2 655 }, { 656 "id": 16, 657 "label": "test_11", 658 "parentId": 2 659 }, { 660 "id": 17, 661 "label": "test_12", 662 "parentId": 2 663 }, { 664 "id": 18, 665 "label": "test_13", 666 "parentId": 2 667 }, { 668 "id": 19, 669 "label": "test_14", 670 "parentId": 2 671 }, { 672 "id": 20, 673 "label": "test_15", 674 "parentId": 2 675 }, { 676 "id": 21, 677 "label": "test_16", 678 "parentId": 2 679 }, { 680 "id": 22, 681 "label": "test_17", 682 "parentId": 2 683 }, { 684 "id": 23, 685 "label": "test_18", 686 "parentId": 2 687 }, { 688 "id": 24, 689 "label": "test_19", 690 "parentId": 2 691 }, { 692 "id": 25, 693 "label": "test_root", 694 "parentId": 2 695 }, { 696 "id": 26, 697 "label": "test_out", 698 "parentId": 2 699 }] 700}` 701 702 actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString)) 703 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs 704 // are given via a deep depset, but the depset is flattened when returned as a 705 // BuildStatement slice. 706 inputPaths := []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root"} 707 for i := 1; i < 20; i++ { 708 inputPaths = append(inputPaths, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i)) 709 } 710 expectedBuildStatements := []BuildStatement{ 711 BuildStatement{ 712 Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'", 713 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"}, 714 InputPaths: inputPaths, 715 Mnemonic: "Action", 716 }, 717 } 718 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements) 719} 720 721func TestMiddlemenAction(t *testing.T) { 722 const inputString = ` 723{ 724 "artifacts": [{ 725 "id": 1, 726 "pathFragmentId": 1 727 }, { 728 "id": 2, 729 "pathFragmentId": 2 730 }, { 731 "id": 3, 732 "pathFragmentId": 3 733 }, { 734 "id": 4, 735 "pathFragmentId": 4 736 }, { 737 "id": 5, 738 "pathFragmentId": 5 739 }, { 740 "id": 6, 741 "pathFragmentId": 6 742 }], 743 "pathFragments": [{ 744 "id": 1, 745 "label": "middleinput_one" 746 }, { 747 "id": 2, 748 "label": "middleinput_two" 749 }, { 750 "id": 3, 751 "label": "middleman_artifact" 752 }, { 753 "id": 4, 754 "label": "maininput_one" 755 }, { 756 "id": 5, 757 "label": "maininput_two" 758 }, { 759 "id": 6, 760 "label": "output" 761 }], 762 "depSetOfFiles": [{ 763 "id": 1, 764 "directArtifactIds": [1, 2] 765 }, { 766 "id": 2, 767 "directArtifactIds": [3, 4, 5] 768 }], 769 "actions": [{ 770 "targetId": 1, 771 "actionKey": "x", 772 "mnemonic": "Middleman", 773 "arguments": ["touch", "foo"], 774 "inputDepSetIds": [1], 775 "outputIds": [3], 776 "primaryOutputId": 3 777 }, { 778 "targetId": 2, 779 "actionKey": "y", 780 "mnemonic": "Main action", 781 "arguments": ["touch", "foo"], 782 "inputDepSetIds": [2], 783 "outputIds": [6], 784 "primaryOutputId": 6 785 }] 786}` 787 788 actual, err := AqueryBuildStatements([]byte(inputString)) 789 if err != nil { 790 t.Errorf("Unexpected error %q", err) 791 } 792 if expected := 1; len(actual) != expected { 793 t.Fatalf("Expected %d build statements, got %d", expected, len(actual)) 794 } 795 796 bs := actual[0] 797 expectedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"} 798 if !reflect.DeepEqual(bs.InputPaths, expectedInputs) { 799 t.Errorf("Expected main action inputs %q, but got %q", expectedInputs, bs.InputPaths) 800 } 801 802 expectedOutputs := []string{"output"} 803 if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) { 804 t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths) 805 } 806} 807 808func TestSimpleSymlink(t *testing.T) { 809 const inputString = ` 810{ 811 "artifacts": [{ 812 "id": 1, 813 "pathFragmentId": 3 814 }, { 815 "id": 2, 816 "pathFragmentId": 5 817 }], 818 "actions": [{ 819 "targetId": 1, 820 "actionKey": "x", 821 "mnemonic": "Symlink", 822 "inputDepSetIds": [1], 823 "outputIds": [2], 824 "primaryOutputId": 2 825 }], 826 "depSetOfFiles": [{ 827 "id": 1, 828 "directArtifactIds": [1] 829 }], 830 "pathFragments": [{ 831 "id": 1, 832 "label": "one" 833 }, { 834 "id": 2, 835 "label": "file_subdir", 836 "parentId": 1 837 }, { 838 "id": 3, 839 "label": "file", 840 "parentId": 2 841 }, { 842 "id": 4, 843 "label": "symlink_subdir", 844 "parentId": 1 845 }, { 846 "id": 5, 847 "label": "symlink", 848 "parentId": 4 849 }] 850}` 851 852 actual, err := AqueryBuildStatements([]byte(inputString)) 853 854 if err != nil { 855 t.Errorf("Unexpected error %q", err) 856 } 857 858 expectedBuildStatements := []BuildStatement{ 859 BuildStatement{ 860 Command: "mkdir -p one/symlink_subdir && " + 861 "rm -f one/symlink_subdir/symlink && " + 862 "ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink", 863 InputPaths: []string{"one/file_subdir/file"}, 864 OutputPaths: []string{"one/symlink_subdir/symlink"}, 865 SymlinkPaths: []string{"one/symlink_subdir/symlink"}, 866 Mnemonic: "Symlink", 867 }, 868 } 869 assertBuildStatements(t, actual, expectedBuildStatements) 870} 871 872func TestSymlinkQuotesPaths(t *testing.T) { 873 const inputString = ` 874{ 875 "artifacts": [{ 876 "id": 1, 877 "pathFragmentId": 3 878 }, { 879 "id": 2, 880 "pathFragmentId": 5 881 }], 882 "actions": [{ 883 "targetId": 1, 884 "actionKey": "x", 885 "mnemonic": "SolibSymlink", 886 "inputDepSetIds": [1], 887 "outputIds": [2], 888 "primaryOutputId": 2 889 }], 890 "depSetOfFiles": [{ 891 "id": 1, 892 "directArtifactIds": [1] 893 }], 894 "pathFragments": [{ 895 "id": 1, 896 "label": "one" 897 }, { 898 "id": 2, 899 "label": "file subdir", 900 "parentId": 1 901 }, { 902 "id": 3, 903 "label": "file", 904 "parentId": 2 905 }, { 906 "id": 4, 907 "label": "symlink subdir", 908 "parentId": 1 909 }, { 910 "id": 5, 911 "label": "symlink", 912 "parentId": 4 913 }] 914}` 915 916 actual, err := AqueryBuildStatements([]byte(inputString)) 917 918 if err != nil { 919 t.Errorf("Unexpected error %q", err) 920 } 921 922 expectedBuildStatements := []BuildStatement{ 923 BuildStatement{ 924 Command: "mkdir -p 'one/symlink subdir' && " + 925 "rm -f 'one/symlink subdir/symlink' && " + 926 "ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'", 927 InputPaths: []string{"one/file subdir/file"}, 928 OutputPaths: []string{"one/symlink subdir/symlink"}, 929 SymlinkPaths: []string{"one/symlink subdir/symlink"}, 930 Mnemonic: "SolibSymlink", 931 }, 932 } 933 assertBuildStatements(t, expectedBuildStatements, actual) 934} 935 936func TestSymlinkMultipleInputs(t *testing.T) { 937 const inputString = ` 938{ 939 "artifacts": [{ 940 "id": 1, 941 "pathFragmentId": 1 942 }, { 943 "id": 2, 944 "pathFragmentId": 2 945 }, { 946 "id": 3, 947 "pathFragmentId": 3 948 }], 949 "actions": [{ 950 "targetId": 1, 951 "actionKey": "x", 952 "mnemonic": "Symlink", 953 "inputDepSetIds": [1], 954 "outputIds": [3], 955 "primaryOutputId": 3 956 }], 957 "depSetOfFiles": [{ 958 "id": 1, 959 "directArtifactIds": [1,2] 960 }], 961 "pathFragments": [{ 962 "id": 1, 963 "label": "file" 964 }, { 965 "id": 2, 966 "label": "other_file" 967 }, { 968 "id": 3, 969 "label": "symlink" 970 }] 971}` 972 973 _, err := AqueryBuildStatements([]byte(inputString)) 974 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`) 975} 976 977func TestSymlinkMultipleOutputs(t *testing.T) { 978 const inputString = ` 979{ 980 "artifacts": [{ 981 "id": 1, 982 "pathFragmentId": 1 983 }, { 984 "id": 2, 985 "pathFragmentId": 2 986 }, { 987 "id": 3, 988 "pathFragmentId": 3 989 }], 990 "actions": [{ 991 "targetId": 1, 992 "actionKey": "x", 993 "mnemonic": "Symlink", 994 "inputDepSetIds": [1], 995 "outputIds": [2,3], 996 "primaryOutputId": 2 997 }], 998 "depSetOfFiles": [{ 999 "id": 1, 1000 "directArtifactIds": [1] 1001 }], 1002 "pathFragments": [{ 1003 "id": 1, 1004 "label": "file" 1005 }, { 1006 "id": 2, 1007 "label": "symlink" 1008 }, { 1009 "id": 3, 1010 "label": "other_symlink" 1011 }] 1012}` 1013 1014 _, err := AqueryBuildStatements([]byte(inputString)) 1015 assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`) 1016} 1017 1018func TestTemplateExpandActionSubstitutions(t *testing.T) { 1019 const inputString = ` 1020{ 1021 "artifacts": [{ 1022 "id": 1, 1023 "pathFragmentId": 1 1024 }], 1025 "actions": [{ 1026 "targetId": 1, 1027 "actionKey": "x", 1028 "mnemonic": "TemplateExpand", 1029 "configurationId": 1, 1030 "outputIds": [1], 1031 "primaryOutputId": 1, 1032 "executionPlatform": "//build/bazel/platforms:linux_x86_64", 1033 "templateContent": "Test template substitutions: %token1%, %python_binary%", 1034 "substitutions": [{ 1035 "key": "%token1%", 1036 "value": "abcd" 1037 },{ 1038 "key": "%python_binary%", 1039 "value": "python3" 1040 }] 1041 }], 1042 "pathFragments": [{ 1043 "id": 1, 1044 "label": "template_file" 1045 }] 1046}` 1047 1048 actual, err := AqueryBuildStatements([]byte(inputString)) 1049 1050 if err != nil { 1051 t.Errorf("Unexpected error %q", err) 1052 } 1053 1054 expectedBuildStatements := []BuildStatement{ 1055 BuildStatement{ 1056 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " + 1057 "chmod a+x template_file'", 1058 OutputPaths: []string{"template_file"}, 1059 Mnemonic: "TemplateExpand", 1060 }, 1061 } 1062 assertBuildStatements(t, expectedBuildStatements, actual) 1063} 1064 1065func TestTemplateExpandActionNoOutput(t *testing.T) { 1066 const inputString = ` 1067{ 1068 "artifacts": [{ 1069 "id": 1, 1070 "pathFragmentId": 1 1071 }], 1072 "actions": [{ 1073 "targetId": 1, 1074 "actionKey": "x", 1075 "mnemonic": "TemplateExpand", 1076 "configurationId": 1, 1077 "primaryOutputId": 1, 1078 "executionPlatform": "//build/bazel/platforms:linux_x86_64", 1079 "templateContent": "Test template substitutions: %token1%, %python_binary%", 1080 "substitutions": [{ 1081 "key": "%token1%", 1082 "value": "abcd" 1083 },{ 1084 "key": "%python_binary%", 1085 "value": "python3" 1086 }] 1087 }], 1088 "pathFragments": [{ 1089 "id": 1, 1090 "label": "template_file" 1091 }] 1092}` 1093 1094 _, err := AqueryBuildStatements([]byte(inputString)) 1095 assertError(t, err, `Expect 1 output to template expand action, got: output []`) 1096} 1097 1098func TestPythonZipperActionSuccess(t *testing.T) { 1099 const inputString = ` 1100{ 1101 "artifacts": [{ 1102 "id": 1, 1103 "pathFragmentId": 1 1104 },{ 1105 "id": 2, 1106 "pathFragmentId": 2 1107 },{ 1108 "id": 3, 1109 "pathFragmentId": 3 1110 },{ 1111 "id": 4, 1112 "pathFragmentId": 4 1113 },{ 1114 "id": 5, 1115 "pathFragmentId": 10 1116 },{ 1117 "id": 10, 1118 "pathFragmentId": 20 1119 }], 1120 "actions": [{ 1121 "targetId": 1, 1122 "actionKey": "x", 1123 "mnemonic": "TemplateExpand", 1124 "configurationId": 1, 1125 "outputIds": [1], 1126 "primaryOutputId": 1, 1127 "executionPlatform": "//build/bazel/platforms:linux_x86_64", 1128 "templateContent": "Test template substitutions: %token1%, %python_binary%", 1129 "substitutions": [{ 1130 "key": "%token1%", 1131 "value": "abcd" 1132 },{ 1133 "key": "%python_binary%", 1134 "value": "python3" 1135 }] 1136 },{ 1137 "targetId": 1, 1138 "actionKey": "x", 1139 "mnemonic": "PythonZipper", 1140 "configurationId": 1, 1141 "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"], 1142 "outputIds": [2], 1143 "inputDepSetIds": [1], 1144 "primaryOutputId": 2 1145 }], 1146 "depSetOfFiles": [{ 1147 "id": 1, 1148 "directArtifactIds": [4, 3, 5] 1149 }], 1150 "pathFragments": [{ 1151 "id": 1, 1152 "label": "python_binary" 1153 },{ 1154 "id": 2, 1155 "label": "python_binary.zip" 1156 },{ 1157 "id": 3, 1158 "label": "python_binary.py" 1159 },{ 1160 "id": 9, 1161 "label": ".." 1162 }, { 1163 "id": 8, 1164 "label": "bazel_tools", 1165 "parentId": 9 1166 }, { 1167 "id": 7, 1168 "label": "tools", 1169 "parentId": 8 1170 }, { 1171 "id": 6, 1172 "label": "zip", 1173 "parentId": 7 1174 }, { 1175 "id": 5, 1176 "label": "zipper", 1177 "parentId": 6 1178 }, { 1179 "id": 4, 1180 "label": "zipper", 1181 "parentId": 5 1182 },{ 1183 "id": 16, 1184 "label": "bazel-out" 1185 },{ 1186 "id": 15, 1187 "label": "bazel_tools", 1188 "parentId": 16 1189 }, { 1190 "id": 14, 1191 "label": "k8-fastbuild", 1192 "parentId": 15 1193 }, { 1194 "id": 13, 1195 "label": "bin", 1196 "parentId": 14 1197 }, { 1198 "id": 12, 1199 "label": "tools", 1200 "parentId": 13 1201 }, { 1202 "id": 11, 1203 "label": "python", 1204 "parentId": 12 1205 }, { 1206 "id": 10, 1207 "label": "py3wrapper.sh", 1208 "parentId": 11 1209 },{ 1210 "id": 20, 1211 "label": "python_binary" 1212 }] 1213}` 1214 actual, err := AqueryBuildStatements([]byte(inputString)) 1215 1216 if err != nil { 1217 t.Errorf("Unexpected error %q", err) 1218 } 1219 1220 expectedBuildStatements := []BuildStatement{ 1221 BuildStatement{ 1222 Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > python_binary && " + 1223 "chmod a+x python_binary'", 1224 InputPaths: []string{"python_binary.zip"}, 1225 OutputPaths: []string{"python_binary"}, 1226 Mnemonic: "TemplateExpand", 1227 }, 1228 BuildStatement{ 1229 Command: "../bazel_tools/tools/zip/zipper/zipper cC python_binary.zip __main__.py=bazel-out/k8-fastbuild/bin/python_binary.temp " + 1230 "__init__.py= runfiles/__main__/__init__.py= runfiles/__main__/python_binary.py=python_binary.py && " + 1231 "../bazel_tools/tools/zip/zipper/zipper x python_binary.zip -d python_binary.runfiles && ln -sf runfiles/__main__ python_binary.runfiles", 1232 InputPaths: []string{"../bazel_tools/tools/zip/zipper/zipper", "python_binary.py"}, 1233 OutputPaths: []string{"python_binary.zip"}, 1234 Mnemonic: "PythonZipper", 1235 }, 1236 } 1237 assertBuildStatements(t, expectedBuildStatements, actual) 1238} 1239 1240func TestPythonZipperActionNoInput(t *testing.T) { 1241 const inputString = ` 1242{ 1243 "artifacts": [{ 1244 "id": 1, 1245 "pathFragmentId": 1 1246 },{ 1247 "id": 2, 1248 "pathFragmentId": 2 1249 }], 1250 "actions": [{ 1251 "targetId": 1, 1252 "actionKey": "x", 1253 "mnemonic": "PythonZipper", 1254 "configurationId": 1, 1255 "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"], 1256 "outputIds": [2], 1257 "primaryOutputId": 2 1258 }], 1259 "pathFragments": [{ 1260 "id": 1, 1261 "label": "python_binary" 1262 },{ 1263 "id": 2, 1264 "label": "python_binary.zip" 1265 }] 1266}` 1267 _, err := AqueryBuildStatements([]byte(inputString)) 1268 assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`) 1269} 1270 1271func TestPythonZipperActionNoOutput(t *testing.T) { 1272 const inputString = ` 1273{ 1274 "artifacts": [{ 1275 "id": 1, 1276 "pathFragmentId": 1 1277 },{ 1278 "id": 2, 1279 "pathFragmentId": 2 1280 },{ 1281 "id": 3, 1282 "pathFragmentId": 3 1283 },{ 1284 "id": 4, 1285 "pathFragmentId": 4 1286 },{ 1287 "id": 5, 1288 "pathFragmentId": 10 1289 }], 1290 "actions": [{ 1291 "targetId": 1, 1292 "actionKey": "x", 1293 "mnemonic": "PythonZipper", 1294 "configurationId": 1, 1295 "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"], 1296 "inputDepSetIds": [1] 1297 }], 1298 "depSetOfFiles": [{ 1299 "id": 1, 1300 "directArtifactIds": [4, 3, 5] 1301 }], 1302 "pathFragments": [{ 1303 "id": 1, 1304 "label": "python_binary" 1305 },{ 1306 "id": 2, 1307 "label": "python_binary.zip" 1308 },{ 1309 "id": 3, 1310 "label": "python_binary.py" 1311 },{ 1312 "id": 9, 1313 "label": ".." 1314 }, { 1315 "id": 8, 1316 "label": "bazel_tools", 1317 "parentId": 9 1318 }, { 1319 "id": 7, 1320 "label": "tools", 1321 "parentId": 8 1322 }, { 1323 "id": 6, 1324 "label": "zip", 1325 "parentId": 7 1326 }, { 1327 "id": 5, 1328 "label": "zipper", 1329 "parentId": 6 1330 }, { 1331 "id": 4, 1332 "label": "zipper", 1333 "parentId": 5 1334 },{ 1335 "id": 16, 1336 "label": "bazel-out" 1337 },{ 1338 "id": 15, 1339 "label": "bazel_tools", 1340 "parentId": 16 1341 }, { 1342 "id": 14, 1343 "label": "k8-fastbuild", 1344 "parentId": 15 1345 }, { 1346 "id": 13, 1347 "label": "bin", 1348 "parentId": 14 1349 }, { 1350 "id": 12, 1351 "label": "tools", 1352 "parentId": 13 1353 }, { 1354 "id": 11, 1355 "label": "python", 1356 "parentId": 12 1357 }, { 1358 "id": 10, 1359 "label": "py3wrapper.sh", 1360 "parentId": 11 1361 }] 1362}` 1363 _, err := AqueryBuildStatements([]byte(inputString)) 1364 assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["../bazel_tools/tools/zip/zipper/zipper" "python_binary.py"], output []`) 1365} 1366 1367func assertError(t *testing.T, err error, expected string) { 1368 t.Helper() 1369 if err == nil { 1370 t.Errorf("expected error '%s', but got no error", expected) 1371 } else if err.Error() != expected { 1372 t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error()) 1373 } 1374} 1375 1376// Asserts that the given actual build statements match the given expected build statements. 1377// Build statement equivalence is determined using buildStatementEquals. 1378func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) { 1379 t.Helper() 1380 if len(expected) != len(actual) { 1381 t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v", 1382 len(expected), len(actual), expected, actual) 1383 return 1384 } 1385ACTUAL_LOOP: 1386 for _, actualStatement := range actual { 1387 for _, expectedStatement := range expected { 1388 if buildStatementEquals(actualStatement, expectedStatement) { 1389 continue ACTUAL_LOOP 1390 } 1391 } 1392 t.Errorf("unexpected build statement %#v.\n expected: %#v", 1393 actualStatement, expected) 1394 return 1395 } 1396} 1397 1398func buildStatementEquals(first BuildStatement, second BuildStatement) bool { 1399 if first.Mnemonic != second.Mnemonic { 1400 return false 1401 } 1402 if first.Command != second.Command { 1403 return false 1404 } 1405 // Ordering is significant for environment variables. 1406 if !reflect.DeepEqual(first.Env, second.Env) { 1407 return false 1408 } 1409 // Ordering is irrelevant for input and output paths, so compare sets. 1410 if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) { 1411 return false 1412 } 1413 if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) { 1414 return false 1415 } 1416 if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) { 1417 return false 1418 } 1419 if first.Depfile != second.Depfile { 1420 return false 1421 } 1422 return true 1423} 1424 1425func stringSet(stringSlice []string) map[string]struct{} { 1426 stringMap := make(map[string]struct{}) 1427 for _, s := range stringSlice { 1428 stringMap[s] = struct{}{} 1429 } 1430 return stringMap 1431} 1432