]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/dist, cmd/cgo, cmd/go: allow per-goos/goarch default CC
authorRuss Cox <rsc@golang.org>
Sun, 5 Nov 2017 22:09:54 +0000 (17:09 -0500)
committerRuss Cox <rsc@golang.org>
Mon, 6 Nov 2017 20:20:37 +0000 (20:20 +0000)
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: David Crawshaw <crawshaw@golang.org>
src/cmd/cgo/doc.go
src/cmd/cgo/gcc.go
src/cmd/dist/build.go
src/cmd/dist/buildgo.go
src/cmd/go/internal/envcmd/env.go
src/cmd/go/internal/work/exec.go
src/cmd/go/internal/work/gc.go
src/cmd/go/internal/work/gccgo.go

index ee99bfc2c35528e3d7a203050189fbbc88165f35..796d11a63c286c6168881a8ade7f9d5441bd1869 100644 (file)
@@ -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.
 
 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
-variable when building the toolchain using make.bash, or by setting
-the CC environment variable any time you run the go tool. The
-CXX_FOR_TARGET and CXX environment variables work in a similar way for
-C++ code.
+use. You can do this by setting the generic CC_FOR_TARGET or the
+more specific CC_FOR_${GOOS}_${GOARCH} (for example, CC_FOR_linux_arm)
+environment variable when building the toolchain using make.bash,
+or you can set the CC environment variable any time you run the go tool.
+
+The CXX_FOR_TARGET, CXX_FOR_${GOOS}_${GOARCH}, and CXX
+environment variables work in a similar way for C++ code.
 
 Go references to C
 
