diff options
Diffstat (limited to 'libgo/go/cmd/go/build.go')
-rw-r--r-- | libgo/go/cmd/go/build.go | 825 |
1 files changed, 544 insertions, 281 deletions
diff --git a/libgo/go/cmd/go/build.go b/libgo/go/cmd/go/build.go index 864fb87901..f2d11f43f1 100644 --- a/libgo/go/cmd/go/build.go +++ b/libgo/go/cmd/go/build.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -48,6 +48,8 @@ When compiling multiple packages or a single non-main package, build compiles the packages but discards the resulting object, serving only as a check that the packages can be built. +When compiling packages, build ignores files that end in '_test.go'. + The -o flag, only allowed when compiling a single package, forces build to write the resulting executable or object to the named output file, instead of the default behavior described @@ -65,8 +67,7 @@ and test commands: -p n the number of programs, such as build commands or test binaries, that can be run in parallel. - The default is the number of CPUs available, except - on darwin/arm which defaults to 1. + The default is the number of CPUs available. -race enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. @@ -145,17 +146,6 @@ func init() { addBuildFlags(cmdBuild) addBuildFlags(cmdInstall) - - if buildContext.GOOS == "darwin" { - switch buildContext.GOARCH { - case "arm", "arm64": - // darwin/arm cannot run multiple tests simultaneously. - // Parallelism is limited in go_darwin_arm_exec, but - // also needs to be limited here so go test std does not - // timeout tests that waiting to run. - buildP = 1 - } - } } // Flags set by multiple commands. @@ -352,6 +342,18 @@ func buildModeInit() { } return p } + switch platform { + case "darwin/arm", "darwin/arm64": + codegenArg = "-shared" + default: + switch goos { + case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": + // Use -shared so that the result is + // suitable for inclusion in a PIE or + // shared library. + codegenArg = "-shared" + } + } exeSuffix = ".a" ldBuildmode = "c-archive" case "c-shared": @@ -374,6 +376,9 @@ func buildModeInit() { case "android/arm", "android/arm64", "android/amd64", "android/386": codegenArg = "-shared" ldBuildmode = "pie" + case "darwin/arm", "darwin/arm64": + codegenArg = "-shared" + fallthrough default: ldBuildmode = "exe" } @@ -385,7 +390,7 @@ func buildModeInit() { fatalf("-buildmode=pie not supported by gccgo") } else { switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386": codegenArg = "-shared" default: @@ -399,7 +404,7 @@ func buildModeInit() { codegenArg = "-fPIC" } else { switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le": + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x": default: fatalf("-buildmode=shared not supported on %s\n", platform) } @@ -409,6 +414,21 @@ func buildModeInit() { fatalf("-buildmode=shared and -o not supported together") } ldBuildmode = "shared" + case "plugin": + pkgsFilter = pkgsMain + if gccgo { + codegenArg = "-fPIC" + } else { + switch platform { + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", + "android/amd64", "android/arm", "android/arm64", "android/386": + default: + fatalf("-buildmode=plugin not supported on %s\n", platform) + } + codegenArg = "-dynlink" + } + exeSuffix = ".so" + ldBuildmode = "plugin" default: fatalf("buildmode=%s not supported", buildBuildmode) } @@ -417,7 +437,7 @@ func buildModeInit() { codegenArg = "-fPIC" } else { switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le": + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x": buildAsmflags = append(buildAsmflags, "-D=GOBUILDMODE_shared=1") default: fatalf("-linkshared not supported on %s\n", platform) @@ -434,10 +454,13 @@ func buildModeInit() { buildAsmflags = append(buildAsmflags, codegenArg) buildGcflags = append(buildGcflags, codegenArg) } - if buildContext.InstallSuffix != "" { - buildContext.InstallSuffix += "_" + // Don't alter InstallSuffix when modifying default codegen args. + if buildBuildmode != "default" || buildLinkshared { + if buildContext.InstallSuffix != "" { + buildContext.InstallSuffix += "_" + } + buildContext.InstallSuffix += codegenArg[1:] } - buildContext.InstallSuffix += codegenArg[1:] } } @@ -454,6 +477,11 @@ func runBuild(cmd *Command, args []string) { *buildO += exeSuffix } + // Special case -o /dev/null by not writing at all. + if *buildO == os.DevNull { + *buildO = "" + } + // sanity check some often mis-used options switch buildContext.Compiler { case "gccgo": @@ -483,6 +511,7 @@ func runBuild(cmd *Command, args []string) { p := pkgs[0] p.target = *buildO p.Stale = true // must build - not up to date + p.StaleReason = "build -o flag in use" a := b.action(modeInstall, depMode, p) b.do(a) return @@ -525,7 +554,7 @@ func isMetaPackage(name string) bool { } // libname returns the filename to use for the shared library when using -// -buildmode=shared. The rules we use are: +// -buildmode=shared. The rules we use are: // Use arguments for special 'meta' packages: // std --> libstd.so // std cmd --> libstd,cmd.so @@ -581,6 +610,10 @@ func libname(args []string, pkgs []*Package) (string, error) { } func runInstall(cmd *Command, args []string) { + installPackages(args, false) +} + +func installPackages(args []string, forGet bool) { if gobin != "" && !filepath.IsAbs(gobin) { fatalf("cannot install, GOBIN must be an absolute path") } @@ -600,7 +633,7 @@ func runInstall(cmd *Command, args []string) { errorf("go install: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) default: errorf("go install: no install location for directory %s outside GOPATH\n"+ - "\tFor more details see: go help gopath", p.Dir) + "\tFor more details see: 'go help gopath'", p.Dir) } } } @@ -608,6 +641,8 @@ func runInstall(cmd *Command, args []string) { var b builder b.init() + // Set the behavior for `go get` to not error on packages with test files only. + b.testFilesOnlyOK = forGet var a *action if buildBuildmode == "shared" { if libName, err := libname(args, pkgs); err != nil { @@ -681,6 +716,7 @@ var ( func init() { goarch = buildContext.GOARCH goos = buildContext.GOOS + if goos == "windows" { exeSuffix = ".exe" } @@ -694,8 +730,11 @@ type builder struct { work string // the temporary work directory (ends in filepath.Separator) actionCache map[cacheKey]*action // a cache of already-constructed actions mkdirCache map[string]bool // a cache of created directories + flagCache map[string]bool // a cache of supported compiler flags print func(args ...interface{}) (int, error) + testFilesOnlyOK bool // do not error if the packages only have test files + output sync.Mutex scriptDir string // current directory in printed script @@ -795,7 +834,7 @@ func goFilesPackage(gofiles []string) *Package { // Synthesize fake "directory" that only shows the named files, // to make it look like this is a standard package or - // command directory. So that local imports resolve + // command directory. So that local imports resolve // consistently, the files must all be in the same directory. var dirent []os.FileInfo var dir string @@ -853,6 +892,7 @@ func goFilesPackage(gofiles []string) *Package { pkg.Target = pkg.target pkg.Stale = true + pkg.StaleReason = "files named on command line" computeStale(pkg) return pkg @@ -957,7 +997,7 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha // If we are not doing a cross-build, then record the binary we'll // generate for cgo as a dependency of the build of any package // using cgo, to make sure we do not overwrite the binary while - // a package is using it. If this is a cross-build, then the cgo we + // a package is using it. If this is a cross-build, then the cgo we // are writing is not the cgo we need to use. if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace && !buildMSan && reqStdPkgSrc { if (len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo") && !buildLinkshared && buildBuildmode != "shared" { @@ -993,7 +1033,7 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha } if p.local && p.target == "" { - // Imported via local path. No permanent target. + // Imported via local path. No permanent target. mode = modeBuild } work := p.pkgdir @@ -1041,7 +1081,7 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha // the name will show up in ps listings. If the caller has specified // a name, use that instead of a.out. The binary is generated // in an otherwise empty subdirectory named exe to avoid - // naming conflicts. The only possible conflict is if we were + // naming conflicts. The only possible conflict is if we were // to create a top-level package named exe. name := "a.out" if p.exeName != "" { @@ -1227,14 +1267,22 @@ func allArchiveActions(root *action) []*action { // do runs the action graph rooted at root. func (b *builder) do(root *action) { + /* Commented out for gccgo, which does not have osArchSupportsCgo. + + if _, ok := osArchSupportsCgo[goos+"/"+goarch]; !ok && buildContext.Compiler == "gc" { + fmt.Fprintf(os.Stderr, "cmd/go: unsupported GOOS/GOARCH pair %s/%s\n", goos, goarch) + os.Exit(2) + } + */ + // Build list of all actions, assigning depth-first post-order priority. // The original implementation here was a true queue // (using a channel) but it had the effect of getting // distracted by low-level leaf actions to the detriment - // of completing higher-level actions. The order of + // of completing higher-level actions. The order of // work does not matter much to overall execution time, // but when running "go test std" it is nice to see each test - // results as soon as possible. The priorities assigned + // results as soon as possible. The priorities assigned // ensure that, all else being equal, the execution prefers // to do what it would have done first in a simple depth-first // dependency order traversal. @@ -1273,6 +1321,8 @@ func (b *builder) do(root *action) { if err != nil { if err == errPrintedOutput { setExitStatus(2) + } else if _, ok := err.(*build.NoGoError); ok && len(a.p.TestGoFiles) > 0 && b.testFilesOnlyOK { + // Ignore the "no buildable Go source files" error for a package with only test files. } else { errorf("%s", err) } @@ -1331,18 +1381,15 @@ func (b *builder) do(root *action) { wg.Wait() } -// hasString reports whether s appears in the list of strings. -func hasString(strings []string, s string) bool { - for _, t := range strings { - if s == t { - return true - } - } - return false -} - // build is the action for building a single package or command. func (b *builder) build(a *action) (err error) { + // Return an error for binary-only package. + // We only reach this if isStale believes the binary form is + // either not present or not usable. + if a.p.BinaryOnly { + return fmt.Errorf("missing or invalid package binary for binary-only package %s", a.p.ImportPath) + } + // Return an error if the package has CXX files but it's not using // cgo nor SWIG, since the CXX files can only be processed by cgo // and SWIG. @@ -1355,8 +1402,14 @@ func (b *builder) build(a *action) (err error) { return fmt.Errorf("can't build package %s because it contains Objective-C files (%s) but it's not using cgo nor SWIG", a.p.ImportPath, strings.Join(a.p.MFiles, ",")) } + // Same as above for Fortran files + if len(a.p.FFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() { + return fmt.Errorf("can't build package %s because it contains Fortran files (%s) but it's not using cgo nor SWIG", + a.p.ImportPath, strings.Join(a.p.FFiles, ",")) + } + defer func() { - if err != nil && err != errPrintedOutput { + if _, ok := err.(*build.NoGoError); err != nil && err != errPrintedOutput && !(ok && b.testFilesOnlyOK && len(a.p.TestGoFiles) > 0) { err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err) } }() @@ -1387,7 +1440,7 @@ func (b *builder) build(a *action) (err error) { } } - var gofiles, cgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string + var gofiles, cgofiles, objdirCgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string gofiles = append(gofiles, a.p.GoFiles...) cgofiles = append(cgofiles, a.p.CgoFiles...) @@ -1409,7 +1462,7 @@ func (b *builder) build(a *action) (err error) { if err != nil { return err } - cgofiles = append(cgofiles, outGo...) + objdirCgofiles = append(objdirCgofiles, outGo...) cfiles = append(cfiles, outC...) cxxfiles = append(cxxfiles, outCXX...) } @@ -1421,6 +1474,8 @@ func (b *builder) build(a *action) (err error) { // cgo and non-cgo worlds, so it necessarily has files in both. // In that case gcc only gets the gcc_* files. var gccfiles []string + gccfiles = append(gccfiles, cfiles...) + cfiles = nil if a.p.Standard && a.p.ImportPath == "runtime/cgo" { filter := func(files, nongcc, gcc []string) ([]string, []string) { for _, f := range files { @@ -1432,11 +1487,9 @@ func (b *builder) build(a *action) (err error) { } return nongcc, gcc } - cfiles, gccfiles = filter(cfiles, cfiles[:0], gccfiles) sfiles, gccfiles = filter(sfiles, sfiles[:0], gccfiles) } else { - gccfiles = append(cfiles, sfiles...) - cfiles = nil + gccfiles = append(gccfiles, sfiles...) sfiles = nil } @@ -1444,7 +1497,7 @@ func (b *builder) build(a *action) (err error) { if a.cgo != nil && a.cgo.target != "" { cgoExe = a.cgo.target } - outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles) + outGo, outObj, err := b.cgo(a, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, objdirCgofiles, gccfiles, cxxfiles, a.p.MFiles, a.p.FFiles) if err != nil { return err } @@ -1542,17 +1595,17 @@ func (b *builder) build(a *action) (err error) { } // Assemble .s files. - for _, file := range sfiles { - out := file[:len(file)-len(".s")] + ".o" - if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { + if len(sfiles) > 0 { + ofiles, err := buildToolchain.asm(b, a.p, obj, sfiles) + if err != nil { return err } - objects = append(objects, out) + objects = append(objects, ofiles...) } // NOTE(rsc): On Windows, it is critically important that the // gcc-compiled objects (cgoObjects) be listed after the ordinary - // objects in the archive. I do not know why this is. + // objects in the archive. I do not know why this is. // https://golang.org/issue/2601 objects = append(objects, cgoObjects...) @@ -1586,23 +1639,62 @@ func (b *builder) build(a *action) (err error) { return nil } +// pkgconfigCmd returns a pkg-config binary name +// defaultPkgConfig is defined in zdefaultcc.go, written by cmd/dist. +func (b *builder) pkgconfigCmd() string { + return envList("PKG_CONFIG", defaultPkgConfig)[0] +} + +// splitPkgConfigOutput parses the pkg-config output into a slice of +// flags. pkg-config always uses \ to escape special characters. +func splitPkgConfigOutput(out []byte) []string { + if len(out) == 0 { + return nil + } + var flags []string + flag := make([]byte, len(out)) + r, w := 0, 0 + for r < len(out) { + switch out[r] { + case ' ', '\t', '\r', '\n': + if w > 0 { + flags = append(flags, string(flag[:w])) + } + w = 0 + case '\\': + r++ + fallthrough + default: + if r < len(out) { + flag[w] = out[r] + w++ + } + } + r++ + } + if w > 0 { + flags = append(flags, string(flag[:w])) + } + return flags +} + // Calls pkg-config if needed and returns the cflags/ldflags needed to build the package. func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err error) { if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { var out []byte - out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs) + out, err = b.runOut(p.Dir, p.ImportPath, nil, b.pkgconfigCmd(), "--cflags", pkgs) if err != nil { - b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)) + b.showOutput(p.Dir, b.pkgconfigCmd()+" --cflags "+strings.Join(pkgs, " "), string(out)) b.print(err.Error() + "\n") err = errPrintedOutput return } if len(out) > 0 { - cflags = strings.Fields(string(out)) + cflags = splitPkgConfigOutput(out) } - out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) + out, err = b.runOut(p.Dir, p.ImportPath, nil, b.pkgconfigCmd(), "--libs", pkgs) if err != nil { - b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out)) + b.showOutput(p.Dir, b.pkgconfigCmd()+" --libs "+strings.Join(pkgs, " "), string(out)) b.print(err.Error() + "\n") err = errPrintedOutput return @@ -1643,7 +1735,7 @@ func (b *builder) install(a *action) (err error) { perm := os.FileMode(0666) if a1.link { switch buildBuildmode { - case "c-archive", "c-shared": + case "c-archive", "c-shared", "plugin": default: perm = 0777 } @@ -1658,7 +1750,7 @@ func (b *builder) install(a *action) (err error) { } // remove object dir to keep the amount of - // garbage down in a large build. On an operating system + // garbage down in a large build. On an operating system // with aggressive buffering, cleaning incrementally like // this keeps the intermediate objects from hitting the disk. if !buildWork { @@ -1803,7 +1895,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode, force b df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil && toolIsWindows { // Windows does not allow deletion of a binary file - // while it is executing. Try to move it out of the way. + // while it is executing. Try to move it out of the way. // If the move fails, which is likely, we'll try again the // next time we do an install of this binary. if err := os.Rename(dst, dst+"~"); err == nil { @@ -1933,7 +2025,7 @@ func (b *builder) showcmd(dir string, format string, args ...interface{}) { // The output is expected to contain references to 'dir', usually // the source directory for the package that has failed to build. // showOutput rewrites mentions of dir with a relative path to dir -// when the relative path is shorter. This is usually more pleasant. +// when the relative path is shorter. This is usually more pleasant. // For example, if fmt doesn't compile and we are in src/html, // the output is // @@ -1991,7 +2083,7 @@ func relPaths(paths []string) []string { // errPrintedOutput is a special error indicating that a command failed // but that it generated output as well, and that output has already // been printed, so there's no point showing 'exit status 1' or whatever -// the wait status was. The main executor, builder.do, knows not to +// the wait status was. The main executor, builder.do, knows not to // print this error. var errPrintedOutput = errors.New("already printed output - no need to show error") @@ -2060,7 +2152,7 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter err := cmd.Run() // cmd.Run will fail on Unix if some other process has the binary - // we want to run open for writing. This can happen here because + // we want to run open for writing. This can happen here because // we build and install the cgo command and then run it. // If another command was kicked off while we were writing the // cgo binary, the child process for that command may be holding @@ -2072,27 +2164,27 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter // The answer is that running a command is fork and exec. // A child forked while the cgo fd is open inherits that fd. // Until the child has called exec, it holds the fd open and the - // kernel will not let us run cgo. Even if the child were to close + // kernel will not let us run cgo. Even if the child were to close // the fd explicitly, it would still be open from the time of the fork // until the time of the explicit close, and the race would remain. // // On Unix systems, this results in ETXTBSY, which formats // as "text file busy". Rather than hard-code specific error cases, - // we just look for that string. If this happens, sleep a little - // and try again. We let this happen three times, with increasing + // we just look for that string. If this happens, sleep a little + // and try again. We let this happen three times, with increasing // sleep lengths: 100+200+400 ms = 0.7 seconds. // // An alternate solution might be to split the cmd.Run into // separate cmd.Start and cmd.Wait, and then use an RWLock // to make sure that copyFile only executes when no cmd.Start - // call is in progress. However, cmd.Start (really syscall.forkExec) + // call is in progress. However, cmd.Start (really syscall.forkExec) // only guarantees that when it returns, the exec is committed to - // happen and succeed. It uses a close-on-exec file descriptor + // happen and succeed. It uses a close-on-exec file descriptor // itself to determine this, so we know that when cmd.Start returns, // at least one close-on-exec file descriptor has been closed. // However, we cannot be sure that all of them have been closed, // so the program might still encounter ETXTBSY even with such - // an RWLock. The race window would be smaller, perhaps, but not + // an RWLock. The race window would be smaller, perhaps, but not // guaranteed to be gone. // // Sleeping when we observe the race seems to be the most reliable @@ -2142,7 +2234,7 @@ func (b *builder) mkdir(dir string) error { b.exec.Lock() defer b.exec.Unlock() // We can be a little aggressive about being - // sure directories exist. Skip repeated calls. + // sure directories exist. Skip repeated calls. if b.mkdirCache[dir] { return nil } @@ -2180,14 +2272,13 @@ func mkAbs(dir, f string) string { type toolchain interface { // gc runs the compiler in a specific directory on a set of files // and returns the name of the generated output file. - // The compiler runs in the directory dir. gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, out []byte, err error) // cc runs the toolchain's C compiler in a directory on a C file // to produce an output file. cc(b *builder, p *Package, objdir, ofile, cfile string) error - // asm runs the assembler in a specific directory on a specific file - // to generate the named output file. - asm(b *builder, p *Package, obj, ofile, sfile string) error + // asm runs the assembler in a specific directory on specific files + // and returns a list of named output files. + asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) // pkgpath builds an appropriate path for a temporary package file. pkgpath(basedir string, p *Package) string // pack runs the archive packer in a specific directory to create @@ -2224,8 +2315,8 @@ func (noToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, return "", nil, noCompiler() } -func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { - return noCompiler() +func (noToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) { + return nil, noCompiler() } func (noToolchain) pkgpath(basedir string, p *Package) string { @@ -2282,7 +2373,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, // so that it can give good error messages about forward declarations. // Exceptions: a few standard packages have forward declarations for // pieces supplied behind-the-scenes by package runtime. - extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) + extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { case "bytes", "net", "os", "runtime/pprof", "sync", "time": @@ -2307,14 +2398,6 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, } } - for _, path := range p.Imports { - if i := strings.LastIndex(path, "/vendor/"); i >= 0 { - gcargs = append(gcargs, "-importmap", path[i+len("/vendor/"):]+"="+path) - } else if strings.HasPrefix(path, "vendor/") { - gcargs = append(gcargs, "-importmap", path[len("vendor/"):]+"="+path) - } - } - args := []interface{}{buildToolExec, tool("compile"), "-o", ofile, "-trimpath", b.work, buildGcflags, gcargs, "-D", p.localPrefix, importArgs} if ofile == archive { args = append(args, "-pack") @@ -2330,15 +2413,27 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, return ofile, output, err } -func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { +func (gcToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) { // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. inc := filepath.Join(goroot, "pkg", "include") - sfile = mkAbs(p.Dir, sfile) - args := []interface{}{buildToolExec, tool("asm"), "-o", ofile, "-trimpath", b.work, "-I", obj, "-I", inc, "-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch, buildAsmflags, sfile} - if err := b.run(p.Dir, p.ImportPath, nil, args...); err != nil { - return err + args := []interface{}{buildToolExec, tool("asm"), "-trimpath", b.work, "-I", obj, "-I", inc, "-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch, buildAsmflags} + if p.ImportPath == "runtime" && goarch == "386" { + for _, arg := range buildAsmflags { + if arg == "-dynlink" { + args = append(args, "-D=GOBUILDMODE_shared=1") + } + } } - return nil + var ofiles []string + for _, sfile := range sfiles { + ofile := obj + sfile[:len(sfile)-len(".s")] + ".o" + ofiles = append(ofiles, ofile) + a := append(args, "-o", ofile, mkAbs(p.Dir, sfile)) + if err := b.run(p.Dir, p.ImportPath, nil, a...); err != nil { + return nil, err + } + } + return ofiles, nil } // toolVerify checks that the command line args writes the same output file @@ -2381,8 +2476,10 @@ func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s // The archive file should have been created by the compiler. // Since it used to not work that way, verify. - if _, err := os.Stat(absAfile); err != nil { - fatalf("os.Stat of archive file failed: %v", err) + if !buildN { + if _, err := os.Stat(absAfile); err != nil { + fatalf("os.Stat of archive file failed: %v", err) + } } if buildN || buildX { @@ -2494,6 +2591,13 @@ func (gcToolchain) ld(b *builder, root *action, out string, allactions []*action if root.p.omitDWARF { ldflags = append(ldflags, "-w") } + if buildBuildmode == "plugin" { + pluginpath := root.p.ImportPath + if pluginpath == "command-line-arguments" { + pluginpath = "plugin/unnamed-" + root.p.buildID + } + ldflags = append(ldflags, "-pluginpath", pluginpath) + } // If the user has not specified the -extld option, then specify the // appropriate linker. In case of C++ code, use the compiler named @@ -2594,6 +2698,55 @@ func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, asmh if p.localPrefix != "" { gcargs = append(gcargs, "-fgo-relative-import-path="+p.localPrefix) } + savedirs := []string{} + for _, incdir := range importArgs { + if incdir != "-I" { + savedirs = append(savedirs, incdir) + } + } + + for _, path := range p.Imports { + // If this is a new vendor path, add it to the list of importArgs + if i := strings.LastIndex(path, "/vendor"); i >= 0 { + for _, dir := range savedirs { + // Check if the vendor path is already included in dir + if strings.HasSuffix(dir, path[:i+len("/vendor")]) { + continue + } + // Make sure this vendor path is not already in the list for importArgs + vendorPath := dir + "/" + path[:i+len("/vendor")] + for _, imp := range importArgs { + if imp == "-I" { + continue + } + // This vendorPath is already in the list + if imp == vendorPath { + goto nextSuffixPath + } + } + // New vendorPath not yet in the importArgs list, so add it + importArgs = append(importArgs, "-I", vendorPath) + nextSuffixPath: + } + } else if strings.HasPrefix(path, "vendor/") { + for _, dir := range savedirs { + // Make sure this vendor path is not already in the list for importArgs + vendorPath := dir + "/" + path[len("/vendor"):] + for _, imp := range importArgs { + if imp == "-I" { + continue + } + if imp == vendorPath { + goto nextPrefixPath + } + } + // This vendor path is needed and not already in the list, so add it + importArgs = append(importArgs, "-I", vendorPath) + nextPrefixPath: + } + } + } + args := stringList(tools.compiler(), importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) @@ -2603,15 +2756,24 @@ func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, asmh return ofile, output, err } -func (tools gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { - sfile = mkAbs(p.Dir, sfile) - defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} - if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { - defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) +func (tools gccgoToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) { + var ofiles []string + for _, sfile := range sfiles { + ofile := obj + sfile[:len(sfile)-len(".s")] + ".o" + ofiles = append(ofiles, ofile) + sfile = mkAbs(p.Dir, sfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) + } + defs = tools.maybePIC(defs) + defs = append(defs, b.gccArchArgs()...) + err := b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", obj, "-c", "-o", ofile, defs, sfile) + if err != nil { + return nil, err + } } - defs = tools.maybePIC(defs) - defs = append(defs, b.gccArchArgs()...) - return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", obj, "-c", "-o", ofile, defs, sfile) + return ofiles, nil } func (gccgoToolchain) pkgpath(basedir string, p *Package) string { @@ -2629,7 +2791,7 @@ func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles return b.run(p.Dir, p.ImportPath, nil, "ar", "rc", mkAbs(objDir, afile), absOfiles) } -func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (tools gccgoToolchain) link(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string, buildmode, desc string) error { // gccgo needs explicit linking with all package dependencies, // and all LDFLAGS from cgo dependencies. apackagePathsSeen := make(map[string]bool) @@ -2638,8 +2800,14 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions ldflags := b.gccArchArgs() cgoldflags := []string{} usesCgo := false - cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 - objc := len(root.p.MFiles) > 0 + cxx := false + objc := false + fortran := false + if root.p != nil { + cxx = len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 + objc = len(root.p.MFiles) > 0 + fortran = len(root.p.FFiles) > 0 + } readCgoFlags := func(flagsFile string) error { flags, err := ioutil.ReadFile(flagsFile) @@ -2686,11 +2854,11 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions } newarchive := newa.Name() - err = b.run(b.work, root.p.ImportPath, nil, "ar", "x", newarchive, "_cgo_flags") + err = b.run(b.work, desc, nil, "ar", "x", newarchive, "_cgo_flags") if err != nil { return "", err } - err = b.run(".", root.p.ImportPath, nil, "ar", "d", newarchive, "_cgo_flags") + err = b.run(".", desc, nil, "ar", "d", newarchive, "_cgo_flags") if err != nil { return "", err } @@ -2727,7 +2895,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions if !apackagePathsSeen[a.p.ImportPath] { apackagePathsSeen[a.p.ImportPath] = true target := a.target - if len(a.p.CgoFiles) > 0 { + if len(a.p.CgoFiles) > 0 || a.p.usesSwig() { target, err = readAndRemoveCgoFlags(target) if err != nil { return @@ -2777,6 +2945,9 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions if len(a.p.MFiles) > 0 { objc = true } + if len(a.p.FFiles) > 0 { + fortran = true + } } for i, o := range ofiles { @@ -2793,7 +2964,9 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions ldflags = append(ldflags, cgoldflags...) ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) - ldflags = append(ldflags, root.p.CgoLDFLAGS...) + if root.p != nil { + ldflags = append(ldflags, root.p.CgoLDFLAGS...) + } ldflags = stringList("-Wl,-(", ldflags, "-Wl,-)") @@ -2808,7 +2981,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions } var realOut string - switch ldBuildmode { + switch buildmode { case "exe": if usesCgo && goos == "linux" { ldflags = append(ldflags, "-Wl,-E") @@ -2824,7 +2997,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions // initialization code. // // The user remains responsible for linking against - // -lgo -lpthread -lm in the final link. We can't use + // -lgo -lpthread -lm in the final link. We can't use // -r to pick them up because we can't combine // split-stack and non-split-stack code in a single -r // link, and libgo picks up non-split-stack code from @@ -2843,12 +3016,14 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions case "c-shared": ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc") + case "shared": + ldflags = append(ldflags, "-zdefs", "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") default: - fatalf("-buildmode=%s not supported for gccgo", ldBuildmode) + fatalf("-buildmode=%s not supported for gccgo", buildmode) } - switch ldBuildmode { + switch buildmode { case "exe", "c-shared": if cxx { ldflags = append(ldflags, "-lstdc++") @@ -2856,43 +3031,40 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions if objc { ldflags = append(ldflags, "-lobjc") } + if fortran { + fc := os.Getenv("FC") + if fc == "" { + fc = "gfortran" + } + // support gfortran out of the box and let others pass the correct link options + // via CGO_LDFLAGS + if strings.Contains(fc, "gfortran") { + ldflags = append(ldflags, "-lgfortran") + } + } } - if err := b.run(".", root.p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil { + if err := b.run(".", desc, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil { return err } - switch ldBuildmode { + switch buildmode { case "c-archive": - if err := b.run(".", root.p.ImportPath, nil, "ar", "rc", realOut, out); err != nil { + if err := b.run(".", desc, nil, "ar", "rc", realOut, out); err != nil { return err } } return nil } +func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { + return tools.link(b, root, out, allactions, mainpkg, ofiles, ldBuildmode, root.p.ImportPath) +} + func (tools gccgoToolchain) ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error { - args := []string{"-o", out, "-shared", "-nostdlib", "-zdefs", "-Wl,--whole-archive"} - for _, a := range toplevelactions { - args = append(args, a.target) - } - args = append(args, "-Wl,--no-whole-archive", "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") - shlibs := []string{} - for _, a := range allactions { - if strings.HasSuffix(a.target, ".so") { - shlibs = append(shlibs, a.target) - } - } - for _, shlib := range shlibs { - args = append( - args, - "-L"+filepath.Dir(shlib), - "-Wl,-rpath="+filepath.Dir(shlib), - "-l"+strings.TrimSuffix( - strings.TrimPrefix(filepath.Base(shlib), "lib"), - ".so")) - } - return b.run(".", out, nil, tools.linker(), args, buildGccgoflags) + fakeRoot := &action{} + fakeRoot.deps = toplevelactions + return tools.link(b, fakeRoot, out, allactions, "", nil, "shared", out) } func (tools gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { @@ -2915,7 +3087,7 @@ func (tools gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile stri // maybePIC adds -fPIC to the list of arguments if needed. func (tools gccgoToolchain) maybePIC(args []string) []string { switch buildBuildmode { - case "c-shared", "shared": + case "c-shared", "shared", "plugin": args = append(args, "-fPIC") } return args @@ -2950,10 +3122,25 @@ func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) er return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(p.Dir)) } +// gfortran runs the gfortran Fortran compiler to create an object from a single Fortran file. +func (b *builder) gfortran(p *Package, out string, flags []string, ffile string) error { + return b.ccompile(p, out, flags, ffile, b.gfortranCmd(p.Dir)) +} + // ccompile runs the given C or C++ compiler and creates an object from a single source file. -func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error { +func (b *builder) ccompile(p *Package, outfile string, flags []string, file string, compiler []string) error { file = mkAbs(p.Dir, file) - return b.run(p.Dir, p.ImportPath, nil, compiler, flags, "-o", out, "-c", file) + desc := p.ImportPath + output, err := b.runOut(p.Dir, desc, nil, compiler, flags, "-o", outfile, "-c", file) + if len(output) > 0 { + b.showOutput(p.Dir, desc, b.processOutput(output)) + if err != nil { + err = errPrintedOutput + } else if os.Getenv("GO_BUILDER_NAME") != "" { + return errors.New("C compiler warning promoted to error on Go builders") + } + } + return err } // gccld runs the gcc linker to create an executable from a set of object files. @@ -2979,6 +3166,11 @@ func (b *builder) gxxCmd(objdir string) []string { return b.ccompilerCmd("CXX", defaultCXX, objdir) } +// gfortranCmd returns a gfortran command line prefix. +func (b *builder) gfortranCmd(objdir string) []string { + return b.ccompilerCmd("FC", "gfortran", objdir) +} + // ccompilerCmd returns a command line prefix for the given environment // variable and using the default command when the variable is empty. func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { @@ -3016,6 +3208,17 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // disable word wrapping in error messages a = append(a, "-fmessage-length=0") + // Tell gcc not to include the work directory in object files. + if b.gccSupportsFlag("-fdebug-prefix-map=a=b") { + a = append(a, "-fdebug-prefix-map="+b.work+"=/tmp/go-build") + } + + // Tell gcc not to include flags in object files, which defeats the + // point of -fdebug-prefix-map above. + if b.gccSupportsFlag("-gno-record-gcc-switches") { + a = append(a, "-gno-record-gcc-switches") + } + // On OS X, some of the compilers behave as if -fno-common // is always set, and the Mach-O linker in 6l/8l assumes this. // See https://golang.org/issue/3253. @@ -3030,19 +3233,24 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // -no-pie must be passed when doing a partial link with -Wl,-r. But -no-pie is // not supported by all compilers. func (b *builder) gccSupportsNoPie() bool { - if goos != "linux" { - // On some BSD platforms, error messages from the - // compiler make it to the console despite cmd.Std* - // all being nil. As -no-pie is only required on linux - // systems so far, we only test there. - return false + return b.gccSupportsFlag("-no-pie") +} + +// gccSupportsFlag checks to see if the compiler supports a flag. +func (b *builder) gccSupportsFlag(flag string) bool { + b.exec.Lock() + defer b.exec.Unlock() + if b, ok := b.flagCache[flag]; ok { + return b } - src := filepath.Join(b.work, "trivial.c") - if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil { - return false + if b.flagCache == nil { + src := filepath.Join(b.work, "trivial.c") + if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil { + return false + } + b.flagCache = make(map[string]bool) } - cmdArgs := b.gccCmd(b.work) - cmdArgs = append(cmdArgs, "-no-pie", "-c", "trivial.c") + cmdArgs := append(envList("CC", defaultCC), flag, "-c", "trivial.c") if buildN || buildX { b.showcmd(b.work, "%s", joinUnambiguously(cmdArgs)) if buildN { @@ -3051,9 +3259,11 @@ func (b *builder) gccSupportsNoPie() bool { } cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) cmd.Dir = b.work - cmd.Env = envForDir(cmd.Dir, os.Environ()) + cmd.Env = mergeEnvLists([]string{"LC_ALL=C"}, envForDir(cmd.Dir, os.Environ())) out, err := cmd.CombinedOutput() - return err == nil && !bytes.Contains(out, []byte("unrecognized")) + supported := err == nil && !bytes.Contains(out, []byte("unrecognized")) + b.flagCache[flag] = supported + return supported } // gccArchArgs returns arguments to pass to gcc based on the architecture. @@ -3065,6 +3275,12 @@ func (b *builder) gccArchArgs() []string { return []string{"-m64"} case "arm": return []string{"-marm"} // not thumb + case "s390x": + return []string{"-m64", "-march=z196"} + case "mips64", "mips64le": + return []string{"-mabi=64"} + case "mips", "mipsle": + return []string{"-mabi=32", "-march=mips32"} } return nil } @@ -3079,25 +3295,23 @@ func envList(key, def string) []string { return strings.Fields(v) } -// Return the flags to use when invoking the C or C++ compilers, or cgo. -func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) { - var defaults string - if def { - defaults = "-g -O2" - } +// Return the flags to use when invoking the C, C++ or Fortran compilers, or cgo. +func (b *builder) cflags(p *Package) (cppflags, cflags, cxxflags, fflags, ldflags []string) { + defaults := "-g -O2" cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS) cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) + fflags = stringList(envList("CGO_FFLAGS", defaults), p.CgoFFLAGS) ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) return } var cgoRe = regexp.MustCompile(`[/\\:]`) -func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { - cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true) - _, cgoexeCFLAGS, _, _ := b.cflags(p, false) +func (b *builder) cgo(a *action, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, objdirCgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) { + p := a.p + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p) cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) // If we are compiling Objective-C code, then we need to link against libobjc @@ -3105,7 +3319,20 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc") } - if buildMSan && p.ImportPath != "runtime/cgo" { + // Likewise for Fortran, except there are many Fortran compilers. + // Support gfortran out of the box and let others pass the correct link options + // via CGO_LDFLAGS + if len(ffiles) > 0 { + fc := os.Getenv("FC") + if fc == "" { + fc = "gfortran" + } + if strings.Contains(fc, "gfortran") { + cgoLDFLAGS = append(cgoLDFLAGS, "-lgfortran") + } + } + + if buildMSan { cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...) cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...) } @@ -3113,20 +3340,33 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi // Allows including _cgo_export.h from .[ch] files in the package. cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj) + // If we have cgo files in the object directory, then copy any + // other cgo files into the object directory, and pass a + // -srcdir option to cgo. + var srcdirarg []string + if len(objdirCgofiles) > 0 { + for _, fn := range cgofiles { + if err := b.copyFile(a, obj+filepath.Base(fn), filepath.Join(p.Dir, fn), 0666, false); err != nil { + return nil, nil, err + } + } + cgofiles = append(cgofiles, objdirCgofiles...) + srcdirarg = []string{"-srcdir", obj} + } + // cgo // TODO: CGO_FLAGS? gofiles := []string{obj + "_cgo_gotypes.go"} - cfiles := []string{"_cgo_main.c", "_cgo_export.c"} + cfiles := []string{"_cgo_export.c"} for _, fn := range cgofiles { f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_") gofiles = append(gofiles, obj+f+"cgo1.go") cfiles = append(cfiles, f+"cgo2.c") } - defunC := obj + "_cgo_defun.c" - cgoflags := []string{} // TODO: make cgo not depend on $GOARCH? + cgoflags := []string{} if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } @@ -3163,154 +3403,166 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi cgoflags = append(cgoflags, "-exportheader="+obj+"_cgo_install.h") } - if err := b.run(p.Dir, p.ImportPath, cgoenv, buildToolExec, cgoExe, "-objdir", obj, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoexeCFLAGS, cgofiles); err != nil { + if err := b.run(p.Dir, p.ImportPath, cgoenv, buildToolExec, cgoExe, srcdirarg, "-objdir", obj, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil { return nil, nil, err } outGo = append(outGo, gofiles...) - // cc _cgo_defun.c - _, gccgo := buildToolchain.(gccgoToolchain) - if gccgo { - defunObj := obj + "_cgo_defun.o" - if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil { - return nil, nil, err - } - outObj = append(outObj, defunObj) - } - // gcc - var linkobj []string - - var bareLDFLAGS []string - // When linking relocatable objects, various flags need to be - // filtered out as they are inapplicable and can cause some linkers - // to fail. - for i := 0; i < len(cgoLDFLAGS); i++ { - f := cgoLDFLAGS[i] - switch { - // skip "-lc" or "-l somelib" - case strings.HasPrefix(f, "-l"): - if f == "-l" { - i++ - } - // skip "-framework X" on Darwin - case goos == "darwin" && f == "-framework": - i++ - // skip "*.{dylib,so,dll}" - case strings.HasSuffix(f, ".dylib"), - strings.HasSuffix(f, ".so"), - strings.HasSuffix(f, ".dll"): - // Remove any -fsanitize=foo flags. - // Otherwise the compiler driver thinks that we are doing final link - // and links sanitizer runtime into the object file. But we are not doing - // the final link, we will link the resulting object file again. And - // so the program ends up with two copies of sanitizer runtime. - // See issue 8788 for details. - case strings.HasPrefix(f, "-fsanitize="): - continue - // runpath flags not applicable unless building a shared - // object or executable; see issue 12115 for details. This - // is necessary as Go currently does not offer a way to - // specify the set of LDFLAGS that only apply to shared - // objects. - case strings.HasPrefix(f, "-Wl,-rpath"): - if f == "-Wl,-rpath" || f == "-Wl,-rpath-link" { - // Skip following argument to -rpath* too. - i++ - } - default: - bareLDFLAGS = append(bareLDFLAGS, f) - } - } - - var staticLibs []string - if goos == "windows" { - // libmingw32 and libmingwex have some inter-dependencies, - // so must use linker groups. - staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"} - } - cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) for _, cfile := range cfiles { ofile := obj + cfile[:len(cfile)-1] + "o" if err := b.gcc(p, ofile, cflags, obj+cfile); err != nil { return nil, nil, err } - linkobj = append(linkobj, ofile) - if !strings.HasSuffix(ofile, "_cgo_main.o") { - outObj = append(outObj, ofile) - } + outObj = append(outObj, ofile) } for _, file := range gccfiles { - ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" + base := filepath.Base(file) + ofile := obj + cgoRe.ReplaceAllString(base[:len(base)-1], "_") + "o" if err := b.gcc(p, ofile, cflags, file); err != nil { return nil, nil, err } - linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS) for _, file := range gxxfiles { // Append .o to the file, just in case the pkg has file.c and file.cpp - ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" + ofile := obj + cgoRe.ReplaceAllString(filepath.Base(file), "_") + ".o" if err := b.gxx(p, ofile, cxxflags, file); err != nil { return nil, nil, err } - linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } for _, file := range mfiles { // Append .o to the file, just in case the pkg has file.c and file.m - ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" + ofile := obj + cgoRe.ReplaceAllString(filepath.Base(file), "_") + ".o" if err := b.gcc(p, ofile, cflags, file); err != nil { return nil, nil, err } - linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } - linkobj = append(linkobj, p.SysoFiles...) - dynobj := obj + "_cgo_.o" - pie := (goarch == "arm" && goos == "linux") || goos == "android" - if pie { // we need to use -pie for Linux/ARM to get accurate imported sym - cgoLDFLAGS = append(cgoLDFLAGS, "-pie") + fflags := stringList(cgoCPPFLAGS, cgoFFLAGS) + for _, file := range ffiles { + // Append .o to the file, just in case the pkg has file.c and file.f + ofile := obj + cgoRe.ReplaceAllString(filepath.Base(file), "_") + ".o" + if err := b.gfortran(p, ofile, fflags, file); err != nil { + return nil, nil, err + } + outObj = append(outObj, ofile) } - if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil { - return nil, nil, err + + switch buildToolchain.(type) { + case gcToolchain: + importGo := obj + "_cgo_import.go" + if err := b.dynimport(p, obj, importGo, cgoExe, cflags, cgoLDFLAGS, outObj); err != nil { + return nil, nil, err + } + outGo = append(outGo, importGo) + + ofile := obj + "_all.o" + if err := b.collect(p, obj, ofile, cgoLDFLAGS, outObj); err != nil { + return nil, nil, err + } + outObj = []string{ofile} + + case gccgoToolchain: + defunC := obj + "_cgo_defun.c" + defunObj := obj + "_cgo_defun.o" + if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil { + return nil, nil, err + } + outObj = append(outObj, defunObj) + + default: + noCompiler() } - if pie { // but we don't need -pie for normal cgo programs - cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1] + + return outGo, outObj, nil +} + +// dynimport creates a Go source file named importGo containing +// //go:cgo_import_dynamic directives for each symbol or library +// dynamically imported by the object files outObj. +func (b *builder) dynimport(p *Package, obj, importGo, cgoExe string, cflags, cgoLDFLAGS, outObj []string) error { + cfile := obj + "_cgo_main.c" + ofile := obj + "_cgo_main.o" + if err := b.gcc(p, ofile, cflags, cfile); err != nil { + return err } - if _, ok := buildToolchain.(gccgoToolchain); ok { - // we don't use dynimport when using gccgo. - return outGo, outObj, nil + linkobj := stringList(ofile, outObj, p.SysoFiles) + dynobj := obj + "_cgo_.o" + + // we need to use -pie for Linux/ARM to get accurate imported sym + ldflags := cgoLDFLAGS + if (goarch == "arm" && goos == "linux") || goos == "android" { + ldflags = append(ldflags, "-pie") + } + if err := b.gccld(p, dynobj, ldflags, linkobj); err != nil { + return err } // cgo -dynimport - importGo := obj + "_cgo_import.go" - cgoflags = []string{} + var cgoflags []string if p.Standard && p.ImportPath == "runtime/cgo" { - cgoflags = append(cgoflags, "-dynlinker") // record path to dynamic linker + cgoflags = []string{"-dynlinker"} // record path to dynamic linker } - if err := b.run(p.Dir, p.ImportPath, nil, buildToolExec, cgoExe, "-objdir", obj, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags); err != nil { - return nil, nil, err - } - outGo = append(outGo, importGo) + return b.run(p.Dir, p.ImportPath, nil, buildToolExec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags) +} - ofile := obj + "_all.o" - var gccObjs, nonGccObjs []string - for _, f := range outObj { - if strings.HasSuffix(f, ".o") { - gccObjs = append(gccObjs, f) - } else { - nonGccObjs = append(nonGccObjs, f) +// collect partially links the object files outObj into a single +// relocatable object file named ofile. +func (b *builder) collect(p *Package, obj, ofile string, cgoLDFLAGS, outObj []string) error { + // When linking relocatable objects, various flags need to be + // filtered out as they are inapplicable and can cause some linkers + // to fail. + var ldflags []string + for i := 0; i < len(cgoLDFLAGS); i++ { + f := cgoLDFLAGS[i] + switch { + // skip "-lc" or "-l somelib" + case strings.HasPrefix(f, "-l"): + if f == "-l" { + i++ + } + // skip "-framework X" on Darwin + case goos == "darwin" && f == "-framework": + i++ + // skip "*.{dylib,so,dll,o,a}" + case strings.HasSuffix(f, ".dylib"), + strings.HasSuffix(f, ".so"), + strings.HasSuffix(f, ".dll"), + strings.HasSuffix(f, ".o"), + strings.HasSuffix(f, ".a"): + // Remove any -fsanitize=foo flags. + // Otherwise the compiler driver thinks that we are doing final link + // and links sanitizer runtime into the object file. But we are not doing + // the final link, we will link the resulting object file again. And + // so the program ends up with two copies of sanitizer runtime. + // See issue 8788 for details. + case strings.HasPrefix(f, "-fsanitize="): + continue + // runpath flags not applicable unless building a shared + // object or executable; see issue 12115 for details. This + // is necessary as Go currently does not offer a way to + // specify the set of LDFLAGS that only apply to shared + // objects. + case strings.HasPrefix(f, "-Wl,-rpath"): + if f == "-Wl,-rpath" || f == "-Wl,-rpath-link" { + // Skip following argument to -rpath* too. + i++ + } + default: + ldflags = append(ldflags, f) } } - ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs) + + ldflags = append(ldflags, "-Wl,-r", "-nostdlib") if b.gccSupportsNoPie() { ldflags = append(ldflags, "-no-pie") @@ -3319,16 +3571,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi // We are creating an object file, so we don't want a build ID. ldflags = b.disableBuildID(ldflags) - if err := b.gccld(p, ofile, ldflags, gccObjs); err != nil { - return nil, nil, err - } - - // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows - // must be processed before the gcc-generated objects. - // Put it first. https://golang.org/issue/2601 - outObj = stringList(nonGccObjs, ofile) - - return outGo, outObj, nil + return b.gccld(p, ofile, ldflags, outObj) } // Run SWIG on all SWIG input files. @@ -3437,6 +3680,13 @@ func (b *builder) swigVersionCheck() error { return swigCheck } +// Find the value to pass for the -intgosize option to swig. +var ( + swigIntSizeOnce sync.Once + swigIntSize string + swigIntSizeError error +) + // This code fails to build if sizeof(int) <= 32 const swigIntSizeCode = ` package main @@ -3444,8 +3694,8 @@ const i int = 1 << 32 ` // Determine the size of int on the target system for the -intgosize option -// of swig >= 2.0.9 -func (b *builder) swigIntSize(obj string) (intsize string, err error) { +// of swig >= 2.0.9. Run only once. +func (b *builder) swigDoIntSize(obj string) (intsize string, err error) { if buildN { return "$INTBITS", nil } @@ -3463,9 +3713,18 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) { return "64", nil } +// Determine the size of int on the target system for the -intgosize option +// of swig >= 2.0.9. +func (b *builder) swigIntSize(obj string) (intsize string, err error) { + swigIntSizeOnce.Do(func() { + swigIntSize, swigIntSizeError = b.swigDoIntSize(obj) + }) + return swigIntSize, swigIntSizeError +} + // Run SWIG on one SWIG input file. func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) { - cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.cflags(p) var cflags []string if cxx { cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS) @@ -3528,17 +3787,17 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) // swig warning } - return obj + goFile, obj + gccBase + gccExt, nil + return goFile, obj + gccBase + gccExt, nil } // disableBuildID adjusts a linker command line to avoid creating a // build ID when creating an object file rather than an executable or -// shared library. Some systems, such as Ubuntu, always add +// shared library. Some systems, such as Ubuntu, always add // --build-id to every link, but we don't want a build ID when we are -// producing an object file. On some of those system a plain -r (not +// producing an object file. On some of those system a plain -r (not // -Wl,-r) will turn off --build-id, but clang 3.0 doesn't support a -// plain -r. I don't know how to turn off --build-id when using clang -// other than passing a trailing --build-id=none. So that is what we +// plain -r. I don't know how to turn off --build-id when using clang +// other than passing a trailing --build-id=none. So that is what we // do, but only on systems likely to support it, which is to say, // systems that normally use gold or the GNU linker. func (b *builder) disableBuildID(ldflags []string) []string { @@ -3577,7 +3836,11 @@ func instrumentInit() { return } if buildRace && buildMSan { - fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously\n", flag.Args()[0]) + os.Exit(2) + } + if buildMSan && (goos != "linux" || goarch != "amd64") { + fmt.Fprintf(os.Stderr, "-msan is not supported on %s/%s\n", goos, goarch) os.Exit(2) } if goarch != "amd64" || goos != "linux" && goos != "freebsd" && goos != "darwin" && goos != "windows" { |