github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/language/go/resolve_test.go (about) 1 /* Copyright 2018 The Bazel Authors. 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 16 package golang 17 18 import ( 19 "fmt" 20 "path/filepath" 21 "strings" 22 "testing" 23 24 "github.com/bazelbuild/bazel-gazelle/label" 25 "github.com/bazelbuild/bazel-gazelle/pathtools" 26 "github.com/bazelbuild/bazel-gazelle/repo" 27 "github.com/bazelbuild/bazel-gazelle/resolve" 28 "github.com/bazelbuild/bazel-gazelle/rule" 29 bzl "github.com/bazelbuild/buildtools/build" 30 "golang.org/x/tools/go/vcs" 31 ) 32 33 func TestResolveGo(t *testing.T) { 34 type buildFile struct { 35 rel, content string 36 } 37 type testCase struct { 38 desc string 39 index []buildFile 40 old buildFile 41 want string 42 skipIndex bool 43 namingConvention namingConvention 44 } 45 for _, tc := range []testCase{ 46 { 47 desc: "std", 48 index: []buildFile{{ 49 rel: "bad", 50 content: ` 51 go_library( 52 name = "go_default_library", 53 importpath = "fmt", 54 ) 55 `, 56 }}, 57 old: buildFile{ 58 content: ` 59 go_binary( 60 name = "dep", 61 _imports = ["fmt"], 62 ) 63 `, 64 }, 65 want: `go_binary(name = "dep")`, 66 }, { 67 desc: "self_import", 68 old: buildFile{content: ` 69 go_library( 70 name = "go_default_library", 71 importpath = "foo", 72 _imports = ["foo"], 73 ) 74 `}, 75 want: ` 76 go_library( 77 name = "go_default_library", 78 importpath = "foo", 79 ) 80 `, 81 }, { 82 desc: "self_import_embed", 83 old: buildFile{content: ` 84 go_library( 85 name = "a", 86 embeds = [":b"], 87 importpath = "x", 88 ) 89 90 go_library( 91 name = "b", 92 importpath = "x", 93 _imports = ["x"], 94 ) 95 `}, 96 want: ` 97 go_library( 98 name = "a", 99 embeds = [":b"], 100 importpath = "x", 101 ) 102 103 go_library( 104 name = "b", 105 importpath = "x", 106 ) 107 `, 108 }, { 109 desc: "override", 110 index: []buildFile{{ 111 content: ` 112 # gazelle:resolve go go github.com/golang/protobuf/ptypes //:good 113 go_library( 114 name = "bad", 115 importpath = "github.com/golang/protobuf/ptypes", 116 ) 117 `, 118 }}, 119 old: buildFile{ 120 rel: "test", 121 content: ` 122 go_library( 123 name = "a", 124 importpath = "a", 125 _imports = ["github.com/golang/protobuf/ptypes"], 126 ) 127 `, 128 }, 129 want: ` 130 go_library( 131 name = "a", 132 importpath = "a", 133 deps = ["//:good"], 134 ) 135 `, 136 }, { 137 desc: "same_package", 138 old: buildFile{content: ` 139 go_library( 140 name = "a", 141 importpath = "foo", 142 ) 143 144 go_binary( 145 name = "b", 146 _imports = ["foo"], 147 ) 148 `}, 149 want: ` 150 go_library( 151 name = "a", 152 importpath = "foo", 153 ) 154 155 go_binary( 156 name = "b", 157 deps = [":a"], 158 ) 159 `, 160 }, { 161 desc: "different_package", 162 index: []buildFile{{ 163 rel: "a", 164 content: ` 165 go_library( 166 name = "a_lib", 167 importpath = "aa", 168 ) 169 `, 170 }}, 171 old: buildFile{ 172 rel: "b", 173 content: ` 174 go_binary( 175 name = "bin", 176 _imports = ["aa"], 177 ) 178 `, 179 }, 180 want: ` 181 go_binary( 182 name = "bin", 183 deps = ["//a:a_lib"], 184 ) 185 `, 186 }, { 187 desc: "multiple_rules_ambiguous", 188 index: []buildFile{{ 189 rel: "foo", 190 content: ` 191 go_library( 192 name = "a", 193 importpath = "example.com/foo", 194 ) 195 196 go_library( 197 name = "b", 198 importpath = "example.com/foo", 199 ) 200 `, 201 }}, 202 old: buildFile{ 203 content: ` 204 go_binary( 205 name = "bin", 206 _imports = ["example.com/foo"], 207 ) 208 `, 209 }, 210 // an error should be reported, and no dependency should be emitted 211 want: `go_binary(name = "bin")`, 212 }, { 213 desc: "vendor_not_visible", 214 index: []buildFile{ 215 { 216 rel: "", 217 content: ` 218 go_library( 219 name = "root", 220 importpath = "example.com/foo", 221 ) 222 `, 223 }, { 224 rel: "a/vendor/foo", 225 content: ` 226 go_library( 227 name = "vendored", 228 importpath = "example.com/foo", 229 ) 230 `, 231 }, 232 }, 233 old: buildFile{ 234 rel: "b", 235 content: ` 236 go_binary( 237 name = "bin", 238 _imports = ["example.com/foo"], 239 ) 240 `, 241 }, 242 want: ` 243 go_binary( 244 name = "bin", 245 deps = ["//:root"], 246 ) 247 `, 248 }, { 249 desc: "vendor_supercedes_nonvendor", 250 index: []buildFile{ 251 { 252 rel: "", 253 content: ` 254 go_library( 255 name = "root", 256 importpath = "example.com/foo", 257 ) 258 `, 259 }, { 260 rel: "vendor/foo", 261 content: ` 262 go_library( 263 name = "vendored", 264 importpath = "example.com/foo", 265 ) 266 `, 267 }, 268 }, 269 old: buildFile{ 270 rel: "sub", 271 content: ` 272 go_binary( 273 name = "bin", 274 _imports = ["example.com/foo"], 275 ) 276 `, 277 }, 278 want: ` 279 go_binary( 280 name = "bin", 281 deps = ["//vendor/foo:vendored"], 282 ) 283 `, 284 }, { 285 desc: "deep_vendor_shallow_vendor", 286 index: []buildFile{ 287 { 288 rel: "shallow/vendor", 289 content: ` 290 go_library( 291 name = "shallow", 292 importpath = "example.com/foo", 293 ) 294 `, 295 }, { 296 rel: "shallow/deep/vendor", 297 content: ` 298 go_library( 299 name = "deep", 300 importpath = "example.com/foo", 301 ) 302 `, 303 }, 304 }, 305 old: buildFile{ 306 rel: "shallow/deep", 307 content: ` 308 go_binary( 309 name = "bin", 310 _imports = ["example.com/foo"], 311 ) 312 `, 313 }, 314 want: ` 315 go_binary( 316 name = "bin", 317 deps = ["//shallow/deep/vendor:deep"], 318 ) 319 `, 320 }, { 321 desc: "nested_vendor", 322 index: []buildFile{ 323 { 324 rel: "vendor/a", 325 content: ` 326 go_library( 327 name = "a", 328 importpath = "a", 329 ) 330 `, 331 }, { 332 rel: "vendor/b/vendor/a", 333 content: ` 334 go_library( 335 name = "a", 336 importpath = "a", 337 ) 338 `, 339 }, 340 }, 341 old: buildFile{ 342 rel: "vendor/b/c", 343 content: ` 344 go_binary( 345 name = "bin", 346 _imports = ["a"], 347 ) 348 `, 349 }, 350 want: ` 351 go_binary( 352 name = "bin", 353 deps = ["//vendor/b/vendor/a"], 354 ) 355 `, 356 }, { 357 desc: "skip_self_embed", 358 old: buildFile{ 359 content: ` 360 go_library( 361 name = "go_default_library", 362 srcs = ["lib.go"], 363 importpath = "example.com/repo/lib", 364 ) 365 366 go_test( 367 name = "go_default_test", 368 embed = [":go_default_library"], 369 _imports = ["example.com/repo/lib"], 370 ) 371 `, 372 }, 373 want: ` 374 go_library( 375 name = "go_default_library", 376 srcs = ["lib.go"], 377 importpath = "example.com/repo/lib", 378 ) 379 380 go_test( 381 name = "go_default_test", 382 embed = [":go_default_library"], 383 ) 384 `, 385 }, { 386 desc: "binary_embed", 387 old: buildFile{content: ` 388 go_library( 389 name = "a", 390 importpath = "a", 391 ) 392 393 go_library( 394 name = "b", 395 embed = [":a"], 396 ) 397 398 go_binary( 399 name = "c", 400 embed = [":a"], 401 importpath = "a", 402 ) 403 404 go_library( 405 name = "d", 406 _imports = ["a"], 407 ) 408 `}, 409 want: ` 410 go_library( 411 name = "a", 412 importpath = "a", 413 ) 414 415 go_library( 416 name = "b", 417 embed = [":a"], 418 ) 419 420 go_binary( 421 name = "c", 422 embed = [":a"], 423 importpath = "a", 424 ) 425 426 go_library( 427 name = "d", 428 deps = [":b"], 429 ) 430 `, 431 }, { 432 desc: "gazelle_special", 433 old: buildFile{content: ` 434 go_library( 435 name = "go_default_library", 436 _imports = [ 437 "github.com/bazelbuild/bazel-gazelle/language", 438 "github.com/bazelbuild/rules_go/go/tools/bazel", 439 ], 440 ) 441 `}, 442 want: ` 443 go_library( 444 name = "go_default_library", 445 deps = [ 446 "@bazel_gazelle//language:go_default_library", 447 "@io_bazel_rules_go//go/tools/bazel:go_default_library", 448 ], 449 ) 450 `, 451 }, { 452 desc: "local_unknown", 453 skipIndex: true, 454 old: buildFile{content: ` 455 go_binary( 456 name = "bin", 457 _imports = [ 458 "example.com/repo/resolve", 459 "example.com/repo/resolve/sub", 460 ], 461 ) 462 `}, 463 want: ` 464 go_binary( 465 name = "bin", 466 deps = [ 467 ":resolve", 468 "//sub", 469 ], 470 ) 471 `, 472 }, { 473 desc: "local_relative", 474 index: []buildFile{ 475 { 476 rel: "a", 477 content: ` 478 go_library( 479 name = "go_default_library", 480 importpath = "example.com/repo/resolve/a", 481 ) 482 `, 483 }, 484 { 485 rel: "a/b", 486 content: ` 487 go_library( 488 name = "go_default_library", 489 importpath = "example.com/repo/resolve/a/b", 490 ) 491 `, 492 }, 493 { 494 rel: "c", 495 content: ` 496 go_library( 497 name = "go_default_library", 498 importpath = "example.com/repo/resolve/c", 499 ) 500 `, 501 }, 502 }, 503 old: buildFile{ 504 rel: "a", 505 content: ` 506 go_binary( 507 name = "bin", 508 _imports = [ 509 ".", 510 "./b", 511 "../c", 512 ], 513 ) 514 `, 515 }, 516 want: ` 517 go_binary( 518 name = "bin", 519 deps = [ 520 ":go_default_library", 521 "//a/b:go_default_library", 522 "//c:go_default_library", 523 ], 524 ) 525 `, 526 }, { 527 desc: "vendor_no_index", 528 old: buildFile{content: ` 529 go_binary( 530 name = "bin", 531 _imports = ["example.com/outside/prefix"], 532 ) 533 `}, 534 want: ` 535 go_binary( 536 name = "bin", 537 deps = ["//vendor/example.com/outside/prefix"], 538 ) 539 `, 540 }, { 541 desc: "vendor with go_naming_convention=import", 542 namingConvention: importNamingConvention, 543 old: buildFile{content: ` 544 go_binary( 545 name = "bin", 546 _imports = ["example.com/outside/prefix"], 547 ) 548 `}, 549 want: ` 550 go_binary( 551 name = "bin", 552 deps = ["//vendor/example.com/outside/prefix"], 553 ) 554 `, 555 }, { 556 desc: "test_and_library_not_indexed", 557 index: []buildFile{{ 558 rel: "foo", 559 content: ` 560 go_test( 561 name = "go_default_test", 562 importpath = "example.com/foo", 563 ) 564 565 go_binary( 566 name = "cmd", 567 importpath = "example.com/foo", 568 ) 569 `, 570 }}, 571 old: buildFile{content: ` 572 go_binary( 573 name = "bin", 574 _imports = ["example.com/foo"], 575 ) 576 `}, 577 want: ` 578 go_binary( 579 name = "bin", 580 deps = ["//vendor/example.com/foo"], 581 )`, 582 }, { 583 desc: "proto_override", 584 index: []buildFile{{ 585 rel: "", 586 content: ` 587 # gazelle:resolve proto go google/rpc/status.proto :good 588 589 proto_library( 590 name = "bad_proto", 591 srcs = ["google/rpc/status.proto"], 592 ) 593 594 go_proto_library( 595 name = "bad_go_proto", 596 proto = ":bad_proto", 597 importpath = "bad", 598 ) 599 `, 600 }}, 601 old: buildFile{ 602 rel: "test", 603 content: ` 604 go_proto_library( 605 name = "dep_go_proto", 606 importpath = "test", 607 _imports = ["google/rpc/status.proto"], 608 ) 609 `, 610 }, 611 want: ` 612 go_proto_library( 613 name = "dep_go_proto", 614 importpath = "test", 615 deps = ["//:good"], 616 ) 617 `, 618 }, { 619 desc: "proto_override_regexp", 620 index: []buildFile{{ 621 rel: "", 622 content: ` 623 # gazelle:resolve_regexp proto go google/rpc/.*\.proto :good 624 625 proto_library( 626 name = "bad_proto", 627 srcs = ["google/rpc/status.proto"], 628 ) 629 630 go_proto_library( 631 name = "bad_go_proto", 632 proto = ":bad_proto", 633 importpath = "bad", 634 ) 635 `, 636 }}, 637 old: buildFile{ 638 rel: "test", 639 content: ` 640 go_proto_library( 641 name = "dep_go_proto", 642 importpath = "test", 643 _imports = [ 644 "google/rpc/status.proto", 645 "google/rpc/status1.proto", 646 "google/rpc/status2.proto", 647 ], 648 ) 649 `, 650 }, 651 want: ` 652 go_proto_library( 653 name = "dep_go_proto", 654 importpath = "test", 655 deps = ["//:good"], 656 ) 657 `, 658 }, { 659 desc: "proto_index", 660 index: []buildFile{{ 661 rel: "sub", 662 content: ` 663 proto_library( 664 name = "foo_proto", 665 srcs = ["bar.proto"], 666 ) 667 668 go_proto_library( 669 name = "foo_go_proto", 670 importpath = "example.com/foo", 671 proto = ":foo_proto", 672 ) 673 674 go_library( 675 name = "embed", 676 embed = [":foo_go_proto"], 677 importpath = "example.com/foo", 678 ) 679 `, 680 }}, 681 old: buildFile{content: ` 682 go_proto_library( 683 name = "dep_proto", 684 _imports = ["sub/bar.proto"], 685 ) 686 `}, 687 want: ` 688 go_proto_library( 689 name = "dep_proto", 690 deps = ["//sub:embed"], 691 ) 692 `, 693 }, { 694 desc: "proto_embed", 695 old: buildFile{content: ` 696 proto_library( 697 name = "foo_proto", 698 srcs = ["foo.proto"], 699 ) 700 701 go_proto_library( 702 name = "foo_go_proto", 703 importpath = "example.com/repo/foo", 704 proto = ":foo_proto", 705 ) 706 707 go_library( 708 name = "foo_embedder", 709 embed = [":foo_go_proto"], 710 ) 711 712 proto_library( 713 name = "bar_proto", 714 srcs = ["bar.proto"], 715 _imports = ["foo.proto"], 716 ) 717 718 go_proto_library( 719 name = "bar_go_proto", 720 proto = ":bar_proto", 721 _imports = ["foo.proto"], 722 ) 723 `}, 724 want: ` 725 proto_library( 726 name = "foo_proto", 727 srcs = ["foo.proto"], 728 ) 729 730 go_proto_library( 731 name = "foo_go_proto", 732 importpath = "example.com/repo/foo", 733 proto = ":foo_proto", 734 ) 735 736 go_library( 737 name = "foo_embedder", 738 embed = [":foo_go_proto"], 739 ) 740 741 proto_library( 742 name = "bar_proto", 743 srcs = ["bar.proto"], 744 deps = [":foo_proto"], 745 ) 746 747 go_proto_library( 748 name = "bar_go_proto", 749 proto = ":bar_proto", 750 deps = [":foo_embedder"], 751 ) 752 `, 753 }, { 754 desc: "proto_dedup", 755 index: []buildFile{{ 756 rel: "sub", 757 content: ` 758 proto_library( 759 name = "foo_proto", 760 srcs = [ 761 "a.proto", 762 "b.proto", 763 ], 764 ) 765 766 go_proto_library( 767 name = "foo_go_proto", 768 proto = ":foo_proto", 769 importpath = "sub", 770 ) 771 `, 772 }}, 773 old: buildFile{content: ` 774 go_proto_library( 775 name = "dep_proto", 776 _imports = [ 777 "sub/a.proto", 778 "sub/b.proto", 779 ], 780 ) 781 `}, 782 want: ` 783 go_proto_library( 784 name = "dep_proto", 785 deps = ["//sub:foo_go_proto"], 786 ) 787 `, 788 }, { 789 desc: "proto_wkt_cross_resolve", 790 old: buildFile{content: ` 791 go_proto_library( 792 name = "wkts_go_proto", 793 _imports = [ 794 "google/protobuf/any.proto", 795 "google/protobuf/api.proto", 796 "google/protobuf/compiler/plugin.proto", 797 "google/protobuf/descriptor.proto", 798 "google/protobuf/duration.proto", 799 "google/protobuf/empty.proto", 800 "google/protobuf/field_mask.proto", 801 "google/protobuf/source_context.proto", 802 "google/protobuf/struct.proto", 803 "google/protobuf/timestamp.proto", 804 "google/protobuf/type.proto", 805 "google/protobuf/wrappers.proto", 806 ], 807 ) 808 809 go_library( 810 name = "wkts_go_lib", 811 _imports = [ 812 "github.com/golang/protobuf/ptypes/any", 813 "google.golang.org/genproto/protobuf/api", 814 "github.com/golang/protobuf/protoc-gen-go/descriptor", 815 "github.com/golang/protobuf/ptypes/duration", 816 "github.com/golang/protobuf/ptypes/empty", 817 "google.golang.org/genproto/protobuf/field_mask", 818 "google.golang.org/genproto/protobuf/source_context", 819 "github.com/golang/protobuf/ptypes/struct", 820 "github.com/golang/protobuf/ptypes/timestamp", 821 "github.com/golang/protobuf/ptypes/wrappers", 822 "github.com/golang/protobuf/protoc-gen-go/plugin", 823 "google.golang.org/genproto/protobuf/ptype", 824 ], 825 ) 826 `}, 827 want: ` 828 go_proto_library(name = "wkts_go_proto") 829 830 go_library( 831 name = "wkts_go_lib", 832 deps = [ 833 "//vendor/github.com/golang/protobuf/protoc-gen-go/descriptor", 834 "//vendor/github.com/golang/protobuf/protoc-gen-go/plugin", 835 "//vendor/github.com/golang/protobuf/ptypes/any", 836 "//vendor/github.com/golang/protobuf/ptypes/duration", 837 "//vendor/github.com/golang/protobuf/ptypes/empty", 838 "//vendor/github.com/golang/protobuf/ptypes/struct", 839 "//vendor/github.com/golang/protobuf/ptypes/timestamp", 840 "//vendor/github.com/golang/protobuf/ptypes/wrappers", 841 "//vendor/google.golang.org/genproto/protobuf/api", 842 "//vendor/google.golang.org/genproto/protobuf/field_mask", 843 "//vendor/google.golang.org/genproto/protobuf/ptype", 844 "//vendor/google.golang.org/genproto/protobuf/source_context", 845 ], 846 ) 847 `, 848 }, { 849 desc: "proto_self_import", 850 old: buildFile{content: ` 851 proto_library( 852 name = "foo_proto", 853 srcs = [ 854 "a.proto", 855 "b.proto", 856 ], 857 ) 858 859 go_proto_library( 860 name = "foo_go_proto", 861 importpath = "foo", 862 proto = ":foo_proto", 863 _imports = ["a.proto"], 864 ) 865 866 go_library( 867 name = "go_default_library", 868 embed = [":foo_go_proto"], 869 importpath = "foo", 870 ) 871 `}, 872 want: ` 873 proto_library( 874 name = "foo_proto", 875 srcs = [ 876 "a.proto", 877 "b.proto", 878 ], 879 ) 880 881 go_proto_library( 882 name = "foo_go_proto", 883 importpath = "foo", 884 proto = ":foo_proto", 885 ) 886 887 go_library( 888 name = "go_default_library", 889 embed = [":foo_go_proto"], 890 importpath = "foo", 891 ) 892 `, 893 }, { 894 desc: "proto_import_prefix_and_strip_import_prefix", 895 index: []buildFile{{ 896 rel: "sub", 897 content: ` 898 proto_library( 899 name = "foo_proto", 900 srcs = ["bar.proto"], 901 import_prefix = "foo/", 902 strip_import_prefix = "/sub", 903 ) 904 905 go_proto_library( 906 name = "foo_go_proto", 907 importpath = "example.com/foo", 908 proto = ":foo_proto", 909 ) 910 911 go_library( 912 name = "embed", 913 embed = [":foo_go_proto"], 914 importpath = "example.com/foo", 915 ) 916 `, 917 }}, 918 old: buildFile{content: ` 919 go_proto_library( 920 name = "dep_proto", 921 _imports = ["foo/bar.proto"], 922 ) 923 `}, 924 want: ` 925 go_proto_library( 926 name = "dep_proto", 927 deps = ["//sub:embed"], 928 ) 929 `, 930 }, 931 } { 932 t.Run(tc.desc, func(t *testing.T) { 933 c, langs, cexts := testConfig( 934 t, 935 "-go_prefix=example.com/repo/resolve", 936 fmt.Sprintf("-go_naming_convention=%s", tc.namingConvention), 937 "-external=vendored", fmt.Sprintf("-index=%v", !tc.skipIndex)) 938 mrslv := make(mapResolver) 939 exts := make([]interface{}, 0, len(langs)) 940 for _, lang := range langs { 941 for kind := range lang.Kinds() { 942 mrslv[kind] = lang 943 } 944 exts = append(exts, lang) 945 } 946 ix := resolve.NewRuleIndex(mrslv.Resolver, exts...) 947 rc := testRemoteCache(nil) 948 949 for _, bf := range tc.index { 950 buildPath := filepath.Join(filepath.FromSlash(bf.rel), "BUILD.bazel") 951 f, err := rule.LoadData(buildPath, bf.rel, []byte(bf.content)) 952 if err != nil { 953 t.Fatal(err) 954 } 955 if bf.rel == "" { 956 for _, cext := range cexts { 957 cext.Configure(c, "", f) 958 } 959 } 960 for _, r := range f.Rules { 961 ix.AddRule(c, r, f) 962 } 963 } 964 buildPath := filepath.Join(filepath.FromSlash(tc.old.rel), "BUILD.bazel") 965 f, err := rule.LoadData(buildPath, tc.old.rel, []byte(tc.old.content)) 966 if err != nil { 967 t.Fatal(err) 968 } 969 imports := make([]interface{}, len(f.Rules)) 970 for i, r := range f.Rules { 971 imports[i] = convertImportsAttr(r) 972 ix.AddRule(c, r, f) 973 } 974 ix.Finish() 975 for i, r := range f.Rules { 976 mrslv.Resolver(r, "").Resolve(c, ix, rc, r, imports[i], label.New("", tc.old.rel, r.Name())) 977 } 978 f.Sync() 979 got := strings.TrimSpace(string(bzl.Format(f.File))) 980 want := strings.TrimSpace(tc.want) 981 if got != want { 982 t.Errorf("got:\n%s\nwant:\n%s", got, want) 983 } 984 }) 985 } 986 } 987 988 func TestResolveDisableGlobal(t *testing.T) { 989 c, langs, _ := testConfig( 990 t, 991 "-go_prefix=example.com/repo", 992 "-proto=disable_global") 993 exts := make([]interface{}, 0, len(langs)) 994 for _, lang := range langs { 995 exts = append(exts, lang) 996 } 997 ix := resolve.NewRuleIndex(nil, exts...) 998 ix.Finish() 999 rc := testRemoteCache([]repo.Repo{ 1000 { 1001 Name: "com_github_golang_protobuf", 1002 GoPrefix: "github.com/golang/protobuf", 1003 }, { 1004 Name: "org_golang_google_genproto", 1005 GoPrefix: "golang.org/google/genproto", 1006 }, 1007 }) 1008 gl := langs[1].(*goLang) 1009 oldContent := []byte(` 1010 go_library( 1011 name = "go_default_library", 1012 importpath = "foo", 1013 _imports = [ 1014 "github.com/golang/protobuf/ptypes/any", 1015 "google.golang.org/genproto/protobuf/api", 1016 "github.com/golang/protobuf/protoc-gen-go/descriptor", 1017 "github.com/golang/protobuf/ptypes/duration", 1018 "github.com/golang/protobuf/ptypes/empty", 1019 "google.golang.org/genproto/protobuf/field_mask", 1020 "google.golang.org/genproto/protobuf/source_context", 1021 "github.com/golang/protobuf/ptypes/struct", 1022 "github.com/golang/protobuf/ptypes/timestamp", 1023 "github.com/golang/protobuf/ptypes/wrappers", 1024 "github.com/golang/protobuf/protoc-gen-go/plugin", 1025 "google.golang.org/genproto/protobuf/ptype", 1026 "google.golang.org/genproto/googleapis/api/annotations", 1027 "google.golang.org/genproto/googleapis/rpc/status", 1028 "google.golang.org/genproto/googleapis/type/latlng", 1029 "github.com/golang/protobuf/jsonpb", 1030 "github.com/golang/protobuf/descriptor", 1031 "github.com/golang/protobuf/ptypes", 1032 ], 1033 ) 1034 `) 1035 f, err := rule.LoadData("BUILD.bazel", "", oldContent) 1036 if err != nil { 1037 t.Fatal(err) 1038 } 1039 for _, r := range f.Rules { 1040 imports := convertImportsAttr(r) 1041 gl.Resolve(c, ix, rc, r, imports, label.New("", "", r.Name())) 1042 } 1043 f.Sync() 1044 got := strings.TrimSpace(string(bzl.Format(f.File))) 1045 want := strings.TrimSpace(` 1046 go_library( 1047 name = "go_default_library", 1048 importpath = "foo", 1049 deps = [ 1050 "@com_github_golang_protobuf//descriptor:go_default_library", 1051 "@com_github_golang_protobuf//jsonpb:go_default_library", 1052 "@com_github_golang_protobuf//protoc-gen-go/descriptor:go_default_library", 1053 "@com_github_golang_protobuf//protoc-gen-go/plugin:go_default_library", 1054 "@com_github_golang_protobuf//ptypes:go_default_library", 1055 "@com_github_golang_protobuf//ptypes/any:go_default_library", 1056 "@com_github_golang_protobuf//ptypes/duration:go_default_library", 1057 "@com_github_golang_protobuf//ptypes/empty:go_default_library", 1058 "@com_github_golang_protobuf//ptypes/struct:go_default_library", 1059 "@com_github_golang_protobuf//ptypes/timestamp:go_default_library", 1060 "@com_github_golang_protobuf//ptypes/wrappers:go_default_library", 1061 "@org_golang_google_genproto//googleapis/api/annotations:go_default_library", 1062 "@org_golang_google_genproto//googleapis/rpc/status:go_default_library", 1063 "@org_golang_google_genproto//googleapis/type/latlng:go_default_library", 1064 "@org_golang_google_genproto//protobuf/api:go_default_library", 1065 "@org_golang_google_genproto//protobuf/field_mask:go_default_library", 1066 "@org_golang_google_genproto//protobuf/ptype:go_default_library", 1067 "@org_golang_google_genproto//protobuf/source_context:go_default_library", 1068 ], 1069 ) 1070 `) 1071 if got != want { 1072 t.Errorf("got:\n%s\nwant:%s", got, want) 1073 } 1074 } 1075 1076 func TestResolveExternal(t *testing.T) { 1077 c, langs, _ := testConfig( 1078 t, 1079 "-go_prefix=example.com/local") 1080 gc := getGoConfig(c) 1081 ix := resolve.NewRuleIndex(nil) 1082 ix.Finish() 1083 gl := langs[1].(*goLang) 1084 for _, tc := range []struct { 1085 desc, importpath string 1086 repos []repo.Repo 1087 moduleMode bool 1088 depMode dependencyMode 1089 namingConvention namingConvention 1090 namingConventionExternal namingConvention 1091 repoNamingConvention map[string]namingConvention 1092 want string 1093 }{ 1094 { 1095 desc: "top", 1096 importpath: "example.com/repo", 1097 want: "@com_example_repo//:go_default_library", 1098 }, { 1099 desc: "top_import_naming_convention", 1100 namingConvention: goDefaultLibraryNamingConvention, 1101 repoNamingConvention: map[string]namingConvention{ 1102 "com_example_repo": importNamingConvention, 1103 }, 1104 importpath: "example.com/repo", 1105 want: "@com_example_repo//:repo", 1106 }, { 1107 desc: "top_import_alias_naming_convention", 1108 namingConvention: goDefaultLibraryNamingConvention, 1109 repoNamingConvention: map[string]namingConvention{ 1110 "com_example_repo": importAliasNamingConvention, 1111 }, 1112 importpath: "example.com/repo", 1113 want: "@com_example_repo//:go_default_library", 1114 }, { 1115 desc: "sub", 1116 importpath: "example.com/repo/lib", 1117 want: "@com_example_repo//lib:go_default_library", 1118 }, { 1119 desc: "sub_import_alias_naming_convention", 1120 namingConvention: importNamingConvention, 1121 repoNamingConvention: map[string]namingConvention{ 1122 "com_example_repo": importAliasNamingConvention, 1123 }, 1124 importpath: "example.com/repo/lib", 1125 want: "@com_example_repo//lib", 1126 }, { 1127 desc: "custom_repo", 1128 repos: []repo.Repo{{ 1129 Name: "custom_repo_name", 1130 GoPrefix: "example.com/repo", 1131 }}, 1132 importpath: "example.com/repo/lib", 1133 want: "@custom_repo_name//lib:go_default_library", 1134 }, { 1135 desc: "custom_repo_import_naming_convention", 1136 repos: []repo.Repo{{ 1137 Name: "custom_repo_name", 1138 GoPrefix: "example.com/repo", 1139 }}, 1140 repoNamingConvention: map[string]namingConvention{ 1141 "custom_repo_name": importNamingConvention, 1142 }, 1143 importpath: "example.com/repo/lib", 1144 want: "@custom_repo_name//lib", 1145 }, { 1146 desc: "custom_repo_naming_convention_extern_import", 1147 repos: []repo.Repo{{ 1148 Name: "custom_repo_name", 1149 GoPrefix: "example.com/repo", 1150 }}, 1151 namingConventionExternal: importNamingConvention, 1152 importpath: "example.com/repo/lib", 1153 want: "@custom_repo_name//lib", 1154 }, { 1155 desc: "custom_repo_naming_convention_extern_default", 1156 repos: []repo.Repo{{ 1157 Name: "custom_repo_name", 1158 GoPrefix: "example.com/repo", 1159 }}, 1160 namingConventionExternal: goDefaultLibraryNamingConvention, 1161 importpath: "example.com/repo/lib", 1162 want: "@custom_repo_name//lib:go_default_library", 1163 }, { 1164 desc: "qualified", 1165 importpath: "example.com/repo.git/lib", 1166 want: "@com_example_repo_git//lib:go_default_library", 1167 }, { 1168 desc: "domain", 1169 importpath: "example.com/lib", 1170 want: "@com_example//lib:go_default_library", 1171 }, { 1172 desc: "same_prefix", 1173 importpath: "example.com/local/ext", 1174 repos: []repo.Repo{{ 1175 Name: "local_ext", 1176 GoPrefix: "example.com/local/ext", 1177 }}, 1178 want: "@local_ext//:go_default_library", 1179 }, { 1180 desc: "module_mode_unknown", 1181 importpath: "example.com/repo/v2/foo", 1182 moduleMode: true, 1183 want: "@com_example_repo_v2//foo:go_default_library", 1184 }, { 1185 desc: "module_mode_known", 1186 importpath: "example.com/repo/v2/foo", 1187 repos: []repo.Repo{{ 1188 Name: "custom_repo", 1189 GoPrefix: "example.com/repo", 1190 }}, 1191 moduleMode: true, 1192 want: "@custom_repo//v2/foo:go_default_library", 1193 }, { 1194 desc: "min_module_compat", 1195 importpath: "example.com/foo", 1196 repos: []repo.Repo{{ 1197 Name: "com_example_foo_v2", 1198 GoPrefix: "example.com/foo/v2", 1199 }}, 1200 moduleMode: true, 1201 want: "@com_example_foo_v2//:go_default_library", 1202 }, { 1203 desc: "min_module_compat_both", 1204 importpath: "example.com/foo", 1205 repos: []repo.Repo{ 1206 { 1207 Name: "com_example_foo", 1208 GoPrefix: "example.com/foo", 1209 }, { 1210 Name: "com_example_foo_v2", 1211 GoPrefix: "example.com/foo/v2", 1212 }, 1213 }, 1214 moduleMode: true, 1215 want: "@com_example_foo//:go_default_library", 1216 }, { 1217 desc: "static_mode_known", 1218 importpath: "example.com/repo/v2/foo", 1219 repos: []repo.Repo{{ 1220 Name: "custom_repo", 1221 GoPrefix: "example.com/repo", 1222 }}, 1223 depMode: staticMode, 1224 want: "@custom_repo//v2/foo:go_default_library", 1225 }, { 1226 desc: "static_mode_unknown", 1227 importpath: "example.com/repo/v2/foo", 1228 depMode: staticMode, 1229 want: "", 1230 }, 1231 } { 1232 t.Run(tc.desc, func(t *testing.T) { 1233 gc.depMode = tc.depMode 1234 gc.moduleMode = tc.moduleMode 1235 gc.goNamingConvention = tc.namingConvention 1236 gc.goNamingConventionExternal = tc.namingConventionExternal 1237 gc.repoNamingConvention = tc.repoNamingConvention 1238 rc := testRemoteCache(tc.repos) 1239 r := rule.NewRule("go_library", "x") 1240 imports := rule.PlatformStrings{Generic: []string{tc.importpath}} 1241 gl.Resolve(c, ix, rc, r, imports, label.New("", "", "x")) 1242 deps := r.AttrStrings("deps") 1243 if tc.want == "" { 1244 if len(deps) != 0 { 1245 t.Fatalf("deps: got %d; want 0", len(deps)) 1246 } 1247 return 1248 } else if len(deps) != 1 { 1249 t.Fatalf("deps: got %d; want 1", len(deps)) 1250 } 1251 if deps[0] != tc.want { 1252 t.Errorf("got %s; want %s", deps[0], tc.want) 1253 } 1254 }) 1255 } 1256 } 1257 1258 func testRemoteCache(knownRepos []repo.Repo) *repo.RemoteCache { 1259 rc, _ := repo.NewRemoteCache(knownRepos) 1260 rc.RepoRootForImportPath = stubRepoRootForImportPath 1261 rc.HeadCmd = func(_, _ string) (string, error) { 1262 return "", fmt.Errorf("HeadCmd not supported in test") 1263 } 1264 rc.ModInfo = stubModInfo 1265 return rc 1266 } 1267 1268 // stubRepoRootForImportPath is a stub implementation of vcs.RepoRootForImportPath 1269 func stubRepoRootForImportPath(importPath string, verbose bool) (*vcs.RepoRoot, error) { 1270 if pathtools.HasPrefix(importPath, "example.com/repo.git") { 1271 return &vcs.RepoRoot{ 1272 VCS: vcs.ByCmd("git"), 1273 Repo: "https://example.com/repo.git", 1274 Root: "example.com/repo.git", 1275 }, nil 1276 } 1277 1278 if pathtools.HasPrefix(importPath, "example.com/repo") { 1279 return &vcs.RepoRoot{ 1280 VCS: vcs.ByCmd("git"), 1281 Repo: "https://example.com/repo.git", 1282 Root: "example.com/repo", 1283 }, nil 1284 } 1285 1286 if pathtools.HasPrefix(importPath, "example.com") { 1287 return &vcs.RepoRoot{ 1288 VCS: vcs.ByCmd("git"), 1289 Repo: "https://example.com", 1290 Root: "example.com", 1291 }, nil 1292 } 1293 1294 return nil, fmt.Errorf("could not resolve import path: %q", importPath) 1295 } 1296 1297 // stubModInfo is a stub implementation of RemoteCache.ModInfo. 1298 func stubModInfo(importPath string) (string, error) { 1299 if pathtools.HasPrefix(importPath, "example.com/repo/v2") { 1300 return "example.com/repo/v2", nil 1301 } 1302 if pathtools.HasPrefix(importPath, "example.com/repo") { 1303 return "example.com/repo", nil 1304 } 1305 return "", fmt.Errorf("could not find module for import path: %q", importPath) 1306 } 1307 1308 func convertImportsAttr(r *rule.Rule) interface{} { 1309 kind := r.Kind() 1310 value := r.AttrStrings("_imports") 1311 r.DelAttr("_imports") 1312 if _, ok := goKinds[kind]; ok { 1313 return rule.PlatformStrings{Generic: value} 1314 } else { 1315 // proto_library 1316 return value 1317 } 1318 } 1319 1320 type mapResolver map[string]resolve.Resolver 1321 1322 func (mr mapResolver) Resolver(r *rule.Rule, f string) resolve.Resolver { 1323 return mr[r.Kind()] 1324 }