// Note that any new influence on this logic must be reported in b.buildActionID above as well.
func (b *Builder) build(ctx context.Context, a *Action) (err error) {
p := a.Package
+ sh := b.Shell(a)
bit := func(x uint32, b bool) uint32 {
if b {
// different sections of the bootstrap script have to
// be merged, the banners give patch something
// to use to find its context.
- b.Print("\n#\n# " + p.ImportPath + "\n#\n\n")
+ sh.Print("\n#\n# " + p.ImportPath + "\n#\n\n")
}
if cfg.BuildV {
- b.Print(p.ImportPath + "\n")
+ sh.Print(p.ImportPath + "\n")
}
if p.Error != nil {
return err
}
- if err := b.Mkdir(a.Objdir); err != nil {
+ if err := sh.Mkdir(a.Objdir); err != nil {
return err
}
objdir := a.Objdir
// make target directory
dir, _ := filepath.Split(a.Target)
if dir != "" {
- if err := b.Mkdir(dir); err != nil {
+ if err := sh.Mkdir(dir); err != nil {
return err
}
}
from := mkAbs(p.Dir, fs[i])
opath, _ := fsys.OverlayPath(from)
dst := objdir + filepath.Base(fs[i])
- if err := b.CopyFile(dst, opath, 0666, false); err != nil {
+ if err := sh.CopyFile(dst, opath, 0666, false); err != nil {
return err
}
a.nonGoOverlay[from] = dst
if p.Internal.BuildInfo != nil && cfg.ModulesEnabled {
prog := modload.ModInfoProg(p.Internal.BuildInfo.String(), cfg.BuildToolchainName == "gccgo")
if len(prog) > 0 {
- if err := b.writeFile(objdir+"_gomod_.go", prog); err != nil {
+ if err := sh.writeFile(objdir+"_gomod_.go", prog); 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(), embedcfg, symabis, len(sfiles) > 0, gofiles)
- if err := b.reportCmd(a, "", "", out, err); err != nil {
+ if err := sh.reportCmd("", "", out, err); err != nil {
return err
}
if ofile != objpkg {
switch {
case strings.HasSuffix(name, _goos_goarch):
targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext
- if err := b.CopyFile(objdir+targ, filepath.Join(p.Dir, file), 0666, true); err != nil {
+ if err := sh.CopyFile(objdir+targ, filepath.Join(p.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(objdir+targ, filepath.Join(p.Dir, file), 0666, true); err != nil {
+ if err := sh.CopyFile(objdir+targ, filepath.Join(p.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(objdir+targ, filepath.Join(p.Dir, file), 0666, true); err != nil {
+ if err := sh.CopyFile(objdir+targ, filepath.Join(p.Dir, file), 0666, true); err != nil {
return err
}
}
// path, but the content of the error doesn't matter because msg is
// non-empty.
err := errors.New("invalid directive")
- return b.reportCmd(a, "", "", msg.Bytes(), err)
+ return b.Shell(a).reportCmd("", "", msg.Bytes(), err)
}
return nil
}
if err != nil {
return err
}
- return b.CopyFile(a.Objdir+name, cached, 0666, true)
+ return b.Shell(a).CopyFile(a.Objdir+name, cached, 0666, true)
}
func (b *Builder) cacheCgoHdr(a *Action) {
return fmt.Errorf("vet config not found")
}
+ sh := b.Shell(a)
+
vcfg.VetxOnly = a.VetxOnly
vcfg.VetxOutput = a.Objdir + "vet.out"
vcfg.PackageVetx = make(map[string]string)
return fmt.Errorf("internal error marshaling vet config: %v", err)
}
js = append(js, '\n')
- if err := b.writeFile(a.Objdir+"vet.cfg", js); err != nil {
+ if err := sh.writeFile(a.Objdir+"vet.cfg", js); err != nil {
return err
}
if tool == "" {
tool = base.Tool("vet")
}
- runErr := b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, vetFlags, a.Objdir+"vet.cfg")
+ runErr := sh.run(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 {
}
defer b.flushOutput(a)
- if err := b.Mkdir(a.Objdir); err != nil {
+ sh := b.Shell(a)
+ if err := sh.Mkdir(a.Objdir); err != nil {
return err
}
// make target directory
dir, _ := filepath.Split(a.Target)
if dir != "" {
- if err := b.Mkdir(dir); err != nil {
+ if err := sh.Mkdir(dir); err != nil {
return err
}
}
info = a.Package.Internal.BuildInfo.String()
}
fmt.Fprintf(&icfg, "modinfo %q\n", modload.ModInfoData(info))
- return b.writeFile(file, icfg.Bytes())
+ return b.Shell(a).writeFile(file, icfg.Bytes())
}
// PkgconfigCmd returns a pkg-config binary name
// Calls pkg-config if needed and returns the cflags/ldflags needed to build a's package.
func (b *Builder) getPkgConfigFlags(a *Action) (cflags, ldflags []string, err error) {
p := a.Package
+ sh := b.Shell(a)
if pcargs := p.CgoPkgConfig; len(pcargs) > 0 {
// pkg-config permits arguments to appear anywhere in
// the command line. Move them all to the front, before --.
}
}
var out []byte
- out, err = b.runOut(nil, p.Dir, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs)
+ out, err = sh.runOut(p.Dir, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs)
if err != nil {
desc := b.PkgconfigCmd() + " --cflags " + strings.Join(pcflags, " ") + " -- " + strings.Join(pkgs, " ")
- return nil, nil, b.reportCmd(a, desc, "", out, err)
+ return nil, nil, sh.reportCmd(desc, "", out, err)
}
if len(out) > 0 {
cflags, err = splitPkgConfigOutput(bytes.TrimSpace(out))
return nil, nil, err
}
}
- out, err = b.runOut(nil, p.Dir, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs)
+ out, err = sh.runOut(p.Dir, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs)
if err != nil {
desc := b.PkgconfigCmd() + " --libs " + strings.Join(pcflags, " ") + " -- " + strings.Join(pkgs, " ")
- return nil, nil, b.reportCmd(a, desc, "", out, err)
+ return nil, nil, sh.reportCmd(desc, "", out, err)
}
if len(out) > 0 {
// We need to handle path with spaces so that C:/Program\ Files can pass
return err
}
+ sh := b.Shell(a)
a1 := a.Deps[0]
if !cfg.BuildN {
- if err := b.Mkdir(filepath.Dir(a.Target)); err != nil {
+ if err := sh.Mkdir(filepath.Dir(a.Target)); err != nil {
return err
}
}
- return b.writeFile(a.Target, []byte(filepath.Base(a1.Target)+"\n"))
+ return sh.writeFile(a.Target, []byte(filepath.Base(a1.Target)+"\n"))
}
func (b *Builder) linkSharedActionID(a *Action) cache.ActionID {
return err
}
- if err := b.Mkdir(a.Objdir); err != nil {
+ if err := b.Shell(a).Mkdir(a.Objdir); err != nil {
return err
}
err = fmt.Errorf("go %s%s%s: %v", cfg.CmdName, sep, path, err)
}
}()
+ sh := b.Shell(a)
a1 := a.Deps[0]
a.buildID = a1.buildID
// to date).
if !a.buggyInstall && !b.IsCmdList {
if cfg.BuildN {
- b.Showcmd("", "touch %s", a.Target)
+ sh.ShowCmd("", "touch %s", a.Target)
} else if err := AllowInstall(a); err == nil {
now := time.Now()
os.Chtimes(a.Target, now, now)
return err
}
- if err := b.Mkdir(a.Objdir); err != nil {
+ if err := sh.Mkdir(a.Objdir); err != nil {
return err
}
// make target directory
dir, _ := filepath.Split(a.Target)
if dir != "" {
- if err := b.Mkdir(dir); err != nil {
+ if err := sh.Mkdir(dir); err != nil {
return err
}
}
defer b.cleanup(a1)
}
- return b.moveOrCopyFile(a.Target, a1.built, perm, false)
+ return sh.moveOrCopyFile(a.Target, a1.built, perm, false)
}
// AllowInstall returns a non-nil error if this invocation of the go command is
// 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)
+ b.Shell(a).ShowCmd("", "rm -r %s", a.Objdir)
}
}
os.RemoveAll(a.Objdir)
}
// moveOrCopyFile is like 'mv src dst' or 'cp src dst'.
-func (b *Builder) moveOrCopyFile(dst, src string, perm fs.FileMode, force bool) error {
+func (sh *Shell) moveOrCopyFile(dst, src string, perm fs.FileMode, force bool) error {
if cfg.BuildN {
- b.Showcmd("", "mv %s %s", src, dst)
+ sh.ShowCmd("", "mv %s %s", src, dst)
return nil
}
// If the source is in the build cache, we need to copy it.
if strings.HasPrefix(src, cache.DefaultDir()) {
- return b.CopyFile(dst, src, perm, force)
+ return sh.CopyFile(dst, src, perm, force)
}
// On Windows, always copy the file, so that we respect the NTFS
// 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(dst, src, perm, force)
+ return sh.CopyFile(dst, src, perm, force)
}
// If the destination directory has the group sticky bit set,
// https://golang.org/issue/18878
if fi, err := os.Stat(filepath.Dir(dst)); err == nil {
if fi.IsDir() && (fi.Mode()&fs.ModeSetgid) != 0 {
- return b.CopyFile(dst, src, perm, force)
+ return sh.CopyFile(dst, src, perm, force)
}
}
if err := os.Chmod(src, mode); err == nil {
if err := os.Rename(src, dst); err == nil {
if cfg.BuildX {
- b.Showcmd("", "mv %s %s", src, dst)
+ sh.ShowCmd("", "mv %s %s", src, dst)
}
return nil
}
}
- return b.CopyFile(dst, src, perm, force)
+ return sh.CopyFile(dst, src, perm, force)
}
// copyFile is like 'cp src dst'.
-func (b *Builder) CopyFile(dst, src string, perm fs.FileMode, force bool) error {
+func (sh *Shell) CopyFile(dst, src string, perm fs.FileMode, force bool) error {
if cfg.BuildN || cfg.BuildX {
- b.Showcmd("", "cp %s %s", src, dst)
+ sh.ShowCmd("", "cp %s %s", src, dst)
if cfg.BuildN {
return nil
}
}
// writeFile writes the text to file.
-func (b *Builder) writeFile(file string, text []byte) error {
+func (sh *Shell) writeFile(file string, text []byte) error {
if cfg.BuildN || cfg.BuildX {
switch {
case len(text) == 0:
- b.Showcmd("", "echo -n > %s # internal", file)
+ sh.ShowCmd("", "echo -n > %s # internal", file)
case bytes.IndexByte(text, '\n') == len(text)-1:
// One line. Use a simpler "echo" command.
- b.Showcmd("", "echo '%s' > %s # internal", bytes.TrimSuffix(text, []byte("\n")), file)
+ sh.ShowCmd("", "echo '%s' > %s # internal", bytes.TrimSuffix(text, []byte("\n")), file)
default:
// Use the most general form.
- b.Showcmd("", "cat >%s << 'EOF' # internal\n%sEOF", file, text)
+ sh.ShowCmd("", "cat >%s << 'EOF' # internal\n%sEOF", file, text)
}
}
if cfg.BuildN {
// Install the cgo export header file, if there is one.
func (b *Builder) installHeader(ctx context.Context, a *Action) error {
+ sh := b.Shell(a)
+
src := a.Objdir + "_cgo_install.h"
if _, err := os.Stat(src); os.IsNotExist(err) {
// If the file does not exist, there are no exported
// at the right times (not missing rebuilds), here we should
// probably delete the installed header, if any.
if cfg.BuildX {
- b.Showcmd("", "# %s not created", src)
+ sh.ShowCmd("", "# %s not created", src)
}
return nil
}
dir, _ := filepath.Split(a.Target)
if dir != "" {
- if err := b.Mkdir(dir); err != nil {
+ if err := sh.Mkdir(dir); err != nil {
return err
}
}
- return b.moveOrCopyFile(a.Target, src, 0666, true)
+ return sh.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, varName string) error {
- return b.run(a, a.Objdir, "", nil,
+ return b.Shell(a).run(a.Objdir, "", nil,
cfg.BuildToolexec,
base.Tool("cover"),
"-mode", a.Package.Internal.Cover.Mode,
"-outfilelist", covoutputs,
}
args = append(args, infiles...)
- if err := b.run(a, a.Objdir, "", nil,
+ if err := b.Shell(a).run(a.Objdir, "", nil,
cfg.BuildToolexec, args); err != nil {
return nil, err
}
}
func (b *Builder) writeCoverPkgInputs(a *Action, pconfigfile string, covoutputsfile string, outfiles []string) error {
+ sh := b.Shell(a)
p := a.Package
p.Internal.Cover.Cfg = a.Objdir + "coveragecfg"
pcfg := covcmd.CoverPkgConfig{
return err
}
data = append(data, '\n')
- if err := b.writeFile(pconfigfile, data); err != nil {
+ if err := sh.writeFile(pconfigfile, data); err != nil {
return err
}
var sb strings.Builder
for i := range outfiles {
fmt.Fprintf(&sb, "%s\n", outfiles[i])
}
- return b.writeFile(covoutputsfile, []byte(sb.String()))
+ return sh.writeFile(covoutputsfile, []byte(sb.String()))
}
var objectMagic = [][]byte{
os.Remove(s)
}
-// fmtcmd formats a command in the manner of fmt.Sprintf but also:
+// fmtCmd formats a command in the manner of fmt.Sprintf but also:
//
-// fmtcmd replaces the value of b.WorkDir with $WORK.
-func (b *Builder) fmtcmd(dir string, format string, args ...any) string {
+// fmtCmd replaces the value of b.WorkDir with $WORK.
+func (sh *Shell) fmtCmd(dir string, format string, args ...any) string {
cmd := fmt.Sprintf(format, args...)
- if b.WorkDir != "" && !strings.HasPrefix(cmd, "cat ") {
- cmd = strings.ReplaceAll(cmd, b.WorkDir, "$WORK")
- escaped := strconv.Quote(b.WorkDir)
+ if sh.workDir != "" && !strings.HasPrefix(cmd, "cat ") {
+ cmd = strings.ReplaceAll(cmd, sh.workDir, "$WORK")
+ escaped := strconv.Quote(sh.workDir)
escaped = escaped[1 : len(escaped)-1] // strip quote characters
- if escaped != b.WorkDir {
+ if escaped != sh.workDir {
cmd = strings.ReplaceAll(cmd, escaped, "$WORK")
}
}
return cmd
}
-// Showcmd prints the given command to standard output
+// ShowCmd prints the given command to standard output
// for the implementation of -n or -x.
//
-// Showcmd also replaces the name of the current script directory with dot (.)
+// ShowCmd also replaces the name of the current script directory with dot (.)
// but only when it is at the beginning of a space-separated token.
//
-// If dir is not "" or "/" and not the current script directory, Showcmd first
+// If dir is not "" or "/" and not the current script directory, ShowCmd first
// prints a "cd" command to switch to dir and updates the script directory.
-func (b *Builder) Showcmd(dir string, format string, args ...any) {
- b.output.Lock()
- defer b.output.Unlock()
+func (sh *Shell) ShowCmd(dir string, format string, args ...any) {
+ // Use the output lock directly so we can manage scriptDir.
+ sh.printLock.Lock()
+ defer sh.printLock.Unlock()
- cmd := b.fmtcmd(dir, format, args...)
+ cmd := sh.fmtCmd(dir, format, args...)
if dir != "" && dir != "/" {
- if dir != b.scriptDir {
+ if dir != sh.scriptDir {
// Show changing to dir and update the current directory.
- b.Print(b.fmtcmd("", "cd %s\n", dir))
- b.scriptDir = dir
+ sh.printLocked(sh.fmtCmd("", "cd %s\n", dir))
+ sh.scriptDir = dir
}
// Replace scriptDir is our working directory. Replace it
// with "." in the command.
cmd = strings.ReplaceAll(" "+cmd, " "+dir, dot)[1:]
}
- b.Print(cmd + "\n")
+ sh.printLocked(cmd + "\n")
}
// reportCmd reports the output and exit status of a command. The cmdOut and
// desc is optional. If "", a.Package.Desc() is used.
//
// dir is optional. If "", a.Package.Dir is used.
-func (b *Builder) reportCmd(a *Action, desc, dir string, cmdOut []byte, cmdErr error) error {
+func (sh *Shell) reportCmd(desc, dir string, cmdOut []byte, cmdErr error) error {
if len(cmdOut) == 0 && cmdErr == nil {
// Common case
return nil
// Fetch defaults from the package.
var p *load.Package
+ a := sh.action
if a != nil {
p = a.Package
}
}
// Replace workDir with $WORK
- out = replacePrefix(out, b.WorkDir, "$WORK")
+ out = replacePrefix(out, sh.workDir, "$WORK")
// Rewrite mentions of dir with a relative path to dir
// when the relative path is shorter.
a.output = append(a.output, err.Error()...)
} else {
// Write directly to the Builder output.
- b.output.Lock()
- defer b.output.Unlock()
- b.Print(err.Error())
+ sh.Print(err.Error())
}
return nil
}
// run runs the command given by cmdline in the directory dir.
// 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 ...any) error {
- out, err := b.runOut(a, dir, env, cmdargs...)
+func (sh *Shell) run(dir string, desc string, env []string, cmdargs ...any) error {
+ out, err := sh.runOut(dir, env, cmdargs...)
if desc == "" {
- desc = b.fmtcmd(dir, "%s", strings.Join(str.StringList(cmdargs...), " "))
+ desc = sh.fmtCmd(dir, "%s", strings.Join(str.StringList(cmdargs...), " "))
}
- return b.reportCmd(a, desc, dir, out, err)
+ return sh.reportCmd(desc, dir, out, err)
}
// runOut runs the command given by cmdline in the directory dir.
// It returns the command output and any errors that occurred.
// It accumulates execution time in a.
-func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...any) ([]byte, error) {
+func (sh *Shell) runOut(dir string, env []string, cmdargs ...any) ([]byte, error) {
+ a := sh.action
+
cmdline := str.StringList(cmdargs...)
for _, arg := range cmdline {
}
}
envcmdline += joinUnambiguously(cmdline)
- b.Showcmd(dir, "%s", envcmdline)
+ sh.ShowCmd(dir, "%s", envcmdline)
if cfg.BuildN {
return nil, nil
}
}
// Mkdir makes the named directory.
-func (b *Builder) Mkdir(dir string) error {
+func (sh *Shell) Mkdir(dir string) error {
// Make Mkdir(a.Objdir) a no-op instead of an error when a.Objdir == "".
if dir == "" {
return nil
}
- b.exec.Lock()
- defer b.exec.Unlock()
// We can be a little aggressive about being
// sure directories exist. Skip repeated calls.
- if b.mkdirCache[dir] {
- return nil
- }
- b.mkdirCache[dir] = true
-
- if cfg.BuildN || cfg.BuildX {
- b.Showcmd("", "mkdir -p %s", dir)
- if cfg.BuildN {
- return nil
+ return sh.mkdirCache.Do(dir, func() error {
+ if cfg.BuildN || cfg.BuildX {
+ sh.ShowCmd("", "mkdir -p %s", dir)
+ if cfg.BuildN {
+ return nil
+ }
}
- }
- if err := os.MkdirAll(dir, 0777); err != nil {
- return err
- }
- return nil
+ return os.MkdirAll(dir, 0777)
+ })
}
// Symlink creates a symlink newname -> oldname.
-func (b *Builder) Symlink(oldname, newname string) error {
+func (sh *Shell) Symlink(oldname, newname string) error {
// It's not an error to try to recreate an existing symlink.
if link, err := os.Readlink(newname); err == nil && link == oldname {
return nil
}
if cfg.BuildN || cfg.BuildX {
- b.Showcmd("", "ln -s %s %s", oldname, newname)
+ sh.ShowCmd("", "ln -s %s %s", oldname, newname)
if cfg.BuildN {
return nil
}
// ccompile runs the given C or C++ compiler and creates an object from a single source file.
func (b *Builder) ccompile(a *Action, outfile string, flags []string, file string, compiler []string) error {
p := a.Package
+ sh := b.Shell(a)
file = mkAbs(p.Dir, file)
outfile = mkAbs(p.Dir, outfile)
if p, ok := a.nonGoOverlay[overlayPath]; ok {
overlayPath = p
}
- output, err := b.runOut(a, filepath.Dir(overlayPath), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(overlayPath))
+ output, err := sh.runOut(filepath.Dir(overlayPath), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(overlayPath))
// On FreeBSD 11, when we pass -g to clang 3.8 it
// invokes its internal assembler with -dwarf-version=2.
err = errors.New("warning promoted to error")
}
- return b.reportCmd(a, "", "", output, err)
+ return sh.reportCmd("", "", output, err)
}
// gccld runs the gcc linker to create an executable from a set of object files.
// Any error output is only displayed for BuildN or BuildX.
func (b *Builder) gccld(a *Action, objdir, outfile string, flags []string, objs []string) error {
p := a.Package
+ sh := b.Shell(a)
var cmd []string
if len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 {
cmd = b.GxxCmd(p.Dir, objdir)
}
cmdargs := []any{cmd, "-o", outfile, objs, flags}
- out, err := b.runOut(a, base.Cwd(), b.cCompilerEnv(), cmdargs...)
+ out, err := sh.runOut(base.Cwd(), b.cCompilerEnv(), cmdargs...)
if len(out) > 0 {
// Filter out useless linker warnings caused by bugs outside Go.
// Note that failure is an expected outcome here, so we report output only
// in debug mode and don't report the error.
if cfg.BuildN || cfg.BuildX {
- b.reportCmd(a, "", "", out, nil)
+ sh.reportCmd("", "", out, nil)
}
return err
}
// gccSupportsFlag checks to see if the compiler supports a flag.
func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
+ // We use the background shell for operations here because, while this is
+ // triggered by some Action, it's not really about that Action, and often we
+ // just get the results from the global cache.
+ sh := b.BackgroundShell()
+
key := [2]string{compiler[0], flag}
// We used to write an empty C file, but that gets complicated with go
cmdArgs = append(cmdArgs, "-x", "c", "-", "-o", tmp)
if cfg.BuildN {
- b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
+ sh.ShowCmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
return false
}
}
if cfg.BuildX {
- b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
+ sh.ShowCmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
cmd.Dir = b.WorkDir
// Other parts of cmd/go can use the id as a hash
// of the installed compiler version.
func (b *Builder) gccCompilerID(compiler string) (id cache.ActionID, ok bool) {
+ // We use the background shell for operations here because, while this is
+ // triggered by some Action, it's not really about that Action, and often we
+ // just get the results from the global cache.
+ sh := b.BackgroundShell()
+
if cfg.BuildN {
- b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously([]string{compiler, "--version"}))
+ sh.ShowCmd(b.WorkDir, "%s || true", joinUnambiguously([]string{compiler, "--version"}))
return cache.ActionID{}, false
}
func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) {
p := a.Package
+ sh := b.Shell(a)
+
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS, err := b.CFlags(p)
if err != nil {
return nil, nil, err
flagLists := [][]string{cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS}
if flagsNotCompatibleWithInternalLinking(flagSources, flagLists) {
tokenFile := objdir + "preferlinkext"
- if err := b.writeFile(tokenFile, nil); err != nil {
+ if err := sh.writeFile(tokenFile, nil); err != nil {
return nil, nil, err
}
outObj = append(outObj, tokenFile)
cgoflags = append(cgoflags, "-trimpath", strings.Join(trimpath, ";"))
}
- if err := b.run(a, p.Dir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
+ if err := sh.run(p.Dir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
return nil, nil, err
}
outGo = append(outGo, gofiles...)
// dynOutObj, if not empty, is a new file to add to the generated archive.
func (b *Builder) dynimport(a *Action, objdir, importGo, cgoExe string, cflags, cgoLDFLAGS, outObj []string) (dynOutGo, dynOutObj string, err error) {
p := a.Package
+ sh := b.Shell(a)
+
cfile := objdir + "_cgo_main.c"
ofile := objdir + "_cgo_main.o"
if err := b.gcc(a, objdir, ofile, cflags, cfile); err != nil {
// cmd/link explicitly looks for the name "dynimportfail".
// See issue #52863.
fail := objdir + "dynimportfail"
- if err := b.writeFile(fail, nil); err != nil {
+ if err := sh.writeFile(fail, nil); err != nil {
return "", "", err
}
return "", fail, nil
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = []string{"-dynlinker"} // record path to dynamic linker
}
- err = b.run(a, base.Cwd(), p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
+ err = sh.run(base.Cwd(), p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
if err != nil {
return "", "", err
}
)
func (b *Builder) swigDoVersionCheck() error {
- out, err := b.runOut(nil, ".", nil, "swig", "-version")
+ sh := b.BackgroundShell()
+ out, err := sh.runOut(".", nil, "swig", "-version")
if err != nil {
return err
}
// Run SWIG on one SWIG input file.
func (b *Builder) swigOne(a *Action, file, objdir string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) {
p := a.Package
+ sh := b.Shell(a)
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _, err := b.CFlags(p)
if err != nil {
args = append(args, "-c++")
}
- out, err := b.runOut(a, p.Dir, nil, "swig", args, file)
+ out, err := sh.runOut(p.Dir, nil, "swig", args, file)
if err != nil && (bytes.Contains(out, []byte("-intgosize")) || bytes.Contains(out, []byte("-cgo"))) {
return "", "", errors.New("must have SWIG version >= 3.0.6")
}
- if err := b.reportCmd(a, "", "", out, err); err != nil {
+ if err := sh.reportCmd("", "", out, err); err != nil {
return "", "", err
}
goFile = objdir + goFile
newGoFile := objdir + "_" + base + "_swig.go"
if cfg.BuildX || cfg.BuildN {
- b.Showcmd("", "mv %s %s", goFile, newGoFile)
+ sh.ShowCmd("", "mv %s %s", goFile, newGoFile)
}
if !cfg.BuildN {
if err := os.Rename(goFile, newGoFile); err != nil {
func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
p := a.Package
+ sh := b.Shell(a)
objdir := a.Objdir
out := "_go_.o"
ofile = objdir + out
args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
if importcfg != nil {
if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
- if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
+ if err := sh.writeFile(objdir+"importcfg", importcfg); err != nil {
return "", nil, err
}
args = append(args, "-fgo-importcfg="+objdir+"importcfg")
} else {
root := objdir + "_importcfgroot_"
- if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
+ if err := buildImportcfgSymlinks(sh, root, importcfg); err != nil {
return "", nil, err
}
args = append(args, "-I", root)
}
}
if embedcfg != nil && b.gccSupportsFlag(args[:1], "-fgo-embedcfg=/dev/null") {
- if err := b.writeFile(objdir+"embedcfg", embedcfg); err != nil {
+ if err := sh.writeFile(objdir+"embedcfg", embedcfg); err != nil {
return "", nil, err
}
args = append(args, "-fgo-embedcfg="+objdir+"embedcfg")
args = append(args, f)
}
- output, err = b.runOut(a, p.Dir, nil, args)
+ output, err = sh.runOut(p.Dir, nil, args)
return ofile, output, err
}
// This serves as a temporary transition mechanism until
// we can depend on gccgo reading an importcfg directly.
// (The Go 1.9 and later gc compilers already do.)
-func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
+func buildImportcfgSymlinks(sh *Shell, root string, importcfg []byte) error {
for lineNum, line := range strings.Split(string(importcfg), "\n") {
lineNum++ // 1-based
line = strings.TrimSpace(line)
return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
}
archive := gccgoArchive(root, before)
- if err := b.Mkdir(filepath.Dir(archive)); err != nil {
+ if err := sh.Mkdir(filepath.Dir(archive)); err != nil {
return err
}
- if err := b.Symlink(after, archive); err != nil {
+ if err := sh.Symlink(after, archive); err != nil {
return err
}
case "importmap":
}
beforeA := gccgoArchive(root, before)
afterA := gccgoArchive(root, after)
- if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
+ if err := sh.Mkdir(filepath.Dir(beforeA)); err != nil {
return err
}
- if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
+ if err := sh.Mkdir(filepath.Dir(afterA)); err != nil {
return err
}
- if err := b.Symlink(afterA, beforeA); err != nil {
+ if err := sh.Symlink(afterA, beforeA); err != nil {
return err
}
case "packageshlib":
}
defs = tools.maybePIC(defs)
defs = append(defs, b.gccArchArgs()...)
- err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
+ err := b.Shell(a).run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
if err != nil {
return nil, err
}
func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
p := a.Package
+ sh := b.Shell(a)
objdir := a.Objdir
var absOfiles []string
for _, f := range ofiles {
}
absAfile := mkAbs(objdir, afile)
// Try with D modifier first, then without if that fails.
- output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
+ output, err := sh.runOut(p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
if err != nil {
- return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
+ return sh.run(p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
}
// Show the output if there is any even without errors.
- return b.reportCmd(a, "", "", output, nil)
+ return sh.reportCmd("", "", output, nil)
}
func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
+ sh := b.Shell(root)
+
// gccgo needs explicit linking with all package dependencies,
// and all LDFLAGS from cgo dependencies.
afiles := []string{}
readAndRemoveCgoFlags := func(archive string) (string, error) {
newID++
newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
- if err := b.CopyFile(newArchive, archive, 0666, false); err != nil {
+ if err := sh.CopyFile(newArchive, archive, 0666, false); err != nil {
return "", err
}
if cfg.BuildN || cfg.BuildX {
- b.Showcmd("", "ar d %s _cgo_flags", newArchive)
+ sh.ShowCmd("", "ar d %s _cgo_flags", newArchive)
if cfg.BuildN {
// TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
// Either the archive is already built and we can read them out,
return "", nil
}
}
- err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
+ err := sh.run(root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
if err != nil {
return "", err
}
- err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
+ err = sh.run(".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
if err != nil {
return "", err
}
}
}
- if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
+ if err := sh.run(".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
return err
}
switch buildmode {
case "c-archive":
- if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
+ if err := sh.run(".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
return err
}
}
if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
defs = append(defs, "-gno-record-gcc-switches")
}
- return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
+ return b.Shell(a).run(p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
}
// The result value is unrelated to the Action.
func (tools gccgoToolchain) supportsCgoIncomplete(b *Builder, a *Action) bool {
gccgoSupportsCgoIncompleteOnce.Do(func() {
+ sh := b.Shell(a)
+
fail := func(err error) {
fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
base.SetExitStatus(2)
on := strings.TrimSuffix(fn, ".go") + ".o"
if cfg.BuildN || cfg.BuildX {
- b.Showcmd(tmpdir, "%s -c -o %s %s || true", tools.compiler(), on, fn)
+ sh.ShowCmd(tmpdir, "%s -c -o %s %s || true", tools.compiler(), on, fn)
// Since this function affects later builds,
// and only generates temporary files,
// we run the command even with -n.
if cfg.BuildN || cfg.BuildX {
// Show output. We always pass a nil err because errors are an
// expected outcome in this case.
- desc := b.fmtcmd(tmpdir, "%s -c -o %s %s", tools.compiler(), on, fn)
- b.reportCmd(a, desc, tmpdir, buf.Bytes(), nil)
+ desc := sh.fmtCmd(tmpdir, "%s -c -o %s %s", tools.compiler(), on, fn)
+ sh.reportCmd(desc, tmpdir, buf.Bytes(), nil)
}
})
return gccgoSupportsCgoIncomplete