diff options
Diffstat (limited to 'libgo/go/cmd/go/internal/work/exec.go')
-rw-r--r-- | libgo/go/cmd/go/internal/work/exec.go | 757 |
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 +} |