| # Regression test for https://go.dev/issue/64423: |
| # |
| # When we parse the version for a Clang binary, we should accept |
| # an arbitrary vendor prefix, which (as of 2023) may be injected |
| # by defining CLANG_VENDOR when building clang itself. |
| # |
| # Since we don't want to actually rebuild the Clang toolchain in |
| # this test, we instead simulate it by injecting a fake "clang" |
| # binary that runs the real one as a subprocess. |
| |
| [!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/64423 resulted in cache keys that |
| # didn't contain any information about the C compiler. |
| # 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/issue64423 |
| 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 ( |
| "bufio" |
| "bytes" |
| "log" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "strings" |
| ) |
| |
| 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) |
| } |
| clang := string(bytes.TrimSpace(b)) |
| cmd := exec.Command(clang, os.Args[1:]...) |
| cmd.Stdout = os.Stdout |
| stderr, err := cmd.StderrPipe() |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| if err := cmd.Start(); err != nil { |
| log.Fatal(err) |
| } |
| |
| r := bufio.NewReader(stderr) |
| for { |
| line, err := r.ReadString('\n') |
| if line != "" { |
| if strings.Contains(line, "clang version") { |
| // Simulate a clang version string with an arbitrary vendor prefix. |
| const vendorString = "Gopher Solutions Unlimited " |
| os.Stderr.WriteString(vendorString) |
| } |
| os.Stderr.WriteString(line) |
| } |
| if err != nil { |
| break |
| } |
| } |
| os.Stderr.Close() |
| |
| if err := cmd.Wait(); err != nil { |
| os.Exit(1) |
| } |
| } |