Skip to content

Commit 60f34f7

Browse files
author
Jay Conrod
committed
cmd/dist: support GOROOT vendoring
In the second step of make.bash, cmd/dist builds cmd/go by invoking the compiler, linker, and other tools directly on transitive dependencies of cmd/go. Essentially, cmd/dist acts as a minimal version of 'go install' when building go_toolchain. Until now, cmd/go has had no transitive dependencies in vendor directories. This changes in CL 202698, where several packages are deleted and equivalent versions in golang.org/x/mod are used instead. So this CL adds support to cmd/dist for vendor directories. Updates #31761 Change-Id: Iab4cdc7e505069a8df296287d16fbaa871944955 Reviewed-on: https://go-review.googlesource.com/c/go/+/203537 Run-TryBot: Jay Conrod <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]>
1 parent 3e45703 commit 60f34f7

File tree

2 files changed

+101
-35
lines changed

2 files changed

+101
-35
lines changed

src/cmd/dist/build.go

+70-35
Original file line numberDiff line numberDiff line change
@@ -605,26 +605,26 @@ func startInstall(dir string) chan struct{} {
605605

606606
// runInstall installs the library, package, or binary associated with dir,
607607
// which is relative to $GOROOT/src.
608-
func runInstall(dir string, ch chan struct{}) {
609-
if dir == "net" || dir == "os/user" || dir == "crypto/x509" {
610-
fatalf("go_bootstrap cannot depend on cgo package %s", dir)
608+
func runInstall(pkg string, ch chan struct{}) {
609+
if pkg == "net" || pkg == "os/user" || pkg == "crypto/x509" {
610+
fatalf("go_bootstrap cannot depend on cgo package %s", pkg)
611611
}
612612

613613
defer close(ch)
614614

615-
if dir == "unsafe" {
615+
if pkg == "unsafe" {
616616
return
617617
}
618618

619619
if vflag > 0 {
620620
if goos != gohostos || goarch != gohostarch {
621-
errprintf("%s (%s/%s)\n", dir, goos, goarch)
621+
errprintf("%s (%s/%s)\n", pkg, goos, goarch)
622622
} else {
623-
errprintf("%s\n", dir)
623+
errprintf("%s\n", pkg)
624624
}
625625
}
626626

627-
workdir := pathf("%s/%s", workdir, dir)
627+
workdir := pathf("%s/%s", workdir, pkg)
628628
xmkdirall(workdir)
629629

630630
var clean []string
@@ -634,11 +634,14 @@ func runInstall(dir string, ch chan struct{}) {
634634
}
635635
}()
636636

637-
// path = full path to dir.
638-
path := pathf("%s/src/%s", goroot, dir)
637+
// dir = full path to pkg.
638+
dir := pathf("%s/src/%s", goroot, pkg)
639639
name := filepath.Base(dir)
640640

641-
ispkg := !strings.HasPrefix(dir, "cmd/") || strings.Contains(dir, "/internal/")
641+
// ispkg predicts whether the package should be linked as a binary, based
642+
// on the name. There should be no "main" packages in vendor, since
643+
// 'go mod vendor' will only copy imported packages there.
644+
ispkg := !strings.HasPrefix(pkg, "cmd/") || strings.Contains(pkg, "/internal/") || strings.Contains(pkg, "/vendor/")
642645

643646
// Start final link command line.
644647
// Note: code below knows that link.p[targ] is the target.
@@ -650,7 +653,7 @@ func runInstall(dir string, ch chan struct{}) {
650653
if ispkg {
651654
// Go library (package).
652655
ispackcmd = true
653-
link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)}
656+
link = []string{"pack", packagefile(pkg)}
654657
targ = len(link) - 1
655658
xmkdirall(filepath.Dir(link[targ]))
656659
} else {
@@ -675,7 +678,7 @@ func runInstall(dir string, ch chan struct{}) {
675678
// Gather files that are sources for this target.
676679
// Everything in that directory, and any target-specific
677680
// additions.
678-
files := xreaddir(path)
681+
files := xreaddir(dir)
679682

680683
// Remove files beginning with . or _,
681684
// which are likely to be editor temporary files.
@@ -687,7 +690,7 @@ func runInstall(dir string, ch chan struct{}) {
687690
})
688691

689692
for _, dt := range deptab {
690-
if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) {
693+
if pkg == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(pkg, dt.prefix) {
691694
for _, p := range dt.dep {
692695
p = os.ExpandEnv(p)
693696
files = append(files, p)
@@ -699,7 +702,7 @@ func runInstall(dir string, ch chan struct{}) {
699702
// Convert to absolute paths.
700703
for i, p := range files {
701704
if !filepath.IsAbs(p) {
702-
files[i] = pathf("%s/%s", path, p)
705+
files[i] = pathf("%s/%s", dir, p)
703706
}
704707
}
705708

@@ -715,7 +718,7 @@ func runInstall(dir string, ch chan struct{}) {
715718
return false
716719
ok:
717720
t := mtime(p)
718-
if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) {
721+
if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, pkg) {
719722
return false
720723
}
721724
if strings.HasSuffix(p, ".go") {
@@ -742,7 +745,7 @@ func runInstall(dir string, ch chan struct{}) {
742745
}
743746

744747
// For package runtime, copy some files into the work space.
745-
if dir == "runtime" {
748+
if pkg == "runtime" {
746749
xmkdirall(pathf("%s/pkg/include", goroot))
747750
// For use by assembly and C files.
748751
copyfile(pathf("%s/pkg/include/textflag.h", goroot),
@@ -764,7 +767,7 @@ func runInstall(dir string, ch chan struct{}) {
764767
if vflag > 1 {
765768
errprintf("generate %s\n", p)
766769
}
767-
gt.gen(path, p)
770+
gt.gen(dir, p)
768771
// Do not add generated file to clean list.
769772
// In runtime, we want to be able to
770773
// build the package with the go tool,
@@ -782,22 +785,31 @@ func runInstall(dir string, ch chan struct{}) {
782785
built:
783786
}
784787

785-
// Make sure dependencies are installed.
786-
var deps []string
788+
// Resolve imported packages to actual package paths.
789+
// Make sure they're installed.
790+
importMap := make(map[string]string)
787791
for _, p := range gofiles {
788-
deps = append(deps, readimports(p)...)
792+
for _, imp := range readimports(p) {
793+
importMap[imp] = resolveVendor(imp, dir)
794+
}
795+
}
796+
sortedImports := make([]string, 0, len(importMap))
797+
for imp := range importMap {
798+
sortedImports = append(sortedImports, imp)
789799
}
790-
for _, dir1 := range deps {
791-
startInstall(dir1)
800+
sort.Strings(sortedImports)
801+
802+
for _, dep := range importMap {
803+
startInstall(dep)
792804
}
793-
for _, dir1 := range deps {
794-
install(dir1)
805+
for _, dep := range importMap {
806+
install(dep)
795807
}
796808

797809
if goos != gohostos || goarch != gohostarch {
798810
// We've generated the right files; the go command can do the build.
799811
if vflag > 1 {
800-
errprintf("skip build for cross-compile %s\n", dir)
812+
errprintf("skip build for cross-compile %s\n", pkg)
801813
}
802814
return
803815
}
@@ -830,18 +842,35 @@ func runInstall(dir string, ch chan struct{}) {
830842
if err := ioutil.WriteFile(goasmh, nil, 0666); err != nil {
831843
fatalf("cannot write empty go_asm.h: %s", err)
832844
}
833-
bgrun(&wg, path, asmabis...)
845+
bgrun(&wg, dir, asmabis...)
834846
bgwait(&wg)
835847
}
836848

849+
// Build an importcfg file for the compiler.
850+
buf := &bytes.Buffer{}
851+
for _, imp := range sortedImports {
852+
if imp == "unsafe" {
853+
continue
854+
}
855+
dep := importMap[imp]
856+
if imp != dep {
857+
fmt.Fprintf(buf, "importmap %s=%s\n", imp, dep)
858+
}
859+
fmt.Fprintf(buf, "packagefile %s=%s\n", dep, packagefile(dep))
860+
}
861+
importcfg := pathf("%s/importcfg", workdir)
862+
if err := ioutil.WriteFile(importcfg, buf.Bytes(), 0666); err != nil {
863+
fatalf("cannot write importcfg file: %v", err)
864+
}
865+
837866
var archive string
838867
// The next loop will compile individual non-Go files.
839868
// Hand the Go files to the compiler en masse.
840869
// For packages containing assembly, this writes go_asm.h, which
841870
// the assembly files will need.
842-
pkg := dir
843-
if strings.HasPrefix(dir, "cmd/") && strings.Count(dir, "/") == 1 {
844-
pkg = "main"
871+
pkgName := pkg
872+
if strings.HasPrefix(pkg, "cmd/") && strings.Count(pkg, "/") == 1 {
873+
pkgName = "main"
845874
}
846875
b := pathf("%s/_go_.a", workdir)
847876
clean = append(clean, b)
@@ -852,11 +881,11 @@ func runInstall(dir string, ch chan struct{}) {
852881
}
853882

854883
// Compile Go code.
855-
compile := []string{pathf("%s/compile", tooldir), "-std", "-pack", "-o", b, "-p", pkg}
884+
compile := []string{pathf("%s/compile", tooldir), "-std", "-pack", "-o", b, "-p", pkgName, "-importcfg", importcfg}
856885
if gogcflags != "" {
857886
compile = append(compile, strings.Fields(gogcflags)...)
858887
}
859-
if dir == "runtime" {
888+
if pkg == "runtime" {
860889
compile = append(compile, "-+")
861890
}
862891
if len(sfiles) > 0 {
@@ -874,7 +903,7 @@ func runInstall(dir string, ch chan struct{}) {
874903
// We use bgrun and immediately wait for it instead of calling run() synchronously.
875904
// This executes all jobs through the bgwork channel and allows the process
876905
// to exit cleanly in case an error occurs.
877-
bgrun(&wg, path, compile...)
906+
bgrun(&wg, dir, compile...)
878907
bgwait(&wg)
879908

880909
// Compile the files.
@@ -888,7 +917,7 @@ func runInstall(dir string, ch chan struct{}) {
888917
// Change the last character of the output file (which was c or s).
889918
b = b[:len(b)-1] + "o"
890919
compile = append(compile, "-o", b, p)
891-
bgrun(&wg, path, compile...)
920+
bgrun(&wg, dir, compile...)
892921

893922
link = append(link, b)
894923
if doclean {
@@ -909,6 +938,12 @@ func runInstall(dir string, ch chan struct{}) {
909938
bgwait(&wg)
910939
}
911940

941+
// packagefile returns the path to a compiled .a file for the given package
942+
// path. Paths may need to be resolved with resolveVendor first.
943+
func packagefile(pkg string) string {
944+
return pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, pkg)
945+
}
946+
912947
// matchfield reports whether the field (x,y,z) matches this build.
913948
// all the elements in the field must be satisfied.
914949
func matchfield(f string) bool {
@@ -940,7 +975,7 @@ func matchtag(tag string) bool {
940975
// of GOOS and GOARCH.
941976
// We also allow the special tag cmd_go_bootstrap.
942977
// See ../go/bootstrap.go and package go/build.
943-
func shouldbuild(file, dir string) bool {
978+
func shouldbuild(file, pkg string) bool {
944979
// Check file name for GOOS or GOARCH.
945980
name := filepath.Base(file)
946981
excluded := func(list []string, ok string) bool {
@@ -982,7 +1017,7 @@ func shouldbuild(file, dir string) bool {
9821017
if code == "package documentation" {
9831018
return false
9841019
}
985-
if code == "package main" && dir != "cmd/go" && dir != "cmd/cgo" {
1020+
if code == "package main" && pkg != "cmd/go" && pkg != "cmd/cgo" {
9861021
return false
9871022
}
9881023
if !strings.HasPrefix(p, "//") {

src/cmd/dist/imports.go

+31
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ package main
1111
import (
1212
"bufio"
1313
"errors"
14+
"fmt"
1415
"io"
16+
"path"
17+
"path/filepath"
1518
"strconv"
1619
"strings"
1720
"unicode/utf8"
@@ -243,3 +246,31 @@ func readimports(file string) []string {
243246

244247
return imports
245248
}
249+
250+
// resolveVendor returns a unique package path imported with the given import
251+
// path from srcDir.
252+
//
253+
// resolveVendor assumes that a package is vendored if and only if its first
254+
// path component contains a dot. If a package is vendored, its import path
255+
// is returned with a "vendor" or "cmd/vendor" prefix, depending on srcDir.
256+
// Otherwise, the import path is returned verbatim.
257+
func resolveVendor(imp, srcDir string) string {
258+
var first string
259+
if i := strings.Index(imp, "/"); i < 0 {
260+
first = imp
261+
} else {
262+
first = imp[:i]
263+
}
264+
isStandard := !strings.Contains(first, ".")
265+
if isStandard {
266+
return imp
267+
}
268+
269+
if strings.HasPrefix(srcDir, filepath.Join(goroot, "src", "cmd")) {
270+
return path.Join("cmd", "vendor", imp)
271+
} else if strings.HasPrefix(srcDir, filepath.Join(goroot, "src")) {
272+
return path.Join("vendor", imp)
273+
} else {
274+
panic(fmt.Sprintf("srcDir %q not in GOOROT/src", srcDir))
275+
}
276+
}

0 commit comments

Comments
 (0)