diff options
author | Jay Conrod <jayconrod@google.com> | 2021-07-14 16:57:24 -0700 |
---|---|---|
committer | Jay Conrod <jayconrod@google.com> | 2021-08-16 20:23:11 +0000 |
commit | 742dcba7bb953a96c9f3fcdeb32b1c03cbbd8d5e (patch) | |
tree | 66a2cfc70194cd67bf4a0406aff8568ec63e234e /src/cmd/cgo | |
parent | 41d991e4e1f3a9230cc3832a39dbf49ce9aa191f (diff) | |
download | go-742dcba7bb953a96c9f3fcdeb32b1c03cbbd8d5e.tar.gz go-742dcba7bb953a96c9f3fcdeb32b1c03cbbd8d5e.zip |
cmd: support space and quotes in CC and CXX
The CC and CXX environment variables now support spaces and quotes
(both double and single). This fixes two issues: first, if CC is a
single path that contains spaces (like 'c:\Program
Files\gcc\bin\gcc.exe'), that should now work if the space is quoted
or escaped (#41400). Second, if CC or CXX has multiple arguments (like
'gcc -O2'), they are now split correctly, and the arguments are passed
before other arguments when invoking the C compiler. Previously,
strings.Fields was used to split arguments, and the arguments were
placed later in the command line. (#43078).
Fixes golang/go#41400
Fixes golang/go#43078
NOTE: This change also includes a fix (CL 341929) for a test that was
broken by the original CL. Commit message for the fix is below.
[dev.cmdgo] cmd/link: fix TestBuildForTvOS
This test was broken in CL 334732 on darwin.
The test invokes 'go build' with a CC containing the arguments
-framework CoreFoundation. Previously, the go command split CC on
whitespace, and inserted the arguments after the command line when
running CC directly. Those arguments weren't passed to cgo though,
so cgo ran CC without -framework CoreFoundation (or any of the other
flags).
In CL 334732, we pass CC through to cgo, and cgo splits arguments
using str.SplitQuotedFields. So -framework CoreFoundation actually
gets passed to the C compiler. It appears that -framework flags are
only meant to be used in linking operations, so when cgo invokes clang
with -E (run preprocessor only), clang emits an error that -framework
is unused.
This change fixes the test by moving -framework CoreFoundation out of
CC and into CGO_LDFLAGS.
Change-Id: I2d5d89ddb19c94adef65982a8137b01f037d5c11
Reviewed-on: https://go-review.googlesource.com/c/go/+/334732
Trust: Jay Conrod <jayconrod@google.com>
Trust: Michael Matloob <matloob@golang.org>
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-on: https://go-review.googlesource.com/c/go/+/341936
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r-- | src/cmd/cgo/gcc.go | 46 | ||||
-rw-r--r-- | src/cmd/cgo/main.go | 8 |
2 files changed, 37 insertions, 17 deletions
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index a73e998877..92adb1ed9c 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -23,10 +23,13 @@ import ( "internal/xcoff" "math" "os" + "os/exec" "strconv" "strings" "unicode" "unicode/utf8" + + "cmd/internal/str" ) var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") @@ -382,7 +385,7 @@ func (p *Package) guessKinds(f *File) []*Name { stderr = p.gccErrors(b.Bytes()) } if stderr == "" { - fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) + fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes()) } completed := false @@ -457,7 +460,7 @@ func (p *Package) guessKinds(f *File) []*Name { } if !completed { - fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr) + fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr) } for i, n := range names { @@ -488,7 +491,7 @@ func (p *Package) guessKinds(f *File) []*Name { // to users debugging preamble mistakes. See issue 8442. preambleErrors := p.gccErrors([]byte(f.Preamble)) if len(preambleErrors) > 0 { - error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors) + error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors) } fatalf("unresolved names") @@ -1545,20 +1548,37 @@ func gofmtPos(n ast.Expr, pos token.Pos) string { return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s) } -// gccBaseCmd returns the start of the compiler command line. +// checkGCCBaseCmd returns the start of the compiler command line. // It uses $CC if set, or else $GCC, or else the compiler recorded // during the initial build as defaultCC. // defaultCC is defined in zdefaultcc.go, written by cmd/dist. -func (p *Package) gccBaseCmd() []string { +// +// The compiler command line is split into arguments on whitespace. Quotes +// are understood, so arguments may contain whitespace. +// +// checkGCCBaseCmd confirms that the compiler exists in PATH, returning +// an error if it does not. +func checkGCCBaseCmd() ([]string, error) { // Use $CC if set, since that's what the build uses. - if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 { - return ret + value := os.Getenv("CC") + if value == "" { + // Try $GCC if set, since that's what we used to use. + value = os.Getenv("GCC") + } + if value == "" { + value = defaultCC(goos, goarch) + } + args, err := str.SplitQuotedFields(value) + if err != nil { + return nil, err + } + if len(args) == 0 { + return nil, errors.New("CC not set and no default found") } - // Try $GCC if set, since that's what we used to use. - if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 { - return ret + if _, err := exec.LookPath(args[0]); err != nil { + return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err) } - return strings.Fields(defaultCC(goos, goarch)) + return args[:len(args):len(args)], nil } // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". @@ -1604,7 +1624,7 @@ func gccTmp() string { // gccCmd returns the gcc command line to use for compiling // the input. func (p *Package) gccCmd() []string { - c := append(p.gccBaseCmd(), + c := append(gccBaseCmd, "-w", // no warnings "-Wno-error", // warnings are not errors "-o"+gccTmp(), // write object to tmp @@ -2005,7 +2025,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // #defines that gcc encountered while processing the input // and its included files. func (p *Package) gccDefines(stdin []byte) string { - base := append(p.gccBaseCmd(), "-E", "-dM", "-xc") + base := append(gccBaseCmd, "-E", "-dM", "-xc") base = append(base, p.gccMachine()...) stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) return stdout diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index c6a0c525e6..14642b7576 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -21,7 +21,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path/filepath" "reflect" "runtime" @@ -248,6 +247,7 @@ var importSyscall = flag.Bool("import_syscall", true, "import syscall in generat var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths") var goarch, goos, gomips, gomips64 string +var gccBaseCmd []string func main() { objabi.AddVersionFlag() // -V @@ -305,10 +305,10 @@ func main() { p := newPackage(args[:i]) // We need a C compiler to be available. Check this. - gccName := p.gccBaseCmd()[0] - _, err := exec.LookPath(gccName) + var err error + gccBaseCmd, err = checkGCCBaseCmd() if err != nil { - fatalf("C compiler %q not found: %v", gccName, err) + fatalf("%v", err) os.Exit(2) } |