// compiler changes we rebuild the package.
if ccID, _, err := b.gccToolID(ccExe[0], "c"); err == nil {
fmt.Fprintf(h, "CC ID=%q\n", ccID)
+ } else {
+ fmt.Fprintf(h, "CC ID ERROR=%q\n", err)
}
if len(p.CXXFiles)+len(p.SwigCXXFiles) > 0 {
cxxExe := b.cxxExe()
fmt.Fprintf(h, "CXX=%q %q\n", cxxExe, cxxflags)
if cxxID, _, err := b.gccToolID(cxxExe[0], "c++"); err == nil {
fmt.Fprintf(h, "CXX ID=%q\n", cxxID)
+ } else {
+ fmt.Fprintf(h, "CXX ID ERROR=%q\n", err)
}
}
if len(p.FFiles) > 0 {
fmt.Fprintf(h, "FC=%q %q\n", fcExe, fflags)
if fcID, _, err := b.gccToolID(fcExe[0], "f95"); err == nil {
fmt.Fprintf(h, "FC ID=%q\n", fcID)
+ } else {
+ fmt.Fprintf(h, "FC ID ERROR=%q\n", err)
}
}
// TODO(rsc): Should we include the SWIG version?
--- /dev/null
+# Regression test for https://go.dev/issue/64589:
+# This test is very similar to build_cc_cache_issue64423. Issue #64423
+# was that we weren't properly parsing the versions output by the compiler.
+# That test checked that we could parse the version and incorporate the
+# version into the hash for the action id. This issue #64589 is that
+# we treat all errors getting the version of the compiler the same, so
+# we'd get the same action id for a missing compiler vs one whose
+# version is unparseable. So the test now first does a run with a compiler
+# that produces unparseable version output, and then runs it again with a missing
+# compiler and ensures the command doesn't return the cached output for the
+# first run when running the second run.
+
+[!cgo] skip
+[short] skip 'builds and links a fake clang binary'
+[!cc:clang] skip 'test is specific to clang version parsing'
+
+# Save the location of the real clang command for our fake one to use.
+go run ./which clang
+cp stdout $WORK/.realclang
+
+# Build a fake clang and ensure that it is the one in $PATH.
+mkdir $WORK/bin
+go build -o $WORK/bin/clang$GOEXE ./fakeclang
+[!GOOS:plan9] env PATH=$WORK${/}bin
+[GOOS:plan9] env path=$WORK${/}bin
+
+# Force CGO_ENABLED=1 so that the following commands should error
+# out if the fake clang doesn't work.
+env CGO_ENABLED=1
+
+# The bug in https://go.dev/issue/64589 resulted in cache keys that
+# didn't contain any information about the error getting the compiler version.
+# Since the bug was in cache key computation, isolate the cache:
+# if we change the way caching works, we want the test to fail
+# instead of accidentally reusing the cached information from a
+# previous test run.
+env GOCACHE=$WORK${/}.cache
+mkdir $GOCACHE
+
+go build -x runtime/cgo
+
+ # Tell our fake clang to stop working.
+ # Previously, 'go build -x runtime/cgo' would continue to
+ # succeed because both the broken clang and the non-broken one
+ # resulted in a cache key with no clang version information.
+env GO_BREAK_CLANG=1
+! go build -x runtime/cgo
+stderr '# runtime/cgo\nGO_BREAK_CLANG is set'
+
+-- go.mod --
+module example/issue64589
+go 1.20
+-- which/main.go --
+package main
+
+import (
+ "os"
+ "os/exec"
+)
+
+func main() {
+ path, err := exec.LookPath(os.Args[1])
+ if err != nil {
+ panic(err)
+ }
+ os.Stdout.WriteString(path)
+}
+-- fakeclang/main.go --
+package main
+
+import (
+ "bytes"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "slices"
+)
+
+func main() {
+ if os.Getenv("GO_BREAK_CLANG") != "" {
+ os.Stderr.WriteString("GO_BREAK_CLANG is set\n")
+ os.Exit(1)
+ }
+
+ b, err := os.ReadFile(filepath.Join(os.Getenv("WORK"), ".realclang"))
+ if err != nil {
+ log.Fatal(err)
+ }
+ if slices.Contains(os.Args, "-###") { // We are being run by gccToolID to determine the tool id used in the action id.
+ return // The important thing is that we don't print the string "version"!
+ }
+ clang := string(bytes.TrimSpace(b))
+ cmd := exec.Command(clang, os.Args[1:]...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ log.Fatal(err)
+ }
+}