Commit 4739c0db authored by Russ Cox's avatar Russ Cox

cmd/dist, cmd/cgo, cmd/go: allow per-goos/goarch default CC

Even though cmd/dist has historically distinguished "CC for gohostos/gohostarch"
from "CC for target goos/goarch", it has not recorded that distinction
for later use by cmd/cgo and cmd/go. Now that content-based staleness
includes the CC setting in the decision about when to rebuild packages,
the go command needs to know the details of which CC to use when.
Otherwise lots of things look out of date and (worse) may be rebuilt with
the wrong CC.

A related issue is that users may want to be able to build a toolchain
capable of cross-compiling for two different non-host targets, and
to date we've required that CC_FOR_TARGET apply to both.
This CL introduces CC_FOR_${GOOS}_${GOARCH}, so that you can
(for example) set CC_FOR_linux_arm and CC_FOR_linux_arm64
separately on a linux/ppc64 host and be able to cross-compile to
either arm or arm64 with the right toolchain.

Fixes #8161.
Half of a fix for #22509.

Change-Id: I7a43769f39d859f659d31bc96980918ba102fb83
Reviewed-on: https://go-review.googlesource.com/76018
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: default avatarDavid Crawshaw <crawshaw@golang.org>
parent 5e48d2b6
...@@ -102,11 +102,13 @@ the use of cgo, and to 0 to disable it. The go tool will set the ...@@ -102,11 +102,13 @@ the use of cgo, and to 0 to disable it. The go tool will set the
build constraint "cgo" if cgo is enabled. build constraint "cgo" if cgo is enabled.
When cross-compiling, you must specify a C cross-compiler for cgo to When cross-compiling, you must specify a C cross-compiler for cgo to
use. You can do this by setting the CC_FOR_TARGET environment use. You can do this by setting the generic CC_FOR_TARGET or the
variable when building the toolchain using make.bash, or by setting more specific CC_FOR_${GOOS}_${GOARCH} (for example, CC_FOR_linux_arm)
the CC environment variable any time you run the go tool. The environment variable when building the toolchain using make.bash,
CXX_FOR_TARGET and CXX environment variables work in a similar way for or you can set the CC environment variable any time you run the go tool.
C++ code.
The CXX_FOR_TARGET, CXX_FOR_${GOOS}_${GOARCH}, and CXX
environment variables work in a similar way for C++ code.
Go references to C Go references to C
......
...@@ -1221,7 +1221,7 @@ func (p *Package) gccBaseCmd() []string { ...@@ -1221,7 +1221,7 @@ func (p *Package) gccBaseCmd() []string {
if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 { if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 {
return ret return ret
} }
return strings.Fields(defaultCC) return strings.Fields(defaultCC(goos, goarch))
} }
// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
......
...@@ -23,29 +23,28 @@ import ( ...@@ -23,29 +23,28 @@ import (
// The usual variables. // The usual variables.
var ( var (
goarch string goarch string
gobin string gobin string
gohostarch string gohostarch string
gohostos string gohostos string
goos string goos string
goarm string goarm string
go386 string go386 string
goroot string goroot string
goroot_final string goroot_final string
goextlinkenabled string goextlinkenabled string
gogcflags string // For running built compiler gogcflags string // For running built compiler
goldflags string goldflags string
workdir string workdir string
tooldir string tooldir string
oldgoos string oldgoos string
oldgoarch string oldgoarch string
exe string exe string
defaultcc string defaultcc map[string]string
defaultcflags string defaultcxx map[string]string
defaultldflags string defaultcflags string
defaultcxxtarget string defaultldflags string
defaultcctarget string defaultpkgconfig string
defaultpkgconfigtarget string
rebuildall bool rebuildall bool
defaultclang bool defaultclang bool
...@@ -172,49 +171,21 @@ func xinit() { ...@@ -172,49 +171,21 @@ func xinit() {
gogcflags = os.Getenv("BOOT_GO_GCFLAGS") gogcflags = os.Getenv("BOOT_GO_GCFLAGS")
b = os.Getenv("CC") cc := "gcc"
if b == "" { if defaultclang {
// Use clang on OS X, because gcc is deprecated there. cc = "clang"
// Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that
// actually runs clang. We prepare different command
// lines for the two binaries, so it matters what we call it.
// See golang.org/issue/5822.
if defaultclang {
b = "clang"
} else {
b = "gcc"
}
} }
defaultcc = b defaultcc = compilerEnv("CC", cc)
defaultcxx = compilerEnv("CXX", cc+"++")
defaultcflags = os.Getenv("CFLAGS") defaultcflags = os.Getenv("CFLAGS")
defaultldflags = os.Getenv("LDFLAGS") defaultldflags = os.Getenv("LDFLAGS")
b = os.Getenv("CC_FOR_TARGET")
if b == "" {
b = defaultcc
}
defaultcctarget = b
b = os.Getenv("CXX_FOR_TARGET")
if b == "" {
b = os.Getenv("CXX")
if b == "" {
if defaultclang {
b = "clang++"
} else {
b = "g++"
}
}
}
defaultcxxtarget = b
b = os.Getenv("PKG_CONFIG") b = os.Getenv("PKG_CONFIG")
if b == "" { if b == "" {
b = "pkg-config" b = "pkg-config"
} }
defaultpkgconfigtarget = b defaultpkgconfig = b
// For tools being invoked but also for os.ExpandEnv. // For tools being invoked but also for os.ExpandEnv.
os.Setenv("GO386", go386) os.Setenv("GO386", go386)
...@@ -244,6 +215,55 @@ func xinit() { ...@@ -244,6 +215,55 @@ func xinit() {
tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch) tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
} }
// compilerEnv returns a map from "goos/goarch" to the
// compiler setting to use for that platform.
// The entry for key "" covers any goos/goarch not explicitly set in the map.
// For example, compilerEnv("CC", "gcc") returns the C compiler settings
// read from $CC, defaulting to gcc.
//
// The result is a map because additional environment variables
// can be set to change the compiler based on goos/goarch settings.
// The following applies to all envNames but CC is assumed to simplify
// the presentation.
//
// If no environment variables are set, we use def for all goos/goarch.
// $CC, if set, applies to all goos/goarch but is overridden by the following.
// $CC_FOR_TARGET, if set, applies to all goos/goarch except gohostos/gohostarch,
// but is overridden by the following.
// If gohostos=goos and gohostarch=goarch, then $CC_FOR_TARGET applies even for gohostos/gohostarch.
// $CC_FOR_goos_goarch, if set, applies only to goos/goarch.
func compilerEnv(envName, def string) map[string]string {
m := map[string]string{"": def}
if env := os.Getenv(envName); env != "" {
m[""] = env
}
if env := os.Getenv(envName + "_FOR_TARGET"); env != "" {
if gohostos != goos || gohostarch != goarch {
m[gohostos+"/"+gohostarch] = m[""]
}
m[""] = env
}
for _, goos := range okgoos {
for _, goarch := range okgoarch {
if env := os.Getenv(envName + "_FOR_" + goos + "_" + goarch); env != "" {
m[goos+"/"+goarch] = env
}
}
}
return m
}
// compilerEnvLookup returns the compiler settings for goos/goarch in map m.
func compilerEnvLookup(m map[string]string, goos, goarch string) string {
if cc := m[goos+"/"+goarch]; cc != "" {
return cc
}
return m[""]
}
// rmworkdir deletes the work directory. // rmworkdir deletes the work directory.
func rmworkdir() { func rmworkdir() {
if vflag > 1 { if vflag > 1 {
...@@ -1009,8 +1029,6 @@ func cmdenv() { ...@@ -1009,8 +1029,6 @@ func cmdenv() {
format = "set %s=%s\r\n" format = "set %s=%s\r\n"
} }
xprintf(format, "CC", defaultcc)
xprintf(format, "CC_FOR_TARGET", defaultcctarget)
xprintf(format, "GOROOT", goroot) xprintf(format, "GOROOT", goroot)
xprintf(format, "GOBIN", gobin) xprintf(format, "GOBIN", gobin)
xprintf(format, "GOARCH", goarch) xprintf(format, "GOARCH", goarch)
...@@ -1171,12 +1189,7 @@ func cmdbootstrap() { ...@@ -1171,12 +1189,7 @@ func cmdbootstrap() {
xprintf("\n") xprintf("\n")
} }
xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n") xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n")
os.Setenv("CC", defaultcc) os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch))
if goos == oldgoos && goarch == oldgoarch {
// Host and target are same, and we have historically
// chosen $CC_FOR_TARGET in this case.
os.Setenv("CC", defaultcctarget)
}
goInstall(goBootstrap, append([]string{"-i"}, toolchain...)...) goInstall(goBootstrap, append([]string{"-i"}, toolchain...)...)
if debug { if debug {
run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
...@@ -1241,7 +1254,7 @@ func cmdbootstrap() { ...@@ -1241,7 +1254,7 @@ func cmdbootstrap() {
goarch = oldgoarch goarch = oldgoarch
os.Setenv("GOOS", goos) os.Setenv("GOOS", goos)
os.Setenv("GOARCH", goarch) os.Setenv("GOARCH", goarch)
os.Setenv("CC", defaultcctarget) os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch))
xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch) xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch)
} }
goInstall(goBootstrap, "std", "cmd") goInstall(goBootstrap, "std", "cmd")
...@@ -1372,7 +1385,7 @@ func checkCC() { ...@@ -1372,7 +1385,7 @@ func checkCC() {
if !needCC() { if !needCC() {
return return
} }
if output, err := exec.Command(defaultcc, "--help").CombinedOutput(); err != nil { if output, err := exec.Command(defaultcc[""], "--help").CombinedOutput(); err != nil {
outputHdr := "" outputHdr := ""
if len(output) > 0 { if len(output) > 0 {
outputHdr = "\nCommand output:\n\n" outputHdr = "\nCommand output:\n\n"
......
...@@ -33,9 +33,9 @@ func mkzdefaultcc(dir, file string) { ...@@ -33,9 +33,9 @@ func mkzdefaultcc(dir, file string) {
fmt.Fprintln(&buf) fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "package cfg\n") fmt.Fprintf(&buf, "package cfg\n")
fmt.Fprintln(&buf) fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const DefaultCC = `%s`\n", defaultcctarget) fmt.Fprintf(&buf, "const DefaultPkgConfig = `%s`\n", defaultpkgconfig)
fmt.Fprintf(&buf, "const DefaultCXX = `%s`\n", defaultcxxtarget) buf.WriteString(defaultCCFunc("DefaultCC", defaultcc))
fmt.Fprintf(&buf, "const DefaultPkgConfig = `%s`\n", defaultpkgconfigtarget) buf.WriteString(defaultCCFunc("DefaultCXX", defaultcxx))
writefile(buf.String(), file, writeSkipSame) writefile(buf.String(), file, writeSkipSame)
return return
} }
...@@ -45,12 +45,34 @@ func mkzdefaultcc(dir, file string) { ...@@ -45,12 +45,34 @@ func mkzdefaultcc(dir, file string) {
fmt.Fprintln(&buf) fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "package main\n") fmt.Fprintf(&buf, "package main\n")
fmt.Fprintln(&buf) fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const defaultCC = `%s`\n", defaultcctarget) fmt.Fprintf(&buf, "const defaultPkgConfig = `%s`\n", defaultpkgconfig)
fmt.Fprintf(&buf, "const defaultCXX = `%s`\n", defaultcxxtarget) buf.WriteString(defaultCCFunc("defaultCC", defaultcc))
fmt.Fprintf(&buf, "const defaultPkgConfig = `%s`\n", defaultpkgconfigtarget) buf.WriteString(defaultCCFunc("defaultCXX", defaultcxx))
writefile(buf.String(), file, writeSkipSame) writefile(buf.String(), file, writeSkipSame)
} }
func defaultCCFunc(name string, defaultcc map[string]string) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "func %s(goos, goarch string) string {\n", name)
fmt.Fprintf(&buf, "\tswitch goos+`/`+goarch {\n")
var keys []string
for k := range defaultcc {
if k != "" {
keys = append(keys, k)
}
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprintf(&buf, "\tcase %q:\n\t\treturn %q\n", k, defaultcc[k])
}
fmt.Fprintf(&buf, "\t}\n")
fmt.Fprintf(&buf, "\treturn %q\n", defaultcc[""])
fmt.Fprintf(&buf, "}\n")
return buf.String()
}
// mkzcgo writes zosarch.go for cmd/go. // mkzcgo writes zosarch.go for cmd/go.
func mkzosarch(dir, file string) { func mkzosarch(dir, file string) {
// sort for deterministic zosarch.go file // sort for deterministic zosarch.go file
......
...@@ -78,11 +78,11 @@ func MkEnv() []cfg.EnvVar { ...@@ -78,11 +78,11 @@ func MkEnv() []cfg.EnvVar {
env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386}) env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386})
} }
cc := cfg.DefaultCC cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
if env := strings.Fields(os.Getenv("CC")); len(env) > 0 { if env := strings.Fields(os.Getenv("CC")); len(env) > 0 {
cc = env[0] cc = env[0]
} }
cxx := cfg.DefaultCXX cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
if env := strings.Fields(os.Getenv("CXX")); len(env) > 0 { if env := strings.Fields(os.Getenv("CXX")); len(env) > 0 {
cxx = env[0] cxx = env[0]
} }
......
...@@ -1597,12 +1597,12 @@ func (b *Builder) gfortranCmd(incdir, workdir string) []string { ...@@ -1597,12 +1597,12 @@ func (b *Builder) gfortranCmd(incdir, workdir string) []string {
// ccExe returns the CC compiler setting without all the extra flags we add implicitly. // ccExe returns the CC compiler setting without all the extra flags we add implicitly.
func (b *Builder) ccExe() []string { func (b *Builder) ccExe() []string {
return b.compilerExe(origCC, cfg.DefaultCC) return b.compilerExe(origCC, cfg.DefaultCC(cfg.Goos, cfg.Goarch))
} }
// cxxExe returns the CXX compiler setting without all the extra flags we add implicitly. // cxxExe returns the CXX compiler setting without all the extra flags we add implicitly.
func (b *Builder) cxxExe() []string { func (b *Builder) cxxExe() []string {
return b.compilerExe(origCXX, cfg.DefaultCXX) return b.compilerExe(origCXX, cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
} }
// fcExe returns the FC compiler setting without all the extra flags we add implicitly. // fcExe returns the FC compiler setting without all the extra flags we add implicitly.
......
...@@ -430,9 +430,9 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) ...@@ -430,9 +430,9 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string)
// Else, use the CC environment variable and defaultCC as fallback. // Else, use the CC environment variable and defaultCC as fallback.
var compiler []string var compiler []string
if cxx { if cxx {
compiler = envList("CXX", cfg.DefaultCXX) compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
} else { } else {
compiler = envList("CC", cfg.DefaultCC) compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
} }
ldflags = append(ldflags, "-buildmode="+ldBuildmode) ldflags = append(ldflags, "-buildmode="+ldBuildmode)
if root.buildID != "" { if root.buildID != "" {
...@@ -474,9 +474,9 @@ func (gcToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcf ...@@ -474,9 +474,9 @@ func (gcToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcf
// Else, use the CC environment variable and defaultCC as fallback. // Else, use the CC environment variable and defaultCC as fallback.
var compiler []string var compiler []string
if cxx { if cxx {
compiler = envList("CXX", cfg.DefaultCXX) compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
} else { } else {
compiler = envList("CC", cfg.DefaultCC) compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
} }
ldflags = setextld(ldflags, compiler) ldflags = setextld(ldflags, compiler)
for _, d := range toplevelactions { for _, d := range toplevelactions {
......
...@@ -464,7 +464,7 @@ func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error ...@@ -464,7 +464,7 @@ func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error
defs = append(defs, "-fsplit-stack") defs = append(defs, "-fsplit-stack")
} }
defs = tools.maybePIC(defs) defs = tools.maybePIC(defs)
return b.run(p.Dir, p.ImportPath, nil, envList("CC", cfg.DefaultCC), "-Wall", "-g", return b.run(p.Dir, p.ImportPath, nil, envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)), "-Wall", "-g",
"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment