Skip to content

Commit 6624657

Browse files
committed
Fix dh-make-golang estimate
As support for the gopath mode of "go get" has been removed in latest versions of Go, we cannot use the GO111MODULE=off trick any more. To fix the estimate command, we now download the sources of the repo manually then run "go get" in the repo dir to make use of the information contained in the go.mod file and download the dependencies. For go packages that are not modules yet, we init ourselves a go module using "go mod init".
1 parent d70d43c commit 6624657

File tree

1 file changed

+63
-22
lines changed

1 file changed

+63
-22
lines changed

estimate.go

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,41 @@ import (
1515
"golang.org/x/tools/refactor/importgraph"
1616
)
1717

18-
func get(gopath, repo string) error {
18+
func clone(srcdir, repo string) (string, error) {
1919
done := make(chan struct{})
2020
defer close(done)
21-
go progressSize("go get", filepath.Join(gopath, "src"), done)
22-
23-
// As per https://groups.google.com/forum/#!topic/golang-nuts/N5apfenE4m4,
24-
// the arguments to “go get” are packages, not repositories. Hence, we
25-
// specify “gopkg/...” in order to cover all packages.
26-
// As a concrete example, github.com/jacobsa/util is a repository we want
27-
// to package into a single Debian package, and using “go get -d
28-
// github.com/jacobsa/util” fails because there are no buildable go files
29-
// in the top level of that repository.
30-
cmd := exec.Command("go", "get", "-d", "-t", repo+"/...")
21+
go progressSize("vcs clone", srcdir, done)
22+
23+
// Get the sources of the module in a temporary dir to be able to run
24+
// go get in module mode, as the gopath mode has been removed in latest
25+
// version of Go.
26+
rr, err := vcs.RepoRootForImportPath(repo, false)
27+
if err != nil {
28+
return "", fmt.Errorf("get repo root: %w", err)
29+
}
30+
dir := filepath.Join(srcdir, rr.Root)
31+
// Run "git clone {repo} {dir}" (or the equivalent command for hg, svn, bzr)
32+
return dir, rr.VCS.Create(dir, rr.Repo)
33+
}
34+
35+
func get(gopath, repodir, repo string) error {
36+
done := make(chan struct{})
37+
defer close(done)
38+
go progressSize("go get", repodir, done)
39+
40+
// Run go get without arguments directly in the module directory to
41+
// download all its dependencies (with -t to include the test dependencies).
42+
cmd := exec.Command("go", "get", "-t")
43+
cmd.Dir = repodir
3144
cmd.Stderr = os.Stderr
3245
cmd.Env = append([]string{
33-
"GO111MODULE=off",
3446
"GOPATH=" + gopath,
3547
}, passthroughEnv()...)
3648
return cmd.Run()
3749
}
3850

3951
func removeVendor(gopath string) (found bool, _ error) {
40-
err := filepath.Walk(filepath.Join(gopath, "src"), func(path string, info os.FileInfo, err error) error {
52+
err := filepath.Walk(gopath, func(path string, info os.FileInfo, err error) error {
4153
if err != nil {
4254
return err
4355
}
@@ -56,6 +68,13 @@ func removeVendor(gopath string) (found bool, _ error) {
5668
return found, err
5769
}
5870

71+
func isFile(path string) bool {
72+
if info, err := os.Stat(path); err == nil {
73+
return !info.IsDir()
74+
}
75+
return false
76+
}
77+
5978
func estimate(importpath string) error {
6079
// construct a separate GOPATH in a temporary directory
6180
gopath, err := os.MkdirTemp("", "dh-make-golang")
@@ -64,18 +83,33 @@ func estimate(importpath string) error {
6483
}
6584
defer os.RemoveAll(gopath)
6685

67-
if err := get(gopath, importpath); err != nil {
86+
// clone the repo inside the src directory of the GOPATH
87+
// and init a Go module if it is not yet one.
88+
srcdir := filepath.Join(gopath, "src")
89+
repodir, err := clone(srcdir, importpath)
90+
if err != nil {
91+
return fmt.Errorf("vcs clone: %w", err)
92+
}
93+
if !isFile(filepath.Join(repodir, "go.mod")) {
94+
cmd := exec.Command("go", "mod", "init", importpath)
95+
cmd.Dir = repodir
96+
if err := cmd.Run(); err != nil {
97+
return fmt.Errorf("go mod init: %w", err)
98+
}
99+
}
100+
101+
if err := get(gopath, repodir, importpath); err != nil {
68102
return fmt.Errorf("go get: %w", err)
69103
}
70104

71-
found, err := removeVendor(gopath)
105+
found, err := removeVendor(repodir)
72106
if err != nil {
73107
return fmt.Errorf("remove vendor: %w", err)
74108
}
75109

76110
if found {
77111
// Fetch un-vendored dependencies
78-
if err := get(gopath, importpath); err != nil {
112+
if err := get(gopath, repodir, importpath); err != nil {
79113
return fmt.Errorf("fetch un-vendored: go get: %w", err)
80114
}
81115
}
@@ -84,7 +118,6 @@ func estimate(importpath string) error {
84118
cmd := exec.Command("go", "list", "std")
85119
cmd.Stderr = os.Stderr
86120
cmd.Env = append([]string{
87-
"GO111MODULE=off",
88121
"GOPATH=" + gopath,
89122
}, passthroughEnv()...)
90123

@@ -106,13 +139,21 @@ func estimate(importpath string) error {
106139
}
107140

108141
build.Default.GOPATH = gopath
142+
build.Default.Dir = repodir
109143
forward, _, errors := importgraph.Build(&build.Default)
110-
if len(errors) > 0 {
111-
lines := make([]string, 0, len(errors))
112-
for importPath, err := range errors {
113-
lines = append(lines, fmt.Sprintf("%s: %v", importPath, err))
144+
errLines := make([]string, 0, len(errors))
145+
for importPath, err := range errors {
146+
// For an unknown reason, parent directories and subpackages
147+
// of the current module report an error about not being able
148+
// to import them. We can safely ignore them.
149+
isSubpackage := strings.HasPrefix(importPath, importpath+"/")
150+
isParentDir := strings.HasPrefix(importpath, importPath+"/")
151+
if !isSubpackage && !isParentDir && importPath != importPath {
152+
errLines = append(errLines, fmt.Sprintf("%s: %v", importPath, err))
114153
}
115-
return fmt.Errorf("could not load packages: %v", strings.Join(lines, "\n"))
154+
}
155+
if len(errLines) > 0 {
156+
return fmt.Errorf("could not load packages: %v", strings.Join(errLines, "\n"))
116157
}
117158

118159
var lines []string

0 commit comments

Comments
 (0)