...

Source file src/github.com/bazelbuild/bazel-gazelle/language/go/resolve_test.go

Documentation: github.com/bazelbuild/bazel-gazelle/language/go

     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          "@com_github_golang_protobuf//protoc-gen-go/descriptor",
   834          "@com_github_golang_protobuf//protoc-gen-go/plugin",
   835          "@com_github_golang_protobuf//ptypes/any",
   836          "@com_github_golang_protobuf//ptypes/duration",
   837          "@com_github_golang_protobuf//ptypes/empty",
   838          "@com_github_golang_protobuf//ptypes/struct",
   839          "@com_github_golang_protobuf//ptypes/timestamp",
   840          "@com_github_golang_protobuf//ptypes/wrappers",
   841          "@org_golang_google_genproto//protobuf/api",
   842          "@org_golang_google_genproto//protobuf/field_mask",
   843          "@org_golang_google_genproto//protobuf/ptype",
   844          "@org_golang_google_genproto//protobuf/source_context",
   845      ],
   846  )
   847  `,
   848  		}, {
   849  			desc: "proto_special_cross_resolve",
   850  			old: buildFile{content: `
   851  go_library(
   852      name = "go_default_library",
   853      _imports = [
   854          "github.com/golang/protobuf/proto",
   855          "github.com/golang/protobuf/jsonpb",
   856          "github.com/golang/protobuf/descriptor",
   857          "github.com/golang/protobuf/protoc-gen-go/generator",
   858          "github.com/golang/protobuf/ptypes",
   859          "google.golang.org/grpc",
   860      ],
   861  )
   862  `},
   863  			want: `
   864  go_library(
   865      name = "go_default_library",
   866      deps = [
   867          "@com_github_golang_protobuf//descriptor:go_default_library_gen",
   868          "@com_github_golang_protobuf//jsonpb:go_default_library_gen",
   869          "@com_github_golang_protobuf//proto:go_default_library",
   870          "@com_github_golang_protobuf//protoc-gen-go/generator:go_default_library_gen",
   871          "@com_github_golang_protobuf//ptypes:go_default_library_gen",
   872          "@org_golang_google_grpc//:go_default_library",
   873      ],
   874  )
   875  `,
   876  		}, {
   877  			desc: "proto_self_import",
   878  			old: buildFile{content: `
   879  proto_library(
   880      name = "foo_proto",
   881      srcs = [
   882          "a.proto",
   883          "b.proto",
   884      ],
   885  )
   886  
   887  go_proto_library(
   888      name = "foo_go_proto",
   889      importpath = "foo",
   890      proto = ":foo_proto",
   891      _imports = ["a.proto"],
   892  )
   893  
   894  go_library(
   895      name = "go_default_library",
   896      embed = [":foo_go_proto"],
   897      importpath = "foo",
   898  )
   899  `},
   900  			want: `
   901  proto_library(
   902      name = "foo_proto",
   903      srcs = [
   904          "a.proto",
   905          "b.proto",
   906      ],
   907  )
   908  
   909  go_proto_library(
   910      name = "foo_go_proto",
   911      importpath = "foo",
   912      proto = ":foo_proto",
   913  )
   914  
   915  go_library(
   916      name = "go_default_library",
   917      embed = [":foo_go_proto"],
   918      importpath = "foo",
   919  )
   920  `,
   921  		}, {
   922  			desc: "proto_import_prefix_and_strip_import_prefix",
   923  			index: []buildFile{{
   924  				rel: "sub",
   925  				content: `
   926  proto_library(
   927      name = "foo_proto",
   928      srcs = ["bar.proto"],
   929      import_prefix = "foo/",
   930      strip_import_prefix = "/sub",
   931  )
   932  
   933  go_proto_library(
   934      name = "foo_go_proto",
   935      importpath = "example.com/foo",
   936      proto = ":foo_proto",
   937  )
   938  
   939  go_library(
   940      name = "embed",
   941      embed = [":foo_go_proto"],
   942      importpath = "example.com/foo",
   943  )
   944  `,
   945  			}},
   946  			old: buildFile{content: `
   947  go_proto_library(
   948      name = "dep_proto",
   949      _imports = ["foo/bar.proto"],
   950  )
   951  `},
   952  			want: `
   953  go_proto_library(
   954      name = "dep_proto",
   955      deps = ["//sub:embed"],
   956  )
   957  `,
   958  		},
   959  	} {
   960  		t.Run(tc.desc, func(t *testing.T) {
   961  			c, langs, cexts := testConfig(
   962  				t,
   963  				"-go_prefix=example.com/repo/resolve",
   964  				fmt.Sprintf("-go_naming_convention=%s", tc.namingConvention),
   965  				"-external=vendored", fmt.Sprintf("-index=%v", !tc.skipIndex))
   966  			mrslv := make(mapResolver)
   967  			exts := make([]interface{}, 0, len(langs))
   968  			for _, lang := range langs {
   969  				for kind := range lang.Kinds() {
   970  					mrslv[kind] = lang
   971  				}
   972  				exts = append(exts, lang)
   973  			}
   974  			ix := resolve.NewRuleIndex(mrslv.Resolver, exts...)
   975  			rc := testRemoteCache(nil)
   976  
   977  			for _, bf := range tc.index {
   978  				buildPath := filepath.Join(filepath.FromSlash(bf.rel), "BUILD.bazel")
   979  				f, err := rule.LoadData(buildPath, bf.rel, []byte(bf.content))
   980  				if err != nil {
   981  					t.Fatal(err)
   982  				}
   983  				if bf.rel == "" {
   984  					for _, cext := range cexts {
   985  						cext.Configure(c, "", f)
   986  					}
   987  				}
   988  				for _, r := range f.Rules {
   989  					ix.AddRule(c, r, f)
   990  				}
   991  			}
   992  			buildPath := filepath.Join(filepath.FromSlash(tc.old.rel), "BUILD.bazel")
   993  			f, err := rule.LoadData(buildPath, tc.old.rel, []byte(tc.old.content))
   994  			if err != nil {
   995  				t.Fatal(err)
   996  			}
   997  			imports := make([]interface{}, len(f.Rules))
   998  			for i, r := range f.Rules {
   999  				imports[i] = convertImportsAttr(r)
  1000  				ix.AddRule(c, r, f)
  1001  			}
  1002  			ix.Finish()
  1003  			for i, r := range f.Rules {
  1004  				mrslv.Resolver(r, "").Resolve(c, ix, rc, r, imports[i], label.New("", tc.old.rel, r.Name()))
  1005  			}
  1006  			f.Sync()
  1007  			got := strings.TrimSpace(string(bzl.Format(f.File)))
  1008  			want := strings.TrimSpace(tc.want)
  1009  			if got != want {
  1010  				t.Errorf("got:\n%s\nwant:\n%s", got, want)
  1011  			}
  1012  		})
  1013  	}
  1014  }
  1015  
  1016  func TestResolveDisableGlobal(t *testing.T) {
  1017  	c, langs, _ := testConfig(
  1018  		t,
  1019  		"-go_prefix=example.com/repo",
  1020  		"-proto=disable_global")
  1021  	exts := make([]interface{}, 0, len(langs))
  1022  	for _, lang := range langs {
  1023  		exts = append(exts, lang)
  1024  	}
  1025  	ix := resolve.NewRuleIndex(nil, exts...)
  1026  	ix.Finish()
  1027  	rc := testRemoteCache([]repo.Repo{
  1028  		{
  1029  			Name:     "com_github_golang_protobuf",
  1030  			GoPrefix: "github.com/golang/protobuf",
  1031  		}, {
  1032  			Name:     "org_golang_google_genproto",
  1033  			GoPrefix: "golang.org/google/genproto",
  1034  		},
  1035  	})
  1036  	gl := langs[1].(*goLang)
  1037  	oldContent := []byte(`
  1038  go_library(
  1039      name = "go_default_library",
  1040      importpath = "foo",
  1041      _imports = [
  1042          "github.com/golang/protobuf/ptypes/any",
  1043          "google.golang.org/genproto/protobuf/api",
  1044          "github.com/golang/protobuf/protoc-gen-go/descriptor",
  1045          "github.com/golang/protobuf/ptypes/duration",
  1046          "github.com/golang/protobuf/ptypes/empty",
  1047          "google.golang.org/genproto/protobuf/field_mask",
  1048          "google.golang.org/genproto/protobuf/source_context",
  1049          "github.com/golang/protobuf/ptypes/struct",
  1050          "github.com/golang/protobuf/ptypes/timestamp",
  1051          "github.com/golang/protobuf/ptypes/wrappers",
  1052          "github.com/golang/protobuf/protoc-gen-go/plugin",
  1053          "google.golang.org/genproto/protobuf/ptype",
  1054          "google.golang.org/genproto/googleapis/api/annotations",
  1055          "google.golang.org/genproto/googleapis/rpc/status",
  1056          "google.golang.org/genproto/googleapis/type/latlng",
  1057          "github.com/golang/protobuf/jsonpb",
  1058          "github.com/golang/protobuf/descriptor",
  1059          "github.com/golang/protobuf/ptypes",
  1060      ],
  1061  )
  1062  `)
  1063  	f, err := rule.LoadData("BUILD.bazel", "", oldContent)
  1064  	if err != nil {
  1065  		t.Fatal(err)
  1066  	}
  1067  	for _, r := range f.Rules {
  1068  		imports := convertImportsAttr(r)
  1069  		gl.Resolve(c, ix, rc, r, imports, label.New("", "", r.Name()))
  1070  	}
  1071  	f.Sync()
  1072  	got := strings.TrimSpace(string(bzl.Format(f.File)))
  1073  	want := strings.TrimSpace(`
  1074  go_library(
  1075      name = "go_default_library",
  1076      importpath = "foo",
  1077      deps = [
  1078          "@com_github_golang_protobuf//descriptor:go_default_library",
  1079          "@com_github_golang_protobuf//jsonpb:go_default_library",
  1080          "@com_github_golang_protobuf//protoc-gen-go/descriptor:go_default_library",
  1081          "@com_github_golang_protobuf//protoc-gen-go/plugin:go_default_library",
  1082          "@com_github_golang_protobuf//ptypes:go_default_library",
  1083          "@com_github_golang_protobuf//ptypes/any:go_default_library",
  1084          "@com_github_golang_protobuf//ptypes/duration:go_default_library",
  1085          "@com_github_golang_protobuf//ptypes/empty:go_default_library",
  1086          "@com_github_golang_protobuf//ptypes/struct:go_default_library",
  1087          "@com_github_golang_protobuf//ptypes/timestamp:go_default_library",
  1088          "@com_github_golang_protobuf//ptypes/wrappers:go_default_library",
  1089          "@org_golang_google_genproto//googleapis/api/annotations:go_default_library",
  1090          "@org_golang_google_genproto//googleapis/rpc/status:go_default_library",
  1091          "@org_golang_google_genproto//googleapis/type/latlng:go_default_library",
  1092          "@org_golang_google_genproto//protobuf/api:go_default_library",
  1093          "@org_golang_google_genproto//protobuf/field_mask:go_default_library",
  1094          "@org_golang_google_genproto//protobuf/ptype:go_default_library",
  1095          "@org_golang_google_genproto//protobuf/source_context:go_default_library",
  1096      ],
  1097  )
  1098  `)
  1099  	if got != want {
  1100  		t.Errorf("got:\n%s\nwant:%s", got, want)
  1101  	}
  1102  }
  1103  
  1104  func TestResolveExternal(t *testing.T) {
  1105  	c, langs, _ := testConfig(
  1106  		t,
  1107  		"-go_prefix=example.com/local")
  1108  	gc := getGoConfig(c)
  1109  	ix := resolve.NewRuleIndex(nil)
  1110  	ix.Finish()
  1111  	gl := langs[1].(*goLang)
  1112  	for _, tc := range []struct {
  1113  		desc, importpath         string
  1114  		repos                    []repo.Repo
  1115  		moduleMode               bool
  1116  		depMode                  dependencyMode
  1117  		namingConvention         namingConvention
  1118  		namingConventionExternal namingConvention
  1119  		repoNamingConvention     map[string]namingConvention
  1120  		want                     string
  1121  	}{
  1122  		{
  1123  			desc:       "top",
  1124  			importpath: "example.com/repo",
  1125  			want:       "@com_example_repo//:go_default_library",
  1126  		}, {
  1127  			desc:             "top_import_naming_convention",
  1128  			namingConvention: goDefaultLibraryNamingConvention,
  1129  			repoNamingConvention: map[string]namingConvention{
  1130  				"com_example_repo": importNamingConvention,
  1131  			},
  1132  			importpath: "example.com/repo",
  1133  			want:       "@com_example_repo//:repo",
  1134  		}, {
  1135  			desc:             "top_import_alias_naming_convention",
  1136  			namingConvention: goDefaultLibraryNamingConvention,
  1137  			repoNamingConvention: map[string]namingConvention{
  1138  				"com_example_repo": importAliasNamingConvention,
  1139  			},
  1140  			importpath: "example.com/repo",
  1141  			want:       "@com_example_repo//:go_default_library",
  1142  		}, {
  1143  			desc:       "sub",
  1144  			importpath: "example.com/repo/lib",
  1145  			want:       "@com_example_repo//lib:go_default_library",
  1146  		}, {
  1147  			desc:             "sub_import_alias_naming_convention",
  1148  			namingConvention: importNamingConvention,
  1149  			repoNamingConvention: map[string]namingConvention{
  1150  				"com_example_repo": importAliasNamingConvention,
  1151  			},
  1152  			importpath: "example.com/repo/lib",
  1153  			want:       "@com_example_repo//lib",
  1154  		}, {
  1155  			desc: "custom_repo",
  1156  			repos: []repo.Repo{{
  1157  				Name:     "custom_repo_name",
  1158  				GoPrefix: "example.com/repo",
  1159  			}},
  1160  			importpath: "example.com/repo/lib",
  1161  			want:       "@custom_repo_name//lib:go_default_library",
  1162  		}, {
  1163  			desc: "custom_repo_import_naming_convention",
  1164  			repos: []repo.Repo{{
  1165  				Name:     "custom_repo_name",
  1166  				GoPrefix: "example.com/repo",
  1167  			}},
  1168  			repoNamingConvention: map[string]namingConvention{
  1169  				"custom_repo_name": importNamingConvention,
  1170  			},
  1171  			importpath: "example.com/repo/lib",
  1172  			want:       "@custom_repo_name//lib",
  1173  		}, {
  1174  			desc: "custom_repo_naming_convention_extern_import",
  1175  			repos: []repo.Repo{{
  1176  				Name:     "custom_repo_name",
  1177  				GoPrefix: "example.com/repo",
  1178  			}},
  1179  			namingConventionExternal: importNamingConvention,
  1180  			importpath:               "example.com/repo/lib",
  1181  			want:                     "@custom_repo_name//lib",
  1182  		}, {
  1183  			desc: "custom_repo_naming_convention_extern_default",
  1184  			repos: []repo.Repo{{
  1185  				Name:     "custom_repo_name",
  1186  				GoPrefix: "example.com/repo",
  1187  			}},
  1188  			namingConventionExternal: goDefaultLibraryNamingConvention,
  1189  			importpath:               "example.com/repo/lib",
  1190  			want:                     "@custom_repo_name//lib:go_default_library",
  1191  		}, {
  1192  			desc:       "qualified",
  1193  			importpath: "example.com/repo.git/lib",
  1194  			want:       "@com_example_repo_git//lib:go_default_library",
  1195  		}, {
  1196  			desc:       "domain",
  1197  			importpath: "example.com/lib",
  1198  			want:       "@com_example//lib:go_default_library",
  1199  		}, {
  1200  			desc:       "same_prefix",
  1201  			importpath: "example.com/local/ext",
  1202  			repos: []repo.Repo{{
  1203  				Name:     "local_ext",
  1204  				GoPrefix: "example.com/local/ext",
  1205  			}},
  1206  			want: "@local_ext//:go_default_library",
  1207  		}, {
  1208  			desc:       "module_mode_unknown",
  1209  			importpath: "example.com/repo/v2/foo",
  1210  			moduleMode: true,
  1211  			want:       "@com_example_repo_v2//foo:go_default_library",
  1212  		}, {
  1213  			desc:       "module_mode_known",
  1214  			importpath: "example.com/repo/v2/foo",
  1215  			repos: []repo.Repo{{
  1216  				Name:     "custom_repo",
  1217  				GoPrefix: "example.com/repo",
  1218  			}},
  1219  			moduleMode: true,
  1220  			want:       "@custom_repo//v2/foo:go_default_library",
  1221  		}, {
  1222  			desc:       "min_module_compat",
  1223  			importpath: "example.com/foo",
  1224  			repos: []repo.Repo{{
  1225  				Name:     "com_example_foo_v2",
  1226  				GoPrefix: "example.com/foo/v2",
  1227  			}},
  1228  			moduleMode: true,
  1229  			want:       "@com_example_foo_v2//:go_default_library",
  1230  		}, {
  1231  			desc:       "min_module_compat_both",
  1232  			importpath: "example.com/foo",
  1233  			repos: []repo.Repo{
  1234  				{
  1235  					Name:     "com_example_foo",
  1236  					GoPrefix: "example.com/foo",
  1237  				}, {
  1238  					Name:     "com_example_foo_v2",
  1239  					GoPrefix: "example.com/foo/v2",
  1240  				},
  1241  			},
  1242  			moduleMode: true,
  1243  			want:       "@com_example_foo//:go_default_library",
  1244  		}, {
  1245  			desc:       "static_mode_known",
  1246  			importpath: "example.com/repo/v2/foo",
  1247  			repos: []repo.Repo{{
  1248  				Name:     "custom_repo",
  1249  				GoPrefix: "example.com/repo",
  1250  			}},
  1251  			depMode: staticMode,
  1252  			want:    "@custom_repo//v2/foo:go_default_library",
  1253  		}, {
  1254  			desc:       "static_mode_unknown",
  1255  			importpath: "example.com/repo/v2/foo",
  1256  			depMode:    staticMode,
  1257  			want:       "",
  1258  		},
  1259  	} {
  1260  		t.Run(tc.desc, func(t *testing.T) {
  1261  			gc.depMode = tc.depMode
  1262  			gc.moduleMode = tc.moduleMode
  1263  			gc.goNamingConvention = tc.namingConvention
  1264  			gc.goNamingConventionExternal = tc.namingConventionExternal
  1265  			gc.repoNamingConvention = tc.repoNamingConvention
  1266  			rc := testRemoteCache(tc.repos)
  1267  			r := rule.NewRule("go_library", "x")
  1268  			imports := rule.PlatformStrings{Generic: []string{tc.importpath}}
  1269  			gl.Resolve(c, ix, rc, r, imports, label.New("", "", "x"))
  1270  			deps := r.AttrStrings("deps")
  1271  			if tc.want == "" {
  1272  				if len(deps) != 0 {
  1273  					t.Fatalf("deps: got %d; want 0", len(deps))
  1274  				}
  1275  				return
  1276  			} else if len(deps) != 1 {
  1277  				t.Fatalf("deps: got %d; want 1", len(deps))
  1278  			}
  1279  			if deps[0] != tc.want {
  1280  				t.Errorf("got %s; want %s", deps[0], tc.want)
  1281  			}
  1282  		})
  1283  	}
  1284  }
  1285  
  1286  func testRemoteCache(knownRepos []repo.Repo) *repo.RemoteCache {
  1287  	rc, _ := repo.NewRemoteCache(knownRepos)
  1288  	rc.RepoRootForImportPath = stubRepoRootForImportPath
  1289  	rc.HeadCmd = func(_, _ string) (string, error) {
  1290  		return "", fmt.Errorf("HeadCmd not supported in test")
  1291  	}
  1292  	rc.ModInfo = stubModInfo
  1293  	return rc
  1294  }
  1295  
  1296  // stubRepoRootForImportPath is a stub implementation of vcs.RepoRootForImportPath
  1297  func stubRepoRootForImportPath(importPath string, verbose bool) (*vcs.RepoRoot, error) {
  1298  	if pathtools.HasPrefix(importPath, "example.com/repo.git") {
  1299  		return &vcs.RepoRoot{
  1300  			VCS:  vcs.ByCmd("git"),
  1301  			Repo: "https://example.com/repo.git",
  1302  			Root: "example.com/repo.git",
  1303  		}, nil
  1304  	}
  1305  
  1306  	if pathtools.HasPrefix(importPath, "example.com/repo") {
  1307  		return &vcs.RepoRoot{
  1308  			VCS:  vcs.ByCmd("git"),
  1309  			Repo: "https://example.com/repo.git",
  1310  			Root: "example.com/repo",
  1311  		}, nil
  1312  	}
  1313  
  1314  	if pathtools.HasPrefix(importPath, "example.com") {
  1315  		return &vcs.RepoRoot{
  1316  			VCS:  vcs.ByCmd("git"),
  1317  			Repo: "https://example.com",
  1318  			Root: "example.com",
  1319  		}, nil
  1320  	}
  1321  
  1322  	return nil, fmt.Errorf("could not resolve import path: %q", importPath)
  1323  }
  1324  
  1325  // stubModInfo is a stub implementation of RemoteCache.ModInfo.
  1326  func stubModInfo(importPath string) (string, error) {
  1327  	if pathtools.HasPrefix(importPath, "example.com/repo/v2") {
  1328  		return "example.com/repo/v2", nil
  1329  	}
  1330  	if pathtools.HasPrefix(importPath, "example.com/repo") {
  1331  		return "example.com/repo", nil
  1332  	}
  1333  	return "", fmt.Errorf("could not find module for import path: %q", importPath)
  1334  }
  1335  
  1336  func convertImportsAttr(r *rule.Rule) interface{} {
  1337  	kind := r.Kind()
  1338  	value := r.AttrStrings("_imports")
  1339  	r.DelAttr("_imports")
  1340  	if _, ok := goKinds[kind]; ok {
  1341  		return rule.PlatformStrings{Generic: value}
  1342  	} else {
  1343  		// proto_library
  1344  		return value
  1345  	}
  1346  }
  1347  
  1348  type mapResolver map[string]resolve.Resolver
  1349  
  1350  func (mr mapResolver) Resolver(r *rule.Rule, f string) resolve.Resolver {
  1351  	return mr[r.Kind()]
  1352  }
  1353  

View as plain text