index 99e98cee75575df524fbc57f8406e023e81b9a1a..95be03f6e45f4f3579c8ba1757aab3da8014d781 100644 (file)
@@ -1221,7 +1221,7 @@ func (p *Package) gccBaseCmd() []string {
        if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 {
                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".
index aa2402163d6963e94ae0370c5a51c280b13448df..a2f3a8c2822209933a45dd1a9bac8bc8e0fef142 100644 (file)
@@ -23,29 +23,28 @@ import (
 
 // The usual variables.
 var (
-       goarch                 string
-       gobin                  string
-       gohostarch             string
-       gohostos               string
-       goos                   string
-       goarm                  string
-       go386                  string
-       goroot                 string
-       goroot_final           string
-       goextlinkenabled       string
-       gogcflags              string // For running built compiler
-       goldflags              string
-       workdir                string
-       tooldir                string
-       oldgoos                string
-       oldgoarch              string
-       exe                    string
-       defaultcc              string
-       defaultcflags          string
-       defaultldflags         string
-       defaultcxxtarget       string
-       defaultcctarget        string
-       defaultpkgconfigtarget string
+       goarch           string
+       gobin            string
+       gohostarch       string
+       gohostos         string
+       goos             string
+       goarm            string
+       go386            string
+       goroot           string
+       goroot_final     string
+       goextlinkenabled string
+       gogcflags        string // For running built compiler
+       goldflags        string
+       workdir          string
+       tooldir          string
+       oldgoos          string
+       oldgoarch        string
+       exe              string
+       defaultcc        map[string]string
+       defaultcxx       map[string]string
+       defaultcflags    string
+       defaultldflags   string
+       defaultpkgconfig string
 
        rebuildall   bool
        defaultclang bool
@@ -172,49 +171,21 @@ func xinit() {
 
        gogcflags = os.Getenv("BOOT_GO_GCFLAGS")
 
-       b = os.Getenv("CC")
-       if b == "" {
-               // Use clang on OS X, because gcc is deprecated there.
-               // 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"
-               }
+       cc := "gcc"
+       if defaultclang {
+               cc = "clang"
        }
-       defaultcc = b
+       defaultcc = compilerEnv("CC", cc)
+       defaultcxx = compilerEnv("CXX", cc+"++")
 
        defaultcflags = os.Getenv("CFLAGS")
-
        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")
        if b == "" {
                b = "pkg-config"
        }
-       defaultpkgconfigtarget = b
+       defaultpkgconfig = b
 
        // For tools being invoked but also for os.ExpandEnv.
        os.Setenv("GO386", go386)
@@ -244,6 +215,55 @@ func xinit() {
        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.
 func rmworkdir() {
        if vflag > 1 {
@@ -1009,8 +1029,6 @@ func cmdenv() {
                format = "set %s=%s\r\n"
        }
 
-       xprintf(format, "CC", defaultcc)
-       xprintf(format, "CC_FOR_TARGET", defaultcctarget)
        xprintf(format, "GOROOT", goroot)
        xprintf(format, "GOBIN", gobin)
        xprintf(format, "GOARCH", goarch)
@@ -1171,12 +1189,7 @@ func cmdbootstrap() {
                xprintf("\n")
        }
        xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n")
-       os.Setenv("CC", defaultcc)
-       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)
-       }
+       os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch))
        goInstall(goBootstrap, append([]string{"-i"}, toolchain...)...)
        if debug {
                run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
@@ -1241,7 +1254,7 @@ func cmdbootstrap() {
                goarch = oldgoarch
                os.Setenv("GOOS", goos)
                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)
        }
        goInstall(goBootstrap, "std", "cmd")
@@ -1372,7 +1385,7 @@ func checkCC() {
        if !needCC() {
                return
        }
-       if output, err := exec.Command(defaultcc, "--help").CombinedOutput(); err != nil {
+       if output, err := exec.Command(defaultcc[""], "--help").CombinedOutput(); err != nil {
                outputHdr := ""
                if len(output) > 0 {
                        outputHdr = "\nCommand output:\n\n"
index 19384a1a530c242af9f2eef956ce16a17e1815f4..caafc13da888ad2ceec2b30bc73823fe1051177a 100644 (file)
@@ -33,9 +33,9 @@ func mkzdefaultcc(dir, file string) {
                fmt.Fprintln(&buf)
                fmt.Fprintf(&buf, "package cfg\n")
                fmt.Fprintln(&buf)
-               fmt.Fprintf(&buf, "const DefaultCC = `%s`\n", defaultcctarget)
-               fmt.Fprintf(&buf, "const DefaultCXX = `%s`\n", defaultcxxtarget)
-               fmt.Fprintf(&buf, "const DefaultPkgConfig = `%s`\n", defaultpkgconfigtarget)
+               fmt.Fprintf(&buf, "const DefaultPkgConfig = `%s`\n", defaultpkgconfig)
+               buf.WriteString(defaultCCFunc("DefaultCC", defaultcc))
+               buf.WriteString(defaultCCFunc("DefaultCXX", defaultcxx))
                writefile(buf.String(), file, writeSkipSame)
                return
        }
@@ -45,12 +45,34 @@ func mkzdefaultcc(dir, file string) {
        fmt.Fprintln(&buf)
        fmt.Fprintf(&buf, "package main\n")
        fmt.Fprintln(&buf)
-       fmt.Fprintf(&buf, "const defaultCC = `%s`\n", defaultcctarget)
-       fmt.Fprintf(&buf, "const defaultCXX = `%s`\n", defaultcxxtarget)
-       fmt.Fprintf(&buf, "const defaultPkgConfig = `%s`\n", defaultpkgconfigtarget)
+       fmt.Fprintf(&buf, "const defaultPkgConfig = `%s`\n", defaultpkgconfig)
+       buf.WriteString(defaultCCFunc("defaultCC", defaultcc))
+       buf.WriteString(defaultCCFunc("defaultCXX", defaultcxx))
        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.
 func mkzosarch(dir, file string) {
        // sort for deterministic zosarch.go file
index 90ab2d718fe0c31d57d7e38bbc019af078afc68d..f756e3b60722749737041d49bbcce8f93ea127da 100644 (file)
@@ -78,11 +78,11 @@ func MkEnv() []cfg.EnvVar {
                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 {
                cc = env[0]
        }
-       cxx := cfg.DefaultCXX
+       cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
        if env := strings.Fields(os.Getenv("CXX")); len(env) > 0 {
                cxx = env[0]
        }
index 7a4e62b0a4466fa4dca187ea3395eddd9f679bfb..d43a5f2417f0d362e026d4b5fb206dec94a92ca3 100644 (file)
@@ -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.
 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.
 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.
index 651d20c21c57197333e81796267522892684b598..c0db90dfe50561dd19fe9f090736210c8bd0bca9 100644 (file)
@@ -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.
        var compiler []string
        if cxx {
-               compiler = envList("CXX", cfg.DefaultCXX)
+               compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
        } else {
-               compiler = envList("CC", cfg.DefaultCC)
+               compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
        }
        ldflags = append(ldflags, "-buildmode="+ldBuildmode)
        if root.buildID != "" {
@@ -474,9 +474,9 @@ func (gcToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcf
        // Else, use the CC environment variable and defaultCC as fallback.
        var compiler []string
        if cxx {
-               compiler = envList("CXX", cfg.DefaultCXX)
+               compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
        } else {
-               compiler = envList("CC", cfg.DefaultCC)
+               compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
        }
        ldflags = setextld(ldflags, compiler)
        for _, d := range toplevelactions {
index 1d7155c977601982125f2b652636402ee26d5658..898c3c200486a76a821229e823c503e2695486f8 100644 (file)
@@ -464,7 +464,7 @@ func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error
                defs = append(defs, "-fsplit-stack")
        }
        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)
 }