Skip to content

Commit e716599

Browse files
tamayikafindleyr
authored andcommitted
cmd/goimports: support workspace vendoring
Fixes golang/go#65744 This also fixes gopls imports. Change-Id: I8a3beff6c96ff27d7622c1315e3f2dffc83194a3 GitHub-Last-Rev: d199d2c GitHub-Pull-Request: #485 Reviewed-on: https://go-review.googlesource.com/c/tools/+/572675 Reviewed-by: Peter Weinberger <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 7c7d7db commit e716599

File tree

3 files changed

+142
-13
lines changed

3 files changed

+142
-13
lines changed

internal/gocommand/vendor.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,57 @@ func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*Modul
107107
}
108108
return mod, lines[4] == "go1.14", nil
109109
}
110+
111+
// WorkspaceVendorEnabled reports whether workspace vendoring is enabled. It takes a *Runner to execute Go commands
112+
// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
113+
// of which only Verb and Args are modified to run the appropriate Go command.
114+
// Inspired by setDefaultBuildMod in modload/init.go
115+
func WorkspaceVendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, []*ModuleJSON, error) {
116+
inv.Verb = "env"
117+
inv.Args = []string{"GOWORK"}
118+
stdout, err := r.Run(ctx, inv)
119+
if err != nil {
120+
return false, nil, err
121+
}
122+
goWork := string(bytes.TrimSpace(stdout.Bytes()))
123+
if fi, err := os.Stat(filepath.Join(filepath.Dir(goWork), "vendor")); err == nil && fi.IsDir() {
124+
mainMods, err := getWorkspaceMainModules(ctx, inv, r)
125+
if err != nil {
126+
return false, nil, err
127+
}
128+
return true, mainMods, nil
129+
}
130+
return false, nil, nil
131+
}
132+
133+
// getWorkspaceMainModules gets the main modules' information.
134+
// This is the information needed to figure out if vendoring should be enabled.
135+
func getWorkspaceMainModules(ctx context.Context, inv Invocation, r *Runner) ([]*ModuleJSON, error) {
136+
const format = `{{.Path}}
137+
{{.Dir}}
138+
{{.GoMod}}
139+
{{.GoVersion}}
140+
`
141+
inv.Verb = "list"
142+
inv.Args = []string{"-m", "-f", format}
143+
stdout, err := r.Run(ctx, inv)
144+
if err != nil {
145+
return nil, err
146+
}
147+
148+
lines := strings.Split(strings.TrimSuffix(stdout.String(), "\n"), "\n")
149+
if len(lines) < 4 {
150+
return nil, fmt.Errorf("unexpected stdout: %q", stdout.String())
151+
}
152+
mods := make([]*ModuleJSON, 0, len(lines)/4)
153+
for i := 0; i < len(lines); i += 4 {
154+
mods = append(mods, &ModuleJSON{
155+
Path: lines[i],
156+
Dir: lines[i+1],
157+
GoMod: lines[i+2],
158+
GoVersion: lines[i+3],
159+
Main: true,
160+
})
161+
}
162+
return mods, nil
163+
}

internal/imports/mod.go

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,30 +112,47 @@ func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleRe
112112
}
113113

114114
vendorEnabled := false
115-
var mainModVendor *gocommand.ModuleJSON
115+
var mainModVendor *gocommand.ModuleJSON // for module vendoring
116+
var mainModsVendor []*gocommand.ModuleJSON // for workspace vendoring
116117

117-
// Module vendor directories are ignored in workspace mode:
118-
// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md
119-
if len(r.env.Env["GOWORK"]) == 0 {
118+
goWork := r.env.Env["GOWORK"]
119+
if len(goWork) == 0 {
120120
// TODO(rfindley): VendorEnabled runs the go command to get GOFLAGS, but
121121
// they should be available from the ProcessEnv. Can we avoid the redundant
122122
// invocation?
123123
vendorEnabled, mainModVendor, err = gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
124124
if err != nil {
125125
return nil, err
126126
}
127+
} else {
128+
vendorEnabled, mainModsVendor, err = gocommand.WorkspaceVendorEnabled(context.Background(), inv, r.env.GocmdRunner)
129+
if err != nil {
130+
return nil, err
131+
}
127132
}
128133

129-
if mainModVendor != nil && vendorEnabled {
130-
// Vendor mode is on, so all the non-Main modules are irrelevant,
131-
// and we need to search /vendor for everything.
132-
r.mains = []*gocommand.ModuleJSON{mainModVendor}
133-
r.dummyVendorMod = &gocommand.ModuleJSON{
134-
Path: "",
135-
Dir: filepath.Join(mainModVendor.Dir, "vendor"),
134+
if vendorEnabled {
135+
if mainModVendor != nil {
136+
// Module vendor mode is on, so all the non-Main modules are irrelevant,
137+
// and we need to search /vendor for everything.
138+
r.mains = []*gocommand.ModuleJSON{mainModVendor}
139+
r.dummyVendorMod = &gocommand.ModuleJSON{
140+
Path: "",
141+
Dir: filepath.Join(mainModVendor.Dir, "vendor"),
142+
}
143+
r.modsByModPath = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
144+
r.modsByDir = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
145+
} else {
146+
// Workspace vendor mode is on, so all the non-Main modules are irrelevant,
147+
// and we need to search /vendor for everything.
148+
r.mains = mainModsVendor
149+
r.dummyVendorMod = &gocommand.ModuleJSON{
150+
Path: "",
151+
Dir: filepath.Join(filepath.Dir(goWork), "vendor"),
152+
}
153+
r.modsByModPath = append(append([]*gocommand.ModuleJSON{}, mainModsVendor...), r.dummyVendorMod)
154+
r.modsByDir = append(append([]*gocommand.ModuleJSON{}, mainModsVendor...), r.dummyVendorMod)
136155
}
137-
r.modsByModPath = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
138-
r.modsByDir = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
139156
} else {
140157
// Vendor mode is off, so run go list -m ... to find everything.
141158
err := r.initAllMods()

internal/imports/mod_go122_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.22
6+
// +build go1.22
7+
8+
package imports
9+
10+
import (
11+
"context"
12+
"testing"
13+
)
14+
15+
// Tests that go.work files and vendor directory are respected.
16+
func TestModWorkspaceVendoring(t *testing.T) {
17+
mt := setup(t, nil, `
18+
-- go.work --
19+
go 1.22
20+
21+
use (
22+
./a
23+
./b
24+
)
25+
-- a/go.mod --
26+
module example.com/a
27+
28+
go 1.22
29+
30+
require rsc.io/sampler v1.3.1
31+
-- a/a.go --
32+
package a
33+
34+
import _ "rsc.io/sampler"
35+
-- b/go.mod --
36+
module example.com/b
37+
38+
go 1.22
39+
-- b/b.go --
40+
package b
41+
`, "")
42+
defer mt.cleanup()
43+
44+
// generate vendor directory
45+
if _, err := mt.env.invokeGo(context.Background(), "work", "vendor"); err != nil {
46+
t.Fatal(err)
47+
}
48+
49+
// update module resolver
50+
mt.env.ClearModuleInfo()
51+
mt.env.UpdateResolver(mt.env.resolver.ClearForNewScan())
52+
53+
mt.assertModuleFoundInDir("example.com/a", "a", `main/a$`)
54+
mt.assertScanFinds("example.com/a", "a")
55+
mt.assertModuleFoundInDir("example.com/b", "b", `main/b$`)
56+
mt.assertScanFinds("example.com/b", "b")
57+
mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", `/vendor/`)
58+
}

0 commit comments

Comments
 (0)