Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions src/cmd/go/internal/work/buildid.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,22 @@ func (b *Builder) toolID(name string) string {
})
}

// gccToolID returns the unique ID to use for a tool that is invoked
// gccToolID returns the unique ID to use for a C, C++, or Fortran compiler
// driven by cgo. Those compilers are not otherwise invoked under -toolexec, so
// their version probe is not run under -toolexec either (see
// go.dev/issue/64580).
func (b *Builder) gccToolID(name, language string) (id, exe string, err error) {
return b.gccToolIDPrefix(name, language, nil)
}

// gccgoToolID returns the unique ID to use for the gccgo compiler. gccgo is a
// Go toolchain compiler, treated like cmd/compile, so its version probe is run
// under cfg.BuildToolexec.
func (b *Builder) gccgoToolID(name, language string) (id, exe string, err error) {
return b.gccToolIDPrefix(name, language, cfg.BuildToolexec)
}

// gccToolIDPrefix returns the unique ID to use for a tool that is invoked
// by the GCC driver. This is used particularly for gccgo, but this can also
// be used for gcc, g++, gfortran, etc.; those tools all use the GCC
// driver under different names. The approach used here should also
Expand All @@ -193,17 +208,21 @@ func (b *Builder) toolID(name string) string {
//
// For these tools we have no -V=full option to dump the build ID,
// but we can run the tool with -v -### to reliably get the compiler proper
// and hash that. That will work in the presence of -toolexec.
// and hash that.
//
// In order to get reproducible builds for released compilers, we
// detect a released compiler by the absence of "experimental" in the
// --version output, and in that case we just use the version string.
//
// gccToolID also returns the underlying executable for the compiler.
// gccToolIDPrefix also returns the underlying executable for the compiler.
// The caller assumes that stat of the exe can be used, combined with the id,
// to detect changes in the underlying compiler. The returned exe can be empty,
// which means to rely only on the id.
func (b *Builder) gccToolID(name, language string) (id, exe string, err error) {
//
// prefix is prepended to the probe command line so that callers can run the
// probe under cfg.BuildToolexec. Callers should generally use the gccToolID
// and gccgoToolID wrappers rather than calling gccToolIDPrefix directly.
func (b *Builder) gccToolIDPrefix(name, language string, prefix []string) (id, exe string, err error) {
//TODO: Use par.Cache instead of a mutex and a map. See Builder.toolID.
key := name + "." + language
b.id.Lock()
Expand All @@ -218,7 +237,7 @@ func (b *Builder) gccToolID(name, language string) (id, exe string, err error) {
// Invoke the driver with -### to see the subcommands and the
// version strings. Use -x to set the language. Pretend to
// compile an empty file on standard input.
cmdline := str.StringList(cfg.BuildToolexec, name, "-###", "-x", language, "-c", "-")
cmdline := str.StringList(prefix, name, "-###", "-x", language, "-c", "-")
cmd := exec.Command(cmdline[0], cmdline[1:]...)
// Force untranslated output so that we see the string "version".
cmd.Env = append(os.Environ(), "LC_ALL=C")
Expand Down
8 changes: 4 additions & 4 deletions src/cmd/go/internal/work/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,15 +395,15 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
}

case "gccgo":
id, _, err := b.gccToolID(BuildToolchain.compiler(), "go")
id, _, err := b.gccgoToolID(BuildToolchain.compiler(), "go")
if err != nil {
base.Fatalf("%v", err)
}
fmt.Fprintf(h, "compile %s %q %q\n", id, forcedGccgoflags, p.Internal.Gccgoflags)
fmt.Fprintf(h, "pkgpath %s\n", gccgoPkgpath(p))
fmt.Fprintf(h, "ar %q\n", BuildToolchain.(gccgoToolchain).ar())
if len(p.SFiles) > 0 {
id, _, _ = b.gccToolID(BuildToolchain.compiler(), "assembler-with-cpp")
id, _, _ = b.gccgoToolID(BuildToolchain.compiler(), "assembler-with-cpp")
// Ignore error; different assembler versions
// are unlikely to make any difference anyhow.
fmt.Fprintf(h, "asm %q\n", id)
Expand Down Expand Up @@ -1614,7 +1614,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
// Or external linker settings and flags?

case "gccgo":
id, _, err := b.gccToolID(BuildToolchain.linker(), "go")
id, _, err := b.gccgoToolID(BuildToolchain.linker(), "go")
if err != nil {
base.Fatalf("%v", err)
}
Expand Down Expand Up @@ -2742,7 +2742,7 @@ func (b *Builder) gccCompilerID(compiler string) (id cache.ActionID, ok bool) {
// For now, there are only at most two filenames in the stat information.
// The first one is the compiler executable we invoke.
// The second is the underlying compiler as reported by -v -###
// (see b.gccToolID implementation in buildid.go).
// (see b.gccToolIDPrefix implementation in buildid.go).
toolID, exe2, err := b.gccToolID(compiler, "c")
if err != nil {
return cache.ActionID{}, false
Expand Down
25 changes: 25 additions & 0 deletions src/cmd/go/testdata/script/toolexec.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ go build ./cmd/mytool
# TODO(#27628): This should not be necessary.
env GOCACHE=$WORK/gocache

# mytool records here if it is ever run for a C-family compiler's -### probe.
env TOOLEXEC_CDRIVER_LOG=$WORK/cdriver.log

# Build the main package with our toolexec program. For each action, it will
# print the tool's name and the TOOLEXEC_IMPORTPATH value. We expect to compile
# each package once, and link the main package once.
Expand All @@ -24,6 +27,11 @@ stderr -count=1 '^compile'${GOEXE}' TOOLEXEC_IMPORTPATH="test/main/withasm"$'
stderr -count=1 '^compile'${GOEXE}' TOOLEXEC_IMPORTPATH="test/main"$'
stderr -count=1 '^link'${GOEXE}' TOOLEXEC_IMPORTPATH="test/main"$'

# The C compiler used by cgo is not run under -toolexec, so the -### probe
# used to compute its tool ID must not be run under -toolexec either.
# See go.dev/issue/64580.
[cgo] ! exists $WORK/cdriver.log

# Test packages are a little bit trickier.
# We have four variants of test/main, as reported by 'go list -test':
#
Expand Down Expand Up @@ -112,6 +120,23 @@ func main() {
toolName := filepath.Base(tool)
if len(args) > 0 && args[0] == "-V=full" {
// We can't alter the version output.
} else if len(args) > 0 && args[0] == "-###" {
// The C, C++, and Fortran compilers driven by cgo are probed with
// -### to compute their tool ID. They are not otherwise run under
// -toolexec, so they must not be probed under -toolexec either.
// See go.dev/issue/64580.
//
// The probe's output is captured by the go command rather than
// streamed, so record the invocation in a file the test can inspect.
if log := os.Getenv("TOOLEXEC_CDRIVER_LOG"); log != "" {
f, err := os.OpenFile(log, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Fprintf(f, "%s -###\n", toolName)
f.Close()
}
} else {
// Print which tool we're running, and on what package.
fmt.Fprintf(os.Stdout, "%s TOOLEXEC_IMPORTPATH=%q\n", toolName, os.Getenv("TOOLEXEC_IMPORTPATH"))
Expand Down