Skip to content

Commit 66bbd4d

Browse files
authored
Gazelle: add importpath attributes to rules (#768)
* go_prefix is no longer generated. In a future change, existing go_prefix rules will be removed in fix mode. * All go_library, go_test, and go_binary rules are generated with an importpath attribute. * importpath is now a mergeable attribute; Gazelle will replace existing importpath attributes unless they have a keep comment. This helps keep libraries up to date after they are moved to a new location. Related #721
1 parent 10a4d19 commit 66bbd4d

File tree

21 files changed

+82
-114
lines changed

21 files changed

+82
-114
lines changed

go/tools/gazelle/gazelle/integration_test.go

+23-25
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,7 @@ func TestSelectLabelsSorted(t *testing.T) {
141141
{
142142
path: "BUILD",
143143
content: `
144-
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_prefix")
145-
146-
go_prefix("example.com/foo")
144+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
147145
148146
go_library(
149147
name = "go_default_library",
@@ -156,6 +154,7 @@ go_library(
156154
],
157155
"//conditions:default": [],
158156
}),
157+
importpath = "example.com/foo",
159158
)
160159
`,
161160
},
@@ -183,9 +182,7 @@ package foo
183182
{path: "outer/outer.go", content: "package outer"},
184183
{path: "outer/inner/inner.go", content: "package inner"},
185184
})
186-
want := `load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_prefix")
187-
188-
go_prefix("example.com/foo")
185+
want := `load("@io_bazel_rules_go//go:def.bzl", "go_library")
189186
190187
go_library(
191188
name = "go_default_library",
@@ -198,6 +195,7 @@ go_library(
198195
],
199196
"//conditions:default": [],
200197
}),
198+
importpath = "example.com/foo",
201199
visibility = ["//visibility:public"],
202200
deps = select({
203201
"@io_bazel_rules_go//go/platform:linux_amd64": [
@@ -213,7 +211,7 @@ go_library(
213211
t.Fatal(err)
214212
}
215213

216-
if err := runGazelle(dir, nil); err != nil {
214+
if err := runGazelle(dir, []string{"-go_prefix", "example.com/foo"}); err != nil {
217215
t.Fatal(err)
218216
}
219217
if got, err := ioutil.ReadFile(filepath.Join(dir, "BUILD")); err != nil {
@@ -279,6 +277,7 @@ go_library(
279277
"pure.go",
280278
],
281279
cgo = True,
280+
importpath = "example.com/foo",
282281
visibility = ["//visibility:default"],
283282
)
284283
@@ -300,6 +299,7 @@ go_library(
300299
"pure.go",
301300
],
302301
cgo = True,
302+
importpath = "example.com/foo",
303303
visibility = ["//visibility:default"],
304304
)
305305
`,
@@ -330,9 +330,7 @@ func TestFixUnlinkedCgoLibrary(t *testing.T) {
330330
{path: "WORKSPACE"},
331331
{
332332
path: "BUILD",
333-
content: `load("@io_bazel_rules_go//go:def.bzl", "cgo_library", "go_library", "go_prefix")
334-
335-
go_prefix("example.com/foo")
333+
content: `load("@io_bazel_rules_go//go:def.bzl", "cgo_library", "go_library")
336334
337335
cgo_library(
338336
name = "cgo_default_library",
@@ -342,6 +340,7 @@ cgo_library(
342340
go_library(
343341
name = "go_default_library",
344342
srcs = ["pure.go"],
343+
importpath = "example.com/foo",
345344
visibility = ["//visibility:public"],
346345
)
347346
`,
@@ -356,17 +355,16 @@ go_library(
356355
t.Fatal(err)
357356
}
358357

359-
want := `load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_prefix")
360-
361-
go_prefix("example.com/foo")
358+
want := `load("@io_bazel_rules_go//go:def.bzl", "go_library")
362359
363360
go_library(
364361
name = "go_default_library",
365362
srcs = ["pure.go"],
363+
importpath = "example.com/foo",
366364
visibility = ["//visibility:public"],
367365
)
368366
`
369-
if err := runGazelle(dir, []string{"fix"}); err != nil {
367+
if err := runGazelle(dir, []string{"fix", "-go_prefix", "example.com/foo"}); err != nil {
370368
t.Fatal(err)
371369
}
372370
if got, err := ioutil.ReadFile(filepath.Join(dir, "BUILD")); err != nil {
@@ -504,13 +502,12 @@ import _ "golang.org/x/baz"
504502
checkFiles(t, dir, []fileSpec{
505503
{
506504
path: config.DefaultValidBuildFileNames[0],
507-
content: `load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_prefix")
508-
509-
go_prefix("example.com/foo")
505+
content: `load("@io_bazel_rules_go//go:def.bzl", "go_library")
510506
511507
go_library(
512508
name = "go_default_library",
513509
srcs = ["a.go"],
510+
importpath = "example.com/foo",
514511
visibility = ["//visibility:public"],
515512
deps = ["//vendor/golang.org/x/bar:go_default_library"],
516513
)
@@ -522,6 +519,7 @@ go_library(
522519
go_library(
523520
name = "go_default_library",
524521
srcs = ["bar.go"],
522+
importpath = "golang.org/x/bar",
525523
visibility = ["//visibility:public"],
526524
deps = ["//vendor/golang.org/x/baz:go_default_library"],
527525
)
@@ -539,7 +537,7 @@ func TestFlatExternal(t *testing.T) {
539537
540538
gazelle(
541539
name = "gazelle",
542-
prefix = "example.com/repo",
540+
prefix = "example.com/foo",
543541
args = ["-experimental_flat"],
544542
)
545543
`,
@@ -590,16 +588,14 @@ import (
590588
checkFiles(t, dir, []fileSpec{
591589
{
592590
path: config.DefaultValidBuildFileNames[0],
593-
content: `load("@io_bazel_rules_go//go:def.bzl", "gazelle", "go_binary", "go_library", "go_prefix", "go_test")
591+
content: `load("@io_bazel_rules_go//go:def.bzl", "gazelle", "go_binary", "go_library", "go_test")
594592
595593
gazelle(
596594
name = "gazelle",
597595
args = ["-experimental_flat"],
598-
prefix = "example.com/repo",
596+
prefix = "example.com/foo",
599597
)
600598
601-
go_prefix("example.com/foo")
602-
603599
go_library(
604600
name = "foo",
605601
srcs = ["a.go"],
@@ -618,6 +614,7 @@ go_test(
618614
name = "b_test",
619615
srcs = ["b/b_test.go"],
620616
data = glob(["b/testdata/**"]),
617+
importpath = "example.com/foo/b",
621618
library = ":b",
622619
rundir = "b",
623620
)
@@ -626,6 +623,7 @@ go_test(
626623
name = "b_xtest",
627624
srcs = ["b/b_x_test.go"],
628625
data = glob(["b/testdata/**"]),
626+
importpath = "example.com/foo/b_test",
629627
rundir = "b",
630628
deps = [":b"],
631629
)
@@ -640,6 +638,7 @@ go_library(
640638
go_library(
641639
name = "c",
642640
srcs = ["c/c.go"],
641+
importpath = "example.com/foo/c",
643642
visibility = ["//visibility:private"],
644643
deps = [
645644
":b",
@@ -651,6 +650,7 @@ go_library(
651650
652651
go_binary(
653652
name = "c_cmd",
653+
importpath = "example.com/foo/c",
654654
library = ":c",
655655
visibility = ["//visibility:public"],
656656
)
@@ -707,7 +707,7 @@ import _ "github.com/jr_hacker/stuff/a/b"
707707
checkFiles(t, dir, []fileSpec{
708708
{
709709
path: config.DefaultValidBuildFileNames[0],
710-
content: `load("@io_bazel_rules_go//go:def.bzl", "gazelle", "go_library", "go_prefix")
710+
content: `load("@io_bazel_rules_go//go:def.bzl", "gazelle", "go_library")
711711
712712
gazelle(
713713
name = "gazelle",
@@ -716,8 +716,6 @@ gazelle(
716716
prefix = "example.com/foo",
717717
)
718718
719-
go_prefix("example.com/foo")
720-
721719
go_library(
722720
name = "foo",
723721
srcs = ["foo.go"],

go/tools/gazelle/gazelle/main.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,20 @@ func (v *hierarchicalVisitor) finish() {
128128
return
129129
}
130130

131-
// We did not process a package at the repository root. We need to put
132-
// a go_prefix rule there, even if there are no .go files in that directory.
133-
oldFile, err := loadBuildFile(v.c, v.c.RepoRoot)
134-
if err != nil && !os.IsNotExist(err) {
131+
// We did not process a package at the repository root. We need to create
132+
// a build file if none exists.
133+
for _, base := range v.c.ValidBuildFileNames {
134+
p := filepath.Join(v.c.RepoRoot, base)
135+
if _, err := os.Stat(p); err == nil || !os.IsNotExist(err) {
136+
return
137+
}
138+
}
139+
p := filepath.Join(v.c.RepoRoot, v.c.DefaultBuildFileName())
140+
if f, err := os.Create(p); err != nil {
135141
log.Print(err)
142+
} else {
143+
f.Close()
136144
}
137-
138-
pkg := &packages.Package{Dir: v.c.RepoRoot}
139-
g := rules.NewGenerator(v.c, v.r, v.l, "", oldFile)
140-
genFile := g.Generate(pkg)
141-
v.mergeAndEmit(genFile, oldFile)
142145
}
143146

144147
// flatVisitor generates and updates a single build file that contains rules
@@ -178,7 +181,6 @@ func (v *flatVisitor) finish() {
178181
sort.Strings(packageNames)
179182

180183
genFile.Stmt = append(genFile.Stmt, nil) // reserve space for load
181-
genFile.Stmt = append(genFile.Stmt, g.GeneratePrefix())
182184
for _, name := range packageNames {
183185
rs := v.rules[name]
184186
genFile.Stmt = append(genFile.Stmt, rs...)

go/tools/gazelle/merger/merger.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ const (
3232

3333
var (
3434
mergeableFields = map[string]bool{
35-
"cgo": true,
36-
"clinkopts": true,
37-
"copts": true,
38-
"deps": true,
39-
"library": true,
40-
"srcs": true,
35+
"cgo": true,
36+
"clinkopts": true,
37+
"copts": true,
38+
"deps": true,
39+
"importpath": true,
40+
"library": true,
41+
"srcs": true,
4142
}
4243
)
4344

go/tools/gazelle/packages/package.go

-6
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,13 @@ func (p *Package) HasGo() bool {
8181
// ImportPath returns the inferred Go import path for this package. This
8282
// is determined as follows:
8383
//
84-
// * If there is no library target or if Name is "main", "" is returned.
85-
// This indicates the library cannot be imported.
8684
// * If "vendor" is a component in p.Rel, everything after the last "vendor"
8785
// component is returned.
8886
// * Otherwise, prefix joined with Rel is returned.
8987
//
9088
// TODO(jayconrod): extract canonical import paths from comments on
9189
// package statements.
9290
func (p *Package) ImportPath(prefix string) string {
93-
if !p.Library.HasGo() || p.IsCommand() {
94-
return ""
95-
}
96-
9791
components := strings.Split(p.Rel, "/")
9892
for i := len(components) - 1; i >= 0; i-- {
9993
if components[i] == "vendor" {

go/tools/gazelle/packages/package_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ func TestImportPathNoLib(t *testing.T) {
6767
Name: "bar",
6868
Rel: "foo/bar",
6969
}
70-
if got := pkg.ImportPath("example.com/repo"); got != "" {
71-
t.Errorf(`got %q; want ""`, got)
70+
if got, want := pkg.ImportPath("example.com/repo"), "example.com/repo/foo/bar"; got != want {
71+
t.Errorf(`got %q; want %q`, got, want)
7272
}
7373
}
7474

@@ -82,8 +82,8 @@ func TestImportPathCmd(t *testing.T) {
8282
},
8383
},
8484
}
85-
if got := pkg.ImportPath("example.com/repo"); got != "" {
86-
t.Errorf(`got %q; want ""`, got)
85+
if got, want := pkg.ImportPath("example.com/repo"), "example.com/repo/foo/bar"; got != want {
86+
t.Errorf(`got %q; want %q`, got, want)
8787
}
8888
}
8989

go/tools/gazelle/rules/generator.go

+9-16
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ type Generator interface {
3838
// in this interface.
3939
Generate(pkg *packages.Package) *bf.File
4040

41-
// GeneratePrefix generates a go_prefix rule. This should be in the
42-
// top-level build file for the repository.
43-
GeneratePrefix() bf.Expr
44-
4541
// GenerateRules generates a list of rules for targets in "pkg".
4642
GenerateRules(pkg *packages.Package) []bf.Expr
4743

@@ -72,18 +68,11 @@ func (g *generator) Generate(pkg *packages.Package) *bf.File {
7268
Path: filepath.Join(pkg.Dir, g.c.DefaultBuildFileName()),
7369
}
7470
f.Stmt = append(f.Stmt, nil) // reserve space for load
75-
if pkg.Rel == "" {
76-
f.Stmt = append(f.Stmt, g.GeneratePrefix())
77-
}
7871
f.Stmt = append(f.Stmt, g.GenerateRules(pkg)...)
7972
f.Stmt[0] = g.GenerateLoad(f.Stmt[1:])
8073
return f
8174
}
8275

83-
func (g *generator) GeneratePrefix() bf.Expr {
84-
return newRule("go_prefix", []interface{}{g.c.GoPrefix}, nil)
85-
}
86-
8776
func (g *generator) GenerateRules(pkg *packages.Package) []bf.Expr {
8877
var rules []bf.Expr
8978
library, r := g.generateLib(pkg)
@@ -150,6 +139,9 @@ func (g *generator) generateBin(pkg *packages.Package, library string) bf.Expr {
150139
name := g.l.BinaryLabel(pkg.Rel).Name
151140
visibility := checkInternalVisibility(pkg.Rel, "//visibility:public")
152141
attrs := g.commonAttrs(pkg.Rel, name, visibility, pkg.Binary)
142+
// TODO(jayconrod): don't add importpath if it can be inherited from library.
143+
// This is blocked by bazelbuild/bazel#3575.
144+
attrs = append(attrs, keyvalue{"importpath", pkg.ImportPath(g.c.GoPrefix)})
153145
if library != "" {
154146
attrs = append(attrs, keyvalue{"library", ":" + library})
155147
}
@@ -170,11 +162,7 @@ func (g *generator) generateLib(pkg *packages.Package) (string, bf.Expr) {
170162
}
171163

172164
attrs := g.commonAttrs(pkg.Rel, name, visibility, pkg.Library)
173-
if !pkg.IsCommand() && g.c.StructureMode == config.FlatMode {
174-
// TODO(jayconrod): add importpath attributes outside of flat mode after
175-
// we have verified it works correctly.
176-
attrs = append(attrs, keyvalue{"importpath", pkg.ImportPath(g.c.GoPrefix)})
177-
}
165+
attrs = append(attrs, keyvalue{"importpath", pkg.ImportPath(g.c.GoPrefix)})
178166

179167
rule := newRule("go_library", nil, attrs)
180168
return name, rule
@@ -224,14 +212,19 @@ func (g *generator) filegroup(pkg *packages.Package) bf.Expr {
224212

225213
func (g *generator) generateTest(pkg *packages.Package, library string, isXTest bool) bf.Expr {
226214
target := pkg.Test
215+
importpath := pkg.ImportPath(g.c.GoPrefix)
227216
if isXTest {
228217
target = pkg.XTest
218+
importpath += "_test"
229219
}
230220
if !target.HasGo() {
231221
return nil
232222
}
233223
name := g.l.TestLabel(pkg.Rel, isXTest).Name
234224
attrs := g.commonAttrs(pkg.Rel, name, "", target)
225+
// TODO(jayconrod): don't add importpath if it can be inherited from library.
226+
// This is blocked by bazelbuild/bazel#3575.
227+
attrs = append(attrs, keyvalue{"importpath", importpath})
235228
if library != "" {
236229
attrs = append(attrs, keyvalue{"library", ":" + library})
237230
}

0 commit comments

Comments
 (0)