summaryrefslogtreecommitdiff
path: root/libgo/go/cmd/go/internal/work/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/cmd/go/internal/work/exec.go')
-rw-r--r--libgo/go/cmd/go/internal/work/exec.go757
1 files changed, 587 insertions, 170 deletions
diff --git a/libgo/go/cmd/go/internal/work/exec.go b/libgo/go/cmd/go/internal/work/exec.go
index 5994dbc702f..84870e52ccc 100644
--- a/libgo/go/cmd/go/internal/work/exec.go
+++ b/libgo/go/cmd/go/internal/work/exec.go
@@ -14,6 +14,7 @@ import (
"io"
"io/ioutil"
"log"
+ "math/rand"
"os"
"os/exec"
"path/filepath"
@@ -53,7 +54,7 @@ func actionList(root *Action) []*Action {
// do runs the action graph rooted at root.
func (b *Builder) Do(root *Action) {
- if c := cache.Default(); c != nil && !b.ComputeStaleOnly {
+ if c := cache.Default(); c != nil && !b.IsCmdList {
// If we're doing real work, take time at the end to trim the cache.
defer c.Trim()
}
@@ -195,10 +196,13 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)
fmt.Fprintf(h, "import %q\n", p.ImportPath)
fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)
+ if p.Internal.ForceLibrary {
+ fmt.Fprintf(h, "forcelibrary\n")
+ }
if len(p.CgoFiles)+len(p.SwigFiles) > 0 {
fmt.Fprintf(h, "cgo %q\n", b.toolID("cgo"))
- cppflags, cflags, cxxflags, fflags, _, _ := b.CFlags(p)
- fmt.Fprintf(h, "CC=%q %q %q\n", b.ccExe(), cppflags, cflags)
+ cppflags, cflags, cxxflags, fflags, ldflags, _ := b.CFlags(p)
+ fmt.Fprintf(h, "CC=%q %q %q %q\n", b.ccExe(), cppflags, cflags, ldflags)
if len(p.CXXFiles)+len(p.SwigFiles) > 0 {
fmt.Fprintf(h, "CXX=%q %q\n", b.cxxExe(), cxxflags)
}
@@ -295,40 +299,93 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
return h.Sum()
}
+// needCgoHdr reports whether the actions triggered by this one
+// expect to be able to access the cgo-generated header file.
+func (b *Builder) needCgoHdr(a *Action) bool {
+ // If this build triggers a header install, run cgo to get the header.
+ if !b.IsCmdList && (a.Package.UsesCgo() || a.Package.UsesSwig()) && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {
+ for _, t1 := range a.triggers {
+ if t1.Mode == "install header" {
+ return true
+ }
+ }
+ for _, t1 := range a.triggers {
+ for _, t2 := range t1.triggers {
+ if t2.Mode == "install header" {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+// allowedVersion reports whether the version v is an allowed version of go
+// (one that we can compile).
+// v is known to be of the form "1.23".
+func allowedVersion(v string) bool {
+ // Special case: no requirement.
+ if v == "" {
+ return true
+ }
+ // Special case "1.0" means "go1", which is OK.
+ if v == "1.0" {
+ return true
+ }
+ // Otherwise look through release tags of form "go1.23" for one that matches.
+ for _, tag := range cfg.BuildContext.ReleaseTags {
+ if strings.HasPrefix(tag, "go") && tag[2:] == v {
+ return true
+ }
+ }
+ return false
+}
+
+const (
+ needBuild uint32 = 1 << iota
+ needCgoHdr
+ needVet
+ needCompiledGoFiles
+ needStale
+)
+
// build is the action for building a single package.
// Note that any new influence on this logic must be reported in b.buildActionID above as well.
func (b *Builder) build(a *Action) (err error) {
p := a.Package
+
+ bit := func(x uint32, b bool) uint32 {
+ if b {
+ return x
+ }
+ return 0
+ }
+
cached := false
+ need := bit(needBuild, !b.IsCmdList || b.NeedExport) |
+ bit(needCgoHdr, b.needCgoHdr(a)) |
+ bit(needVet, a.needVet) |
+ bit(needCompiledGoFiles, b.NeedCompiledGoFiles)
+
if !p.BinaryOnly {
if b.useCache(a, p, b.buildActionID(a), p.Target) {
- // If this build triggers a header install, run cgo to get the header.
- // TODO(rsc): Once we can cache multiple file outputs from an action,
- // the header should be cached, and then this awful test can be deleted.
- // Need to look for install header actions depending on this action,
- // or depending on a link that depends on this action.
- needHeader := false
- if (a.Package.UsesCgo() || a.Package.UsesSwig()) && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {
- for _, t1 := range a.triggers {
- if t1.Mode == "install header" {
- needHeader = true
- goto CheckedHeader
- }
- }
- for _, t1 := range a.triggers {
- for _, t2 := range t1.triggers {
- if t2.Mode == "install header" {
- needHeader = true
- goto CheckedHeader
- }
- }
- }
+ // We found the main output in the cache.
+ // If we don't need any other outputs, we can stop.
+ need &^= needBuild
+ if b.NeedExport {
+ p.Export = a.built
}
- CheckedHeader:
- if b.ComputeStaleOnly || !a.needVet && !needHeader {
- return nil
+ if need&needCompiledGoFiles != 0 && b.loadCachedGoFiles(a) {
+ need &^= needCompiledGoFiles
}
+ // Otherwise, we need to write files to a.Objdir (needVet, needCgoHdr).
+ // Remember that we might have them in cache
+ // and check again after we create a.Objdir.
cached = true
+ a.output = []byte{} // start saving output in case we miss any cache results
+ }
+ if need == 0 {
+ return nil
}
defer b.flushOutput(a)
}
@@ -337,6 +394,9 @@ func (b *Builder) build(a *Action) (err error) {
if err != nil && err != errPrintedOutput {
err = fmt.Errorf("go build %s: %v", a.Package.ImportPath, err)
}
+ if err != nil && b.IsCmdList && b.NeedError && p.Error == nil {
+ p.Error = &load.PackageError{Err: err.Error()}
+ }
}()
if cfg.BuildN {
// In -n mode, print a banner between packages.
@@ -356,17 +416,24 @@ func (b *Builder) build(a *Action) (err error) {
if err == nil {
a.built = a.Package.Target
a.Target = a.Package.Target
+ if b.NeedExport {
+ a.Package.Export = a.Package.Target
+ }
a.buildID = b.fileHash(a.Package.Target)
a.Package.Stale = false
a.Package.StaleReason = "binary-only package"
return nil
}
- if b.ComputeStaleOnly {
- a.Package.Stale = true
- a.Package.StaleReason = "missing or invalid binary-only package"
+ a.Package.Stale = true
+ a.Package.StaleReason = "missing or invalid binary-only package"
+ if b.IsCmdList {
return nil
}
- return fmt.Errorf("missing or invalid binary-only package")
+ return fmt.Errorf("missing or invalid binary-only package; expected file %q", a.Package.Target)
+ }
+
+ if p.Module != nil && !allowedVersion(p.Module.GoVersion) {
+ return fmt.Errorf("module requires Go %s", p.Module.GoVersion)
}
if err := b.Mkdir(a.Objdir); err != nil {
@@ -374,6 +441,23 @@ func (b *Builder) build(a *Action) (err error) {
}
objdir := a.Objdir
+ if cached {
+ if need&needCgoHdr != 0 && b.loadCachedCgoHdr(a) {
+ need &^= needCgoHdr
+ }
+
+ // Load cached vet config, but only if that's all we have left
+ // (need == needVet, not testing just the one bit).
+ // If we are going to do a full build anyway,
+ // we're going to regenerate the files below anyway.
+ if need == needVet && b.loadCachedVet(a) {
+ need &^= needVet
+ }
+ if need == 0 {
+ return nil
+ }
+ }
+
// make target directory
dir, _ := filepath.Split(a.Target)
if dir != "" {
@@ -382,13 +466,12 @@ func (b *Builder) build(a *Action) (err error) {
}
}
- var gofiles, cgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
-
- gofiles = append(gofiles, a.Package.GoFiles...)
- cgofiles = append(cgofiles, a.Package.CgoFiles...)
- cfiles = append(cfiles, a.Package.CFiles...)
- sfiles = append(sfiles, a.Package.SFiles...)
- cxxfiles = append(cxxfiles, a.Package.CXXFiles...)
+ gofiles := str.StringList(a.Package.GoFiles)
+ cgofiles := str.StringList(a.Package.CgoFiles)
+ cfiles := str.StringList(a.Package.CFiles)
+ sfiles := str.StringList(a.Package.SFiles)
+ cxxfiles := str.StringList(a.Package.CXXFiles)
+ var objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
if a.Package.UsesCgo() || a.Package.UsesSwig() {
if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.Package); err != nil {
@@ -432,7 +515,7 @@ func (b *Builder) build(a *Action) (err error) {
// Not covering this file.
continue
}
- if err := b.cover(a, coverFile, sourceFile, 0666, cover.Var); err != nil {
+ if err := b.cover(a, coverFile, sourceFile, cover.Var); err != nil {
return err
}
if i < len(gofiles) {
@@ -488,10 +571,16 @@ func (b *Builder) build(a *Action) (err error) {
}
cgoObjects = append(cgoObjects, outObj...)
gofiles = append(gofiles, outGo...)
+
+ switch cfg.BuildBuildmode {
+ case "c-archive", "c-shared":
+ b.cacheCgoHdr(a)
+ }
}
- if cached && !a.needVet {
- return nil
- }
+ b.cacheGofiles(a, gofiles)
+
+ // Running cgo generated the cgo header.
+ need &^= needCgoHdr
// Sanity check only, since Package.load already checked as well.
if len(gofiles) == 0 {
@@ -499,26 +588,19 @@ func (b *Builder) build(a *Action) (err error) {
}
// Prepare Go vet config if needed.
- var vcfg *vetConfig
- if a.needVet {
- // Pass list of absolute paths to vet,
- // so that vet's error messages will use absolute paths,
- // so that we can reformat them relative to the directory
- // in which the go command is invoked.
- vcfg = &vetConfig{
- Compiler: cfg.BuildToolchainName,
- Dir: a.Package.Dir,
- GoFiles: mkAbsFiles(a.Package.Dir, gofiles),
- ImportPath: a.Package.ImportPath,
- ImportMap: make(map[string]string),
- PackageFile: make(map[string]string),
- Standard: make(map[string]bool),
- }
- a.vetCfg = vcfg
- for i, raw := range a.Package.Internal.RawImports {
- final := a.Package.Imports[i]
- vcfg.ImportMap[raw] = final
+ if need&needVet != 0 {
+ buildVetConfig(a, gofiles)
+ need &^= needVet
+ }
+ if need&needCompiledGoFiles != 0 {
+ if !b.loadCachedGoFiles(a) {
+ return fmt.Errorf("failed to cache compiled Go files")
}
+ need &^= needCompiledGoFiles
+ }
+ if need == 0 {
+ // Nothing left to do.
+ return nil
}
// Prepare Go import config.
@@ -529,24 +611,12 @@ func (b *Builder) build(a *Action) (err error) {
// except when it doesn't.
var icfg bytes.Buffer
fmt.Fprintf(&icfg, "# import config\n")
-
for i, raw := range a.Package.Internal.RawImports {
final := a.Package.Imports[i]
if final != raw {
fmt.Fprintf(&icfg, "importmap %s=%s\n", raw, final)
}
}
-
- // Compute the list of mapped imports in the vet config
- // so that we can add any missing mappings below.
- var vcfgMapped map[string]bool
- if vcfg != nil {
- vcfgMapped = make(map[string]bool)
- for _, p := range vcfg.ImportMap {
- vcfgMapped[p] = true
- }
- }
-
for _, a1 := range a.Deps {
p1 := a1.Package
if p1 == nil || p1.ImportPath == "" {
@@ -555,33 +625,20 @@ func (b *Builder) build(a *Action) (err error) {
if a1.built != "" {
fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, a1.built)
}
- if vcfg != nil {
- // Add import mapping if needed
- // (for imports like "runtime/cgo" that appear only in generated code).
- if !vcfgMapped[p1.ImportPath] {
- vcfg.ImportMap[p1.ImportPath] = p1.ImportPath
- }
- if a1.built != "" {
- vcfg.PackageFile[p1.ImportPath] = a1.built
- }
- if p1.Standard {
- vcfg.Standard[p1.ImportPath] = true
- }
- }
}
- if cached {
- // The cached package file is OK, so we don't need to run the compile.
- // We've only going through the motions to prepare the vet configuration,
- // which is now complete.
- return nil
+ if p.Internal.BuildInfo != "" && cfg.ModulesEnabled {
+ if err := b.writeFile(objdir+"_gomod_.go", load.ModInfoProg(p.Internal.BuildInfo)); err != nil {
+ return err
+ }
+ gofiles = append(gofiles, objdir+"_gomod_.go")
}
// Compile Go.
objpkg := objdir + "_pkg_.a"
ofile, out, err := BuildToolchain.gc(b, a, objpkg, icfg.Bytes(), len(sfiles) > 0, gofiles)
if len(out) > 0 {
- b.showOutput(a, a.Package.Dir, a.Package.ImportPath, b.processOutput(out))
+ b.showOutput(a, a.Package.Dir, a.Package.Desc(), b.processOutput(out))
if err != nil {
return errPrintedOutput
}
@@ -604,17 +661,17 @@ func (b *Builder) build(a *Action) (err error) {
switch {
case strings.HasSuffix(name, _goos_goarch):
targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext
- if err := b.copyFile(a, objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil {
+ if err := b.copyFile(objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil {
return err
}
case strings.HasSuffix(name, _goarch):
targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext
- if err := b.copyFile(a, objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil {
+ if err := b.copyFile(objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil {
return err
}
case strings.HasSuffix(name, _goos):
targ := file[:len(name)-len(_goos)] + "_GOOS." + ext
- if err := b.copyFile(a, objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil {
+ if err := b.copyFile(objdir+targ, filepath.Join(a.Package.Dir, file), 0666, true); err != nil {
return err
}
}
@@ -638,7 +695,7 @@ func (b *Builder) build(a *Action) (err error) {
}
// For gccgo on ELF systems, we write the build ID as an assembler file.
- // This lets us set the the SHF_EXCLUDE flag.
+ // This lets us set the SHF_EXCLUDE flag.
// This is read by readGccgoArchive in cmd/internal/buildid/buildid.go.
if a.buildID != "" && cfg.BuildToolchainName == "gccgo" {
switch cfg.Goos {
@@ -695,16 +752,189 @@ func (b *Builder) build(a *Action) (err error) {
return nil
}
+func (b *Builder) cacheObjdirFile(a *Action, c *cache.Cache, name string) error {
+ f, err := os.Open(a.Objdir + name)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, _, err = c.Put(cache.Subkey(a.actionID, name), f)
+ return err
+}
+
+func (b *Builder) findCachedObjdirFile(a *Action, c *cache.Cache, name string) (string, error) {
+ file, _, err := c.GetFile(cache.Subkey(a.actionID, name))
+ if err != nil {
+ return "", err
+ }
+ return file, nil
+}
+
+func (b *Builder) loadCachedObjdirFile(a *Action, c *cache.Cache, name string) error {
+ cached, err := b.findCachedObjdirFile(a, c, name)
+ if err != nil {
+ return err
+ }
+ return b.copyFile(a.Objdir+name, cached, 0666, true)
+}
+
+func (b *Builder) cacheCgoHdr(a *Action) {
+ c := cache.Default()
+ if c == nil {
+ return
+ }
+ b.cacheObjdirFile(a, c, "_cgo_install.h")
+}
+
+func (b *Builder) loadCachedCgoHdr(a *Action) bool {
+ c := cache.Default()
+ if c == nil {
+ return false
+ }
+ err := b.loadCachedObjdirFile(a, c, "_cgo_install.h")
+ return err == nil
+}
+
+func (b *Builder) cacheGofiles(a *Action, gofiles []string) {
+ c := cache.Default()
+ if c == nil {
+ return
+ }
+ var buf bytes.Buffer
+ for _, file := range gofiles {
+ if !strings.HasPrefix(file, a.Objdir) {
+ // not generated
+ buf.WriteString("./")
+ buf.WriteString(file)
+ buf.WriteString("\n")
+ continue
+ }
+ name := file[len(a.Objdir):]
+ buf.WriteString(name)
+ buf.WriteString("\n")
+ if err := b.cacheObjdirFile(a, c, name); err != nil {
+ return
+ }
+ }
+ c.PutBytes(cache.Subkey(a.actionID, "gofiles"), buf.Bytes())
+}
+
+func (b *Builder) loadCachedVet(a *Action) bool {
+ c := cache.Default()
+ if c == nil {
+ return false
+ }
+ list, _, err := c.GetBytes(cache.Subkey(a.actionID, "gofiles"))
+ if err != nil {
+ return false
+ }
+ var gofiles []string
+ for _, name := range strings.Split(string(list), "\n") {
+ if name == "" { // end of list
+ continue
+ }
+ if strings.HasPrefix(name, "./") {
+ gofiles = append(gofiles, name[2:])
+ continue
+ }
+ if err := b.loadCachedObjdirFile(a, c, name); err != nil {
+ return false
+ }
+ gofiles = append(gofiles, a.Objdir+name)
+ }
+ buildVetConfig(a, gofiles)
+ return true
+}
+
+func (b *Builder) loadCachedGoFiles(a *Action) bool {
+ c := cache.Default()
+ if c == nil {
+ return false
+ }
+ list, _, err := c.GetBytes(cache.Subkey(a.actionID, "gofiles"))
+ if err != nil {
+ return false
+ }
+ var files []string
+ for _, name := range strings.Split(string(list), "\n") {
+ if name == "" { // end of list
+ continue
+ }
+ if strings.HasPrefix(name, "./") {
+ files = append(files, name[len("./"):])
+ continue
+ }
+ file, err := b.findCachedObjdirFile(a, c, name)
+ if err != nil {
+ return false
+ }
+ files = append(files, file)
+ }
+ a.Package.CompiledGoFiles = files
+ return true
+}
+
+// vetConfig is the configuration passed to vet describing a single package.
type vetConfig struct {
- Compiler string
- Dir string
- GoFiles []string
- ImportMap map[string]string
- PackageFile map[string]string
- Standard map[string]bool
- ImportPath string
+ Compiler string // compiler name (gc, gccgo)
+ Dir string // directory containing package
+ ImportPath string // canonical import path ("package path")
+ GoFiles []string // absolute paths to package source files
+
+ ImportMap map[string]string // map import path in source code to package path
+ PackageFile map[string]string // map package path to .a file with export data
+ Standard map[string]bool // map package path to whether it's in the standard library
+ PackageVetx map[string]string // map package path to vetx data from earlier vet run
+ VetxOnly bool // only compute vetx data; don't report detected problems
+ VetxOutput string // write vetx data to this output file
+
+ SucceedOnTypecheckFailure bool // awful hack; see #18395 and below
+}
+
+func buildVetConfig(a *Action, gofiles []string) {
+ // Pass list of absolute paths to vet,
+ // so that vet's error messages will use absolute paths,
+ // so that we can reformat them relative to the directory
+ // in which the go command is invoked.
+ vcfg := &vetConfig{
+ Compiler: cfg.BuildToolchainName,
+ Dir: a.Package.Dir,
+ GoFiles: mkAbsFiles(a.Package.Dir, gofiles),
+ ImportPath: a.Package.ImportPath,
+ ImportMap: make(map[string]string),
+ PackageFile: make(map[string]string),
+ Standard: make(map[string]bool),
+ }
+ a.vetCfg = vcfg
+ for i, raw := range a.Package.Internal.RawImports {
+ final := a.Package.Imports[i]
+ vcfg.ImportMap[raw] = final
+ }
- SucceedOnTypecheckFailure bool
+ // Compute the list of mapped imports in the vet config
+ // so that we can add any missing mappings below.
+ vcfgMapped := make(map[string]bool)
+ for _, p := range vcfg.ImportMap {
+ vcfgMapped[p] = true
+ }
+
+ for _, a1 := range a.Deps {
+ p1 := a1.Package
+ if p1 == nil || p1.ImportPath == "" {
+ continue
+ }
+ // Add import mapping if needed
+ // (for imports like "runtime/cgo" that appear only in generated code).
+ if !vcfgMapped[p1.ImportPath] {
+ vcfg.ImportMap[p1.ImportPath] = p1.ImportPath
+ }
+ if a1.built != "" {
+ vcfg.PackageFile[p1.ImportPath] = a1.built
+ }
+ if p1.Standard {
+ vcfg.Standard[p1.ImportPath] = true
+ }
+ }
}
// VetTool is the path to an alternate vet tool binary.
@@ -719,13 +949,51 @@ func (b *Builder) vet(a *Action) error {
// a.Deps[0] is the build of the package being vetted.
// a.Deps[1] is the build of the "fmt" package.
+ a.Failed = false // vet of dependency may have failed but we can still succeed
+
+ if a.Deps[0].Failed {
+ // The build of the package has failed. Skip vet check.
+ // Vet could return export data for non-typecheck errors,
+ // but we ignore it because the package cannot be compiled.
+ return nil
+ }
+
vcfg := a.Deps[0].vetCfg
if vcfg == nil {
// Vet config should only be missing if the build failed.
- if !a.Deps[0].Failed {
- return fmt.Errorf("vet config not found")
+ return fmt.Errorf("vet config not found")
+ }
+
+ vcfg.VetxOnly = a.VetxOnly
+ vcfg.VetxOutput = a.Objdir + "vet.out"
+ vcfg.PackageVetx = make(map[string]string)
+
+ h := cache.NewHash("vet " + a.Package.ImportPath)
+ fmt.Fprintf(h, "vet %q\n", b.toolID("vet"))
+
+ // Note: We could decide that vet should compute export data for
+ // all analyses, in which case we don't need to include the flags here.
+ // But that would mean that if an analysis causes problems like
+ // unexpected crashes there would be no way to turn it off.
+ // It seems better to let the flags disable export analysis too.
+ fmt.Fprintf(h, "vetflags %q\n", VetFlags)
+
+ fmt.Fprintf(h, "pkg %q\n", a.Deps[0].actionID)
+ for _, a1 := range a.Deps {
+ if a1.Mode == "vet" && a1.built != "" {
+ fmt.Fprintf(h, "vetout %q %s\n", a1.Package.ImportPath, b.fileHash(a1.built))
+ vcfg.PackageVetx[a1.Package.ImportPath] = a1.built
+ }
+ }
+ key := cache.ActionID(h.Sum())
+
+ if vcfg.VetxOnly {
+ if c := cache.Default(); c != nil && !cfg.BuildA {
+ if file, _, err := c.GetFile(key); err == nil {
+ a.built = file
+ return nil
+ }
}
- return nil
}
if vcfg.ImportMap["fmt"] == "" {
@@ -742,7 +1010,9 @@ func (b *Builder) vet(a *Action) error {
// so at least for now assume the bug is in vet.
// We know of at least #18395.
// TODO(rsc,gri): Try to remove this for Go 1.11.
- vcfg.SucceedOnTypecheckFailure = cfg.CmdName == "test"
+ //
+ // Disabled 2018-04-20. Let's see if we can do without it.
+ // vcfg.SucceedOnTypecheckFailure = cfg.CmdName == "test"
js, err := json.MarshalIndent(vcfg, "", "\t")
if err != nil {
@@ -753,7 +1023,7 @@ func (b *Builder) vet(a *Action) error {
return err
}
- var env []string
+ env := b.cCompilerEnv()
if cfg.BuildToolchainName == "gccgo" {
env = append(env, "GCCGO="+BuildToolchain.compiler())
}
@@ -763,7 +1033,18 @@ func (b *Builder) vet(a *Action) error {
if tool == "" {
tool = base.Tool("vet")
}
- return b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg")
+ runErr := b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg")
+
+ // If vet wrote export data, save it for input to future vets.
+ if f, err := os.Open(vcfg.VetxOutput); err == nil {
+ a.built = vcfg.VetxOutput
+ if c := cache.Default(); c != nil {
+ c.Put(key, f)
+ }
+ f.Close()
+ }
+
+ return runErr
}
// linkActionID computes the action ID for a link action.
@@ -849,7 +1130,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
// link is the action for linking a single command.
// Note that any new influence on this logic must be reported in b.linkActionID above as well.
func (b *Builder) link(a *Action) (err error) {
- if b.useCache(a, a.Package, b.linkActionID(a), a.Package.Target) {
+ if b.useCache(a, a.Package, b.linkActionID(a), a.Package.Target) || b.IsCmdList {
return nil
}
defer b.flushOutput(a)
@@ -886,11 +1167,11 @@ func (b *Builder) link(a *Action) (err error) {
// We still call updateBuildID to update a.buildID, which is important
// for test result caching, but passing rewrite=false (final arg)
// means we don't actually rewrite the binary, nor store the
- // result into the cache.
- // Not calling updateBuildID means we also don't insert these
- // binaries into the build object cache. That's probably a net win:
+ // result into the cache. That's probably a net win:
// less cache space wasted on large binaries we are not likely to
// need again. (On the other hand it does make repeated go test slower.)
+ // It also makes repeated go run slower, which is a win in itself:
+ // we don't want people to treat go run like a scripting environment.
if err := b.updateBuildID(a, a.Target, !a.Package.Internal.OmitDebug); err != nil {
return err
}
@@ -922,36 +1203,64 @@ func (b *Builder) PkgconfigCmd() string {
}
// splitPkgConfigOutput parses the pkg-config output into a slice of
-// flags. pkg-config always uses \ to escape special characters.
-func splitPkgConfigOutput(out []byte) []string {
+// flags. This implements the algorithm from pkgconf/libpkgconf/argvsplit.c.
+func splitPkgConfigOutput(out []byte) ([]string, error) {
if len(out) == 0 {
- return nil
+ return nil, nil
}
var flags []string
- flag := make([]byte, len(out))
- r, w := 0, 0
- for r < len(out) {
- switch out[r] {
- case ' ', '\t', '\r', '\n':
- if w > 0 {
- flags = append(flags, string(flag[:w]))
+ flag := make([]byte, 0, len(out))
+ escaped := false
+ quote := byte(0)
+
+ for _, c := range out {
+ if escaped {
+ if quote != 0 {
+ switch c {
+ case '$', '`', '"', '\\':
+ default:
+ flag = append(flag, '\\')
+ }
+ flag = append(flag, c)
+ } else {
+ flag = append(flag, c)
}
- w = 0
- case '\\':
- r++
- fallthrough
- default:
- if r < len(out) {
- flag[w] = out[r]
- w++
+ escaped = false
+ } else if quote != 0 {
+ if c == quote {
+ quote = 0
+ } else {
+ switch c {
+ case '\\':
+ escaped = true
+ default:
+ flag = append(flag, c)
+ }
+ }
+ } else if strings.IndexByte(" \t\n\v\f\r", c) < 0 {
+ switch c {
+ case '\\':
+ escaped = true
+ case '\'', '"':
+ quote = c
+ default:
+ flag = append(flag, c)
}
+ } else if len(flag) != 0 {
+ flags = append(flags, string(flag))
+ flag = flag[:0]
}
- r++
}
- if w > 0 {
- flags = append(flags, string(flag[:w]))
+ if escaped {
+ return nil, errors.New("broken character escaping in pkgconf output ")
+ }
+ if quote != 0 {
+ return nil, errors.New("unterminated quoted string in pkgconf output ")
+ } else if len(flag) != 0 {
+ flags = append(flags, string(flag))
}
- return flags
+
+ return flags, nil
}
// Calls pkg-config if needed and returns the cflags/ldflags needed to build the package.
@@ -976,21 +1285,24 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string,
}
}
var out []byte
- out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs)
+ out, err = b.runOut(p.Dir, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs)
if err != nil {
- b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pcflags, " ")+strings.Join(pkgs, " "), string(out))
+ b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out))
b.Print(err.Error() + "\n")
return nil, nil, errPrintedOutput
}
if len(out) > 0 {
- cflags = splitPkgConfigOutput(out)
+ cflags, err = splitPkgConfigOutput(out)
+ if err != nil {
+ return nil, nil, err
+ }
if err := checkCompilerFlags("CFLAGS", "pkg-config --cflags", cflags); err != nil {
return nil, nil, err
}
}
- out, err = b.runOut(p.Dir, p.ImportPath, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs)
+ out, err = b.runOut(p.Dir, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs)
if err != nil {
- b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pcflags, " ")+strings.Join(pkgs, " "), string(out))
+ b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out))
b.Print(err.Error() + "\n")
return nil, nil, errPrintedOutput
}
@@ -1051,7 +1363,7 @@ func (b *Builder) linkSharedActionID(a *Action) cache.ActionID {
}
func (b *Builder) linkShared(a *Action) (err error) {
- if b.useCache(a, nil, b.linkSharedActionID(a), a.Target) {
+ if b.useCache(a, nil, b.linkSharedActionID(a), a.Target) || b.IsCmdList {
return nil
}
defer b.flushOutput(a)
@@ -1096,7 +1408,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
// so the built target is not in the a1.Objdir tree that b.cleanup(a1) removes.
if a1.built == a.Target {
a.built = a.Target
- b.cleanup(a1)
+ if !a.buggyInstall {
+ b.cleanup(a1)
+ }
// Whether we're smart enough to avoid a complete rebuild
// depends on exactly what the staleness and rebuild algorithms
// are, as well as potentially the state of the Go build cache.
@@ -1115,13 +1429,17 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
// We want to hide that awful detail as much as possible, so don't
// advertise it by touching the mtimes (usually the libraries are up
// to date).
- if !a.buggyInstall && !b.ComputeStaleOnly {
+ if !a.buggyInstall && !b.IsCmdList {
now := time.Now()
os.Chtimes(a.Target, now, now)
}
return nil
}
- if b.ComputeStaleOnly {
+
+ // If we're building for go list -export,
+ // never install anything; just keep the cache reference.
+ if b.IsCmdList {
+ a.built = a1.built
return nil
}
@@ -1146,9 +1464,11 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
}
}
- defer b.cleanup(a1)
+ if !a.buggyInstall {
+ defer b.cleanup(a1)
+ }
- return b.moveOrCopyFile(a, a.Target, a1.built, perm, false)
+ return b.moveOrCopyFile(a.Target, a1.built, perm, false)
}
// cleanup removes a's object dir to keep the amount of
@@ -1158,14 +1478,18 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
func (b *Builder) cleanup(a *Action) {
if !cfg.BuildWork {
if cfg.BuildX {
- b.Showcmd("", "rm -r %s", a.Objdir)
+ // Don't say we are removing the directory if
+ // we never created it.
+ if _, err := os.Stat(a.Objdir); err == nil || cfg.BuildN {
+ b.Showcmd("", "rm -r %s", a.Objdir)
+ }
}
os.RemoveAll(a.Objdir)
}
}
// moveOrCopyFile is like 'mv src dst' or 'cp src dst'.
-func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, force bool) error {
+func (b *Builder) moveOrCopyFile(dst, src string, perm os.FileMode, force bool) error {
if cfg.BuildN {
b.Showcmd("", "mv %s %s", src, dst)
return nil
@@ -1176,7 +1500,7 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f
// If the source is in the build cache, we need to copy it.
if strings.HasPrefix(src, cache.DefaultDir()) {
- return b.copyFile(a, dst, src, perm, force)
+ return b.copyFile(dst, src, perm, force)
}
// On Windows, always copy the file, so that we respect the NTFS
@@ -1184,7 +1508,7 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f
// What matters here is not cfg.Goos (the system we are building
// for) but runtime.GOOS (the system we are building on).
if runtime.GOOS == "windows" {
- return b.copyFile(a, dst, src, perm, force)
+ return b.copyFile(dst, src, perm, force)
}
// If the destination directory has the group sticky bit set,
@@ -1192,7 +1516,7 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f
// https://golang.org/issue/18878
if fi, err := os.Stat(filepath.Dir(dst)); err == nil {
if fi.IsDir() && (fi.Mode()&os.ModeSetgid) != 0 {
- return b.copyFile(a, dst, src, perm, force)
+ return b.copyFile(dst, src, perm, force)
}
}
@@ -1222,11 +1546,11 @@ func (b *Builder) moveOrCopyFile(a *Action, dst, src string, perm os.FileMode, f
}
}
- return b.copyFile(a, dst, src, perm, force)
+ return b.copyFile(dst, src, perm, force)
}
// copyFile is like 'cp src dst'.
-func (b *Builder) copyFile(a *Action, dst, src string, perm os.FileMode, force bool) error {
+func (b *Builder) copyFile(dst, src string, perm os.FileMode, force bool) error {
if cfg.BuildN || cfg.BuildX {
b.Showcmd("", "cp %s %s", src, dst)
if cfg.BuildN {
@@ -1317,12 +1641,12 @@ func (b *Builder) installHeader(a *Action) error {
}
}
- return b.moveOrCopyFile(a, a.Target, src, 0666, true)
+ return b.moveOrCopyFile(a.Target, src, 0666, true)
}
// cover runs, in effect,
// go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go
-func (b *Builder) cover(a *Action, dst, src string, perm os.FileMode, varName string) error {
+func (b *Builder) cover(a *Action, dst, src string, varName string) error {
return b.run(a, a.Objdir, "cover "+a.Package.ImportPath, nil,
cfg.BuildToolexec,
base.Tool("cover"),
@@ -1344,6 +1668,7 @@ var objectMagic = [][]byte{
{0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386
{0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64
{0x00, 0x00, 0x06, 0x47}, // Plan 9 arm
+ {0x00, 0x61, 0x73, 0x6D}, // WASM
{0x01, 0xDF}, // XCOFF32
{0x01, 0xF7}, // XCOFF64
}
@@ -1389,11 +1714,11 @@ func mayberemovefile(s string) {
func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string {
cmd := fmt.Sprintf(format, args...)
if dir != "" && dir != "/" {
- to := "."
+ dot := " ."
if dir[len(dir)-1] == filepath.Separator {
- to += string(filepath.Separator)
+ dot += string(filepath.Separator)
}
- cmd = strings.Replace(" "+cmd, " "+dir, " "+to, -1)[1:]
+ cmd = strings.Replace(" "+cmd, " "+dir, dot, -1)[1:]
if b.scriptDir != dir {
b.scriptDir = dir
cmd = "cd " + dir + "\n" + cmd
@@ -1472,7 +1797,7 @@ var cgoTypeSigRe = regexp.MustCompile(`\b_C2?(type|func|var|macro)_\B`)
// If the command fails, run prints information about the failure
// and returns a non-nil error.
func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...interface{}) error {
- out, err := b.runOut(dir, desc, env, cmdargs...)
+ out, err := b.runOut(dir, env, cmdargs...)
if len(out) > 0 {
if desc == "" {
desc = b.fmtcmd(dir, "%s", strings.Join(str.StringList(cmdargs...), " "))
@@ -1504,7 +1829,7 @@ func (b *Builder) processOutput(out []byte) string {
// runOut runs the command given by cmdline in the directory dir.
// It returns the command output and any errors that occurred.
-func (b *Builder) runOut(dir string, desc string, env []string, cmdargs ...interface{}) ([]byte, error) {
+func (b *Builder) runOut(dir string, env []string, cmdargs ...interface{}) ([]byte, error) {
cmdline := str.StringList(cmdargs...)
for _, arg := range cmdline {
@@ -1540,6 +1865,8 @@ func (b *Builder) runOut(dir string, desc string, env []string, cmdargs ...inter
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Stdout = &buf
cmd.Stderr = &buf
+ cleanup := passLongArgsInResponseFiles(cmd)
+ defer cleanup()
cmd.Dir = dir
cmd.Env = base.MergeEnvLists(env, base.EnvForDir(cmd.Dir, os.Environ()))
err := cmd.Run()
@@ -1576,6 +1903,13 @@ func joinUnambiguously(a []string) string {
return buf.String()
}
+// cCompilerEnv returns environment variables to set when running the
+// C compiler. This is needed to disable escape codes in clang error
+// messages that confuse tools like cgo.
+func (b *Builder) cCompilerEnv() []string {
+ return []string{"TERM=dumb"}
+}
+
// mkdir makes the named directory.
func (b *Builder) Mkdir(dir string) error {
// Make Mkdir(a.Objdir) a no-op instead of an error when a.Objdir == "".
@@ -1723,7 +2057,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
if !filepath.IsAbs(outfile) {
outfile = filepath.Join(p.Dir, outfile)
}
- output, err := b.runOut(filepath.Dir(file), desc, nil, compiler, flags, "-o", outfile, "-c", filepath.Base(file))
+ output, err := b.runOut(filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file))
if len(output) > 0 {
// On FreeBSD 11, when we pass -g to clang 3.8 it
// invokes its internal assembler with -dwarf-version=2.
@@ -1763,7 +2097,7 @@ func (b *Builder) gccld(p *load.Package, objdir, out string, flags []string, obj
} else {
cmd = b.GccCmd(p.Dir, objdir)
}
- return b.run(nil, p.Dir, p.ImportPath, nil, cmd, "-o", out, objs, flags)
+ return b.run(nil, p.Dir, p.ImportPath, b.cCompilerEnv(), cmd, "-o", out, objs, flags)
}
// Grab these before main helpfully overwrites them.
@@ -2068,7 +2402,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
// along to the host linker. At this point in the code, cgoLDFLAGS
// consists of the original $CGO_LDFLAGS (unchecked) and all the
// flags put together from source code (checked).
- var cgoenv []string
+ cgoenv := b.cCompilerEnv()
if len(cgoLDFLAGS) > 0 {
flags := make([]string, len(cgoLDFLAGS))
for i, f := range cgoLDFLAGS {
@@ -2195,7 +2529,15 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
// we need to use -pie for Linux/ARM to get accurate imported sym
ldflags := cgoLDFLAGS
if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" {
- ldflags = append(ldflags, "-pie")
+ // -static -pie doesn't make sense, and causes link errors.
+ // Issue 26197.
+ n := make([]string, 0, len(ldflags))
+ for _, flag := range ldflags {
+ if flag != "-static" {
+ n = append(n, flag)
+ }
+ }
+ ldflags = append(n, "-pie")
}
if err := b.gccld(p, objdir, dynobj, ldflags, linkobj); err != nil {
return err
@@ -2206,7 +2548,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = []string{"-dynlinker"} // record path to dynamic linker
}
- return b.run(a, p.Dir, p.ImportPath, nil, cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
+ return b.run(a, p.Dir, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
}
// Run SWIG on all SWIG input files.
@@ -2256,7 +2598,7 @@ var (
)
func (b *Builder) swigDoVersionCheck() error {
- out, err := b.runOut("", "", nil, "swig", "-version")
+ out, err := b.runOut("", nil, "swig", "-version")
if err != nil {
return err
}
@@ -2411,19 +2753,19 @@ func (b *Builder) swigOne(a *Action, p *load.Package, file, objdir string, pcCFL
args = append(args, "-c++")
}
- out, err := b.runOut(p.Dir, p.ImportPath, nil, "swig", args, file)
+ out, err := b.runOut(p.Dir, nil, "swig", args, file)
if err != nil {
if len(out) > 0 {
if bytes.Contains(out, []byte("-intgosize")) || bytes.Contains(out, []byte("-cgo")) {
return "", "", errors.New("must have SWIG version >= 3.0.6")
}
- b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(out)) // swig error
+ b.showOutput(a, p.Dir, p.Desc(), b.processOutput(out)) // swig error
return "", "", errPrintedOutput
}
return "", "", err
}
if len(out) > 0 {
- b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(out)) // swig warning
+ b.showOutput(a, p.Dir, p.Desc(), b.processOutput(out)) // swig warning
}
// If the input was x.swig, the output is x.go in the objdir.
@@ -2473,3 +2815,78 @@ func mkAbsFiles(dir string, files []string) []string {
}
return abs
}
+
+// passLongArgsInResponseFiles modifies cmd on Windows such that, for
+// certain programs, long arguments are passed in "response files", a
+// file on disk with the arguments, with one arg per line. An actual
+// argument starting with '@' means that the rest of the argument is
+// a filename of arguments to expand.
+//
+// See Issue 18468.
+func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) {
+ cleanup = func() {} // no cleanup by default
+
+ var argLen int
+ for _, arg := range cmd.Args {
+ argLen += len(arg)
+ }
+
+ // If we're not approaching 32KB of args, just pass args normally.
+ // (use 30KB instead to be conservative; not sure how accounting is done)
+ if !useResponseFile(cmd.Path, argLen) {
+ return
+ }
+
+ tf, err := ioutil.TempFile("", "args")
+ if err != nil {
+ log.Fatalf("error writing long arguments to response file: %v", err)
+ }
+ cleanup = func() { os.Remove(tf.Name()) }
+ var buf bytes.Buffer
+ for _, arg := range cmd.Args[1:] {
+ fmt.Fprintf(&buf, "%s\n", arg)
+ }
+ if _, err := tf.Write(buf.Bytes()); err != nil {
+ tf.Close()
+ cleanup()
+ log.Fatalf("error writing long arguments to response file: %v", err)
+ }
+ if err := tf.Close(); err != nil {
+ cleanup()
+ log.Fatalf("error writing long arguments to response file: %v", err)
+ }
+ cmd.Args = []string{cmd.Args[0], "@" + tf.Name()}
+ return cleanup
+}
+
+func useResponseFile(path string, argLen int) bool {
+ // Unless we're on Windows, don't use response files.
+ if runtime.GOOS != "windows" {
+ return false
+ }
+
+ // Unless the program uses objabi.Flagparse, which understands
+ // response files, don't use response files.
+ // TODO: do we need more commands? asm? cgo? For now, no.
+ prog := strings.TrimSuffix(filepath.Base(path), ".exe")
+ switch prog {
+ case "compile", "link":
+ default:
+ return false
+ }
+
+ // Windows has a limit of 32 KB arguments. To be conservative and not
+ // worry about whether that includes spaces or not, just use 30 KB.
+ if argLen > (30 << 10) {
+ return true
+ }
+
+ // On the Go build system, use response files about 10% of the
+ // time, just to excercise this codepath.
+ isBuilder := os.Getenv("GO_BUILDER_NAME") != ""
+ if isBuilder && rand.Intn(10) == 0 {
+ return true
+ }
+
+ return false
+}