diff options
Diffstat (limited to 'llgo/third_party/gofrontend/libgo/go/cmd/go/build.go')
| -rw-r--r-- | llgo/third_party/gofrontend/libgo/go/cmd/go/build.go | 1413 |
1 files changed, 1046 insertions, 367 deletions
diff --git a/llgo/third_party/gofrontend/libgo/go/cmd/go/build.go b/llgo/third_party/gofrontend/libgo/go/cmd/go/build.go index 781a43b5d99..865871c5314 100644 --- a/llgo/third_party/gofrontend/libgo/go/cmd/go/build.go +++ b/llgo/third_party/gofrontend/libgo/go/cmd/go/build.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "container/heap" + "debug/elf" "errors" "flag" "fmt" @@ -34,21 +35,23 @@ var cmdBuild = &Command{ Build compiles the packages named by the import paths, along with their dependencies, but it does not install the results. -If the arguments are a list of .go files, build treats them as a list -of source files specifying a single package. +If the arguments to build are a list of .go files, build treats +them as a list of source files specifying a single package. -When the command line specifies a single main package, -build writes the resulting executable to output. -Otherwise build compiles the packages but discards the results, +When compiling a single main package, build writes +the resulting executable to an output file named after +the first source file ('go build ed.go rx.go' writes 'ed' or 'ed.exe') +or the source code directory ('go build unix/sam' writes 'sam' or 'sam.exe'). +The '.exe' suffix is added when writing a Windows executable. + +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. -The -o flag specifies the output file name. If not specified, the -output file name depends on the arguments and derives from the name -of the package, such as p.a for package p, unless p is 'main'. If -the package is main and file names are provided, the file name -derives from the first file name mentioned, such as f1 for 'go build -f1.go f2.go'; with no files provided ('go build'), the output file -name is the base name of the containing directory. +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 +in the last two paragraphs. The -i flag installs the packages that are dependencies of the target. @@ -57,12 +60,12 @@ and test commands: -a force rebuilding of packages that are already up-to-date. - In Go releases, does not apply to the standard library. -n print the commands but do not run them. -p n the number of builds that can be run in parallel. - The default is the number of CPUs available. + The default is the number of CPUs available, except + on darwin/arm which defaults to 1. -race enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. @@ -74,33 +77,54 @@ and test commands: -x print the commands. - -ccflags 'arg list' - arguments to pass on each 5c, 6c, or 8c compiler invocation. + -asmflags 'flag list' + arguments to pass on each go tool asm invocation. + -buildmode mode + build mode to use. See 'go help buildmode' for more. -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc). -gccgoflags 'arg list' arguments to pass on each gccgo compiler/linker invocation. -gcflags 'arg list' - arguments to pass on each 5g, 6g, or 8g compiler invocation. + arguments to pass on each go tool compile invocation. -installsuffix suffix a suffix to use in the name of the package installation directory, in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to race - or, if set explicitly, has _race appended to it. + or, if set explicitly, has _race appended to it. Using a -buildmode + option that requires non-default compile flags has a similar effect. -ldflags 'flag list' - arguments to pass on each 5l, 6l, or 8l linker invocation. + arguments to pass on each go tool link invocation. + -linkshared + link against shared libraries previously created with + -buildmode=shared + -pkgdir dir + install and load all packages from dir instead of the usual locations. + For example, when building with a non-standard configuration, + use -pkgdir to keep generated packages in a separate location. -tags 'tag list' a list of build tags to consider satisfied during the build. For more information about build tags, see the description of build constraints in the documentation for the go/build package. + -toolexec 'cmd args' + a program to use to invoke toolchain programs like vet and asm. + For example, instead of running asm, the go command will run + 'cmd args /path/to/asm <arguments for asm>'. The list flags accept a space-separated list of strings. To embed spaces in an element in the list, surround it with either single or double quotes. For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, -run 'go help gopath'. For more about calling between Go and C/C++, -run 'go help c'. +run 'go help gopath'. +For more about calling between Go and C/C++, run 'go help c'. + +Note: Build adheres to certain conventions such as those described +by 'go help gopath'. Not all projects can follow these conventions, +however. Installations that have their own conventions or that use +a separate software build system may choose to use lower-level +invocations such as 'go tool compile' and 'go tool link' to avoid +some of the overheads and design decisions of the build tool. See also: go install, go get, go clean. `, @@ -115,6 +139,17 @@ 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. @@ -126,16 +161,21 @@ var buildX bool // -x flag var buildI bool // -i flag var buildO = cmdBuild.Flag.String("o", "", "output file") var buildWork bool // -work flag +var buildAsmflags []string // -asmflags flag var buildGcflags []string // -gcflags flag -var buildCcflags []string // -ccflags flag var buildLdflags []string // -ldflags flag var buildGccgoflags []string // -gccgoflags flag var buildRace bool // -race flag +var buildToolExec []string // -toolexec flag +var buildBuildmode string // -buildmode flag +var buildLinkshared bool // -linkshared flag +var buildPkgdir string // -pkgdir flag // Require the source for go std packages var reqStdPkgSrc bool var buildContext = build.Default var buildToolchain toolchain = noToolchain{} +var ldBuildmode string // buildCompiler implements flag.Var. // It implements Set by updating both @@ -171,21 +211,25 @@ func init() { // addBuildFlags adds the flags common to the build, clean, get, // install, list, run, and test commands. func addBuildFlags(cmd *Command) { - // NOTE: If you add flags here, also add them to testflag.go. cmd.Flag.BoolVar(&buildA, "a", false, "") cmd.Flag.BoolVar(&buildN, "n", false, "") cmd.Flag.IntVar(&buildP, "p", buildP, "") - cmd.Flag.StringVar(&buildContext.InstallSuffix, "installsuffix", "", "") cmd.Flag.BoolVar(&buildV, "v", false, "") cmd.Flag.BoolVar(&buildX, "x", false, "") - cmd.Flag.BoolVar(&buildWork, "work", false, "") + + cmd.Flag.Var((*stringsFlag)(&buildAsmflags), "asmflags", "") + cmd.Flag.Var(buildCompiler{}, "compiler", "") + cmd.Flag.StringVar(&buildBuildmode, "buildmode", "default", "") cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "") - cmd.Flag.Var((*stringsFlag)(&buildCcflags), "ccflags", "") - cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "") cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "") - cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") - cmd.Flag.Var(buildCompiler{}, "compiler", "") + cmd.Flag.StringVar(&buildContext.InstallSuffix, "installsuffix", "", "") + cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "") + cmd.Flag.BoolVar(&buildLinkshared, "linkshared", false, "") + cmd.Flag.StringVar(&buildPkgdir, "pkgdir", "", "") cmd.Flag.BoolVar(&buildRace, "race", false, "") + cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") + cmd.Flag.Var((*stringsFlag)(&buildToolExec), "toolexec", "") + cmd.Flag.BoolVar(&buildWork, "work", false, "") switch build.Default.Compiler { case "gc": reqStdPkgSrc = true @@ -266,8 +310,113 @@ func (v *stringsFlag) String() string { return "<stringsFlag>" } +func pkgsMain(pkgs []*Package) (res []*Package) { + for _, p := range pkgs { + if p.Name == "main" { + res = append(res, p) + } + } + return res +} + +func pkgsNotMain(pkgs []*Package) (res []*Package) { + for _, p := range pkgs { + if p.Name != "main" { + res = append(res, p) + } + } + return res +} + +var pkgsFilter = func(pkgs []*Package) []*Package { return pkgs } + +func buildModeInit() { + _, gccgo := buildToolchain.(gccgoToolchain) + var codegenArg string + platform := goos + "/" + goarch + switch buildBuildmode { + case "archive": + pkgsFilter = pkgsNotMain + case "c-archive": + pkgsFilter = func(p []*Package) []*Package { + if len(p) != 1 || p[0].Name != "main" { + fatalf("-buildmode=c-archive requires exactly one main package") + } + return p + } + exeSuffix = ".a" + ldBuildmode = "c-archive" + case "c-shared": + pkgsFilter = pkgsMain + if gccgo { + codegenArg = "-fPIC" + } else { + switch platform { + case "linux/amd64": + codegenArg = "-shared" + case "linux/arm": + buildAsmflags = append(buildAsmflags, "-shared") + case "darwin/amd64": + case "android/arm": + default: + fatalf("-buildmode=c-shared not supported on %s\n", platform) + } + } + ldBuildmode = "c-shared" + case "default": + ldBuildmode = "exe" + case "exe": + pkgsFilter = pkgsMain + ldBuildmode = "exe" + case "shared": + pkgsFilter = pkgsNotMain + if gccgo { + codegenArg = "-fPIC" + } else { + switch platform { + case "linux/amd64": + default: + fatalf("-buildmode=shared not supported on %s\n", platform) + } + codegenArg = "-dynlink" + } + if *buildO != "" { + fatalf("-buildmode=shared and -o not supported together") + } + ldBuildmode = "shared" + default: + fatalf("buildmode=%s not supported", buildBuildmode) + } + if buildLinkshared { + if gccgo { + codegenArg = "-fPIC" + } else { + if platform != "linux/amd64" { + fmt.Fprintf(os.Stderr, "go %s: -linkshared is only supported on linux/amd64\n", flag.Args()[0]) + os.Exit(2) + } + codegenArg = "-dynlink" + // TODO(mwhudson): remove -w when that gets fixed in linker. + buildLdflags = append(buildLdflags, "-linkshared", "-w") + } + } + if codegenArg != "" { + if gccgo { + buildGccgoflags = append(buildGccgoflags, codegenArg) + } else { + buildAsmflags = append(buildAsmflags, codegenArg) + buildGcflags = append(buildGcflags, codegenArg) + } + if buildContext.InstallSuffix != "" { + buildContext.InstallSuffix += "_" + } + buildContext.InstallSuffix += codegenArg[1:] + } +} + func runBuild(cmd *Command, args []string) { raceInit() + buildModeInit() var b builder b.init() @@ -305,16 +454,21 @@ func runBuild(cmd *Command, args []string) { fatalf("no packages to build") } p := pkgs[0] - p.target = "" // must build - not up to date + p.target = *buildO + p.Stale = true // must build - not up to date a := b.action(modeInstall, depMode, p) - a.target = *buildO b.do(a) return } - a := &action{} - for _, p := range packages(args) { - a.deps = append(a.deps, b.action(modeBuild, depMode, p)) + var a *action + if buildBuildmode == "shared" { + a = b.libaction(libname(args), pkgsFilter(packages(args)), modeBuild, depMode) + } else { + a = &action{} + for _, p := range pkgsFilter(packages(args)) { + a.deps = append(a.deps, b.action(modeBuild, depMode, p)) + } } b.do(a) } @@ -333,18 +487,47 @@ See also: go build, go get, go clean. `, } +// libname returns the filename to use for the shared library when using +// -buildmode=shared. The rules we use are: +// 1) Drop any trailing "/..."s if present +// 2) Change / to - +// 3) Join arguments with , +// So std -> libstd.so +// a b/... -> liba,b.so +// gopkg.in/tomb.v2 -> libgopkg.in-tomb.v2.so +func libname(args []string) string { + var libname string + for _, arg := range args { + arg = strings.TrimSuffix(arg, "/...") + arg = strings.Replace(arg, "/", "-", -1) + if libname == "" { + libname = arg + } else { + libname += "," + arg + } + } + // TODO(mwhudson): Needs to change for platforms that use different naming + // conventions... + return "lib" + libname + ".so" +} + func runInstall(cmd *Command, args []string) { raceInit() - pkgs := packagesForBuild(args) + buildModeInit() + pkgs := pkgsFilter(packagesForBuild(args)) for _, p := range pkgs { if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { - if p.cmdline { + switch { + case p.gobinSubdir: + errorf("go install: cannot install cross-compiled binaries when GOBIN is set") + case p.cmdline: errorf("go install: no install location for .go files listed on command line (GOBIN not set)") - } else if p.ConflictDir != "" { + case p.ConflictDir != "": errorf("go install: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) - } else { - errorf("go install: no install location for directory %s outside GOPATH", p.Dir) + default: + errorf("go install: no install location for directory %s outside GOPATH\n"+ + "\tFor more details see: go help gopath", p.Dir) } } } @@ -352,18 +535,68 @@ func runInstall(cmd *Command, args []string) { var b builder b.init() - a := &action{} - for _, p := range pkgs { - a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) + var a *action + if buildBuildmode == "shared" { + a = b.libaction(libname(args), pkgs, modeInstall, modeInstall) + } else { + a = &action{} + var tools []*action + for _, p := range pkgs { + // If p is a tool, delay the installation until the end of the build. + // This avoids installing assemblers/compilers that are being executed + // by other steps in the build. + // cmd/cgo is handled specially in b.action, so that we can + // both build and use it in the same 'go install'. + action := b.action(modeInstall, modeInstall, p) + if goTools[p.ImportPath] == toTool && p.ImportPath != "cmd/cgo" { + a.deps = append(a.deps, action.deps...) + action.deps = append(action.deps, a) + tools = append(tools, action) + continue + } + a.deps = append(a.deps, action) + } + if len(tools) > 0 { + a = &action{ + deps: tools, + } + } } b.do(a) + exitIfErrors() + + // Success. If this command is 'go install' with no arguments + // and the current directory (the implicit argument) is a command, + // remove any leftover command binary from a previous 'go build'. + // The binary is installed; it's not needed here anymore. + // And worse it might be a stale copy, which you don't want to find + // instead of the installed one if $PATH contains dot. + // One way to view this behavior is that it is as if 'go install' first + // runs 'go build' and the moves the generated file to the install dir. + // See issue 9645. + if len(args) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" { + // Compute file 'go build' would have created. + // If it exists and is an executable file, remove it. + _, targ := filepath.Split(pkgs[0].ImportPath) + targ += exeSuffix + if filepath.Join(pkgs[0].Dir, targ) != pkgs[0].Target { // maybe $GOBIN is the current directory + fi, err := os.Stat(targ) + if err == nil { + m := fi.Mode() + if m.IsRegular() { + if m&0111 != 0 || goos == "windows" { // windows never sets executable bit + os.Remove(targ) + } + } + } + } + } } // Global build parameters (used during package load) var ( goarch string goos string - archChar string exeSuffix string ) @@ -373,16 +606,6 @@ func init() { if goos == "windows" { exeSuffix = ".exe" } - var err error - archChar, err = build.ArchChar(goarch) - if err != nil { - if _, isgc := buildToolchain.(gcToolchain); isgc { - fatalf("%s", err) - } - // archChar is only required for gcToolchain, if we're using - // another toolchain leave it blank. - archChar = "" - } } // A builder holds global state about a build. @@ -429,8 +652,9 @@ type action struct { // cacheKey is the key for the action cache. type cacheKey struct { - mode buildMode - p *Package + mode buildMode + p *Package + shlib string } // buildMode specifies the build mode: @@ -505,6 +729,9 @@ func goFilesPackage(gofiles []string) *Package { fatalf("%s is a directory, should be a Go file", file) } dir1, _ := filepath.Split(file) + if dir1 == "" { + dir1 = "./" + } if dir == "" { dir = dir1 } else if dir != dir1 { @@ -541,11 +768,8 @@ func goFilesPackage(gofiles []string) *Package { if gobin != "" { pkg.target = filepath.Join(gobin, exe) } - } else { - if *buildO == "" { - *buildO = pkg.Name + ".a" - } } + pkg.Target = pkg.target pkg.Stale = true @@ -553,24 +777,88 @@ func goFilesPackage(gofiles []string) *Package { return pkg } +// readpkglist returns the list of packages that were built into the shared library +// at shlibpath. For the native toolchain this list is stored, newline separated, in +// an ELF note with name "Go\x00\x00" and type 1. For GCCGO it is extracted from the +// .go_export section. +func readpkglist(shlibpath string) (pkgs []*Package) { + var stk importStack + if _, gccgo := buildToolchain.(gccgoToolchain); gccgo { + f, _ := elf.Open(shlibpath) + sect := f.Section(".go_export") + data, _ := sect.Data() + scanner := bufio.NewScanner(bytes.NewBuffer(data)) + for scanner.Scan() { + t := scanner.Text() + if strings.HasPrefix(t, "pkgpath ") { + t = strings.TrimPrefix(t, "pkgpath ") + t = strings.TrimSuffix(t, ";") + pkgs = append(pkgs, loadPackage(t, &stk)) + } + } + } else { + pkglistbytes, err := readELFNote(shlibpath, "Go\x00\x00", 1) + if err != nil { + fatalf("readELFNote failed: %v", err) + } + scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes)) + for scanner.Scan() { + t := scanner.Text() + pkgs = append(pkgs, loadPackage(t, &stk)) + } + } + return +} + // action returns the action for applying the given operation (mode) to the package. // depMode is the action to use when building dependencies. +// action never looks for p in a shared library. func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action { - key := cacheKey{mode, p} + return b.action1(mode, depMode, p, false) +} + +// action1 returns the action for applying the given operation (mode) to the package. +// depMode is the action to use when building dependencies. +// action1 will look for p in a shared library if lookshared is true. +func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, lookshared bool) *action { + shlib := "" + if lookshared { + shlib = p.Shlib + } + key := cacheKey{mode, p, shlib} + a := b.actionCache[key] if a != nil { return a } + if shlib != "" { + key2 := cacheKey{modeInstall, nil, shlib} + a = b.actionCache[key2] + if a != nil { + b.actionCache[key] = a + return a + } + pkgs := readpkglist(shlib) + a = b.libaction(filepath.Base(shlib), pkgs, modeInstall, depMode) + b.actionCache[key2] = a + b.actionCache[key] = a + return a + } a = &action{p: p, pkgdir: p.build.PkgRoot} if p.pkgdir != "" { // overrides p.t a.pkgdir = p.pkgdir } - b.actionCache[key] = a for _, p1 := range p.imports { - a.deps = append(a.deps, b.action(depMode, depMode, p1)) + ls := buildLinkshared + // If p1 is part of the same shared library as p, we need the action + // that builds p here, not the shared libary or we get action loops. + if p1.Shlib == p.Shlib { + ls = false + } + a.deps = append(a.deps, b.action1(depMode, depMode, p1, ls)) } // If we are not doing a cross-build, then record the binary we'll @@ -578,18 +866,15 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action // 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 // are writing is not the cgo we need to use. - - if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace { - if reqStdPkgSrc { - if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" { - var stk importStack - p1 := loadPackage("cmd/cgo", &stk) - if p1.Error != nil { - fatalf("load cmd/cgo: %v", p1.Error) - } - a.cgo = b.action(depMode, depMode, p1) - a.deps = append(a.deps, a.cgo) + if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace && reqStdPkgSrc { + if (len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo") && !buildLinkshared && buildBuildmode != "shared" { + var stk importStack + p1 := loadPackage("cmd/cgo", &stk) + if p1.Error != nil { + fatalf("load cmd/cgo: %v", p1.Error) } + a.cgo = b.action(depMode, depMode, p1) + a.deps = append(a.deps, a.cgo) } } @@ -629,8 +914,22 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action switch mode { case modeInstall: a.f = (*builder).install - a.deps = []*action{b.action(modeBuild, depMode, p)} + a.deps = []*action{b.action1(modeBuild, depMode, p, lookshared)} a.target = a.p.target + + // Install header for cgo in c-archive and c-shared modes. + if p.usesCgo() && (buildBuildmode == "c-archive" || buildBuildmode == "c-shared") { + ah := &action{ + p: a.p, + deps: []*action{a.deps[0]}, + f: (*builder).installHeader, + pkgdir: a.pkgdir, + objdir: a.objdir, + target: a.target[:len(a.target)-len(filepath.Ext(a.target))] + ".h", + } + a.deps = append(a.deps, ah) + } + case modeBuild: a.f = (*builder).build a.target = a.objpkg @@ -645,6 +944,13 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action name := "a.out" if p.exeName != "" { name = p.exeName + } else if goos == "darwin" && buildBuildmode == "c-shared" && p.target != "" { + // On OS X, the linker output name gets recorded in the + // shared library's LC_ID_DYLIB load command. + // The code invoking the linker knows to pass only the final + // path element. Arrange that the path element matches what + // we'll install it as; otherwise the library is only loadable as "a.out". + _, name = filepath.Split(p.target) } a.target = a.objdir + filepath.Join("exe", name) + exeSuffix } @@ -653,6 +959,100 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action return a } +func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode buildMode) *action { + a := &action{} + if mode == modeBuild { + a.f = (*builder).linkShared + a.target = filepath.Join(b.work, libname) + for _, p := range pkgs { + if p.target == "" { + continue + } + a.deps = append(a.deps, b.action(depMode, depMode, p)) + } + } else if mode == modeInstall { + // Currently build mode shared forces external linking mode, and + // external linking mode forces an import of runtime/cgo. So if it + // was not passed on the command line and it is not present in + // another shared library, add it here. + seencgo := false + _, gccgo := buildToolchain.(gccgoToolchain) + if !gccgo { + for _, p := range pkgs { + seencgo = seencgo || (p.Standard && p.ImportPath == "runtime/cgo") + } + if !seencgo { + var stk importStack + p := loadPackage("runtime/cgo", &stk) + if p.Error != nil { + fatalf("load runtime/cgo: %v", p.Error) + } + computeStale(p) + // If runtime/cgo is in another shared library, then that's + // also the shared library that contains runtime, so + // something will depend on it and so runtime/cgo's staleness + // will be checked when processing that library. + if p.Shlib == "" || p.Shlib == libname { + pkgs = append([]*Package{}, pkgs...) + pkgs = append(pkgs, p) + } + } + } + + // Figure out where the library will go. + var libdir string + for _, p := range pkgs { + plibdir := p.build.PkgTargetRoot + if gccgo { + plibdir = filepath.Join(plibdir, "shlibs") + } + if libdir == "" { + libdir = plibdir + } else if libdir != plibdir { + fatalf("multiple roots %s & %s", libdir, plibdir) + } + } + a.target = filepath.Join(libdir, libname) + + // Now we can check whether we need to rebuild it. + stale := false + var built time.Time + if fi, err := os.Stat(a.target); err == nil { + built = fi.ModTime() + } + for _, p := range pkgs { + if p.target == "" { + continue + } + stale = stale || p.Stale + lstat, err := os.Stat(p.target) + if err != nil || lstat.ModTime().After(built) { + stale = true + } + a.deps = append(a.deps, b.action(depMode, depMode, p)) + } + + if stale { + a.f = (*builder).install + buildAction := b.libaction(libname, pkgs, modeBuild, depMode) + a.deps = []*action{buildAction} + for _, p := range pkgs { + if p.target == "" { + continue + } + shlibnameaction := &action{} + shlibnameaction.f = (*builder).installShlibname + shlibnameaction.target = p.target[:len(p.target)-2] + ".shlibname" + a.deps = append(a.deps, shlibnameaction) + shlibnameaction.deps = append(shlibnameaction.deps, buildAction) + } + } + } else { + fatalf("unregonized mode %v", mode) + } + return a +} + // actionList returns the list of actions in the dag rooted at root // as visited in a depth-first post-order traversal. func actionList(root *action) []*action { @@ -673,6 +1073,31 @@ func actionList(root *action) []*action { return all } +// allArchiveActions returns a list of the archive dependencies of root. +// This is needed because if package p depends on package q that is in libr.so, the +// action graph looks like p->libr.so->q and so just scanning through p's +// dependencies does not find the import dir for q. +func allArchiveActions(root *action) []*action { + seen := map[*action]bool{} + r := []*action{} + var walk func(*action) + walk = func(a *action) { + if seen[a] { + return + } + seen[a] = true + if strings.HasSuffix(a.target, ".so") || a == root { + for _, a1 := range a.deps { + walk(a1) + } + } else if strings.HasSuffix(a.target, ".a") { + r = append(r, a) + } + } + walk(root) + return r +} + // do runs the action graph rooted at root. func (b *builder) do(root *action) { // Build list of all actions, assigning depth-first post-order priority. @@ -793,9 +1218,7 @@ func hasString(strings []string, s string) bool { func (b *builder) build(a *action) (err error) { // 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 (it's possible to have packages with C files without - // using cgo, they will get compiled with the plan9 C compiler and - // linked with the rest of the package). + // and SWIG. if len(a.p.CXXFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() { return fmt.Errorf("can't build package %s because it contains C++ files (%s) but it's not using cgo nor SWIG", a.p.ImportPath, strings.Join(a.p.CXXFiles, ",")) @@ -824,7 +1247,8 @@ func (b *builder) build(a *action) (err error) { } if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && - !hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") { + (!hasString(a.p.GoFiles, "zgoos_"+buildContext.GOOS+".go") || + !hasString(a.p.GoFiles, "zgoarch_"+buildContext.GOARCH+".go")) { return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix()) } @@ -842,19 +1266,35 @@ func (b *builder) build(a *action) (err error) { } } - var gofiles, cfiles, sfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string + var gofiles, cgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string gofiles = append(gofiles, a.p.GoFiles...) + cgofiles = append(cgofiles, a.p.CgoFiles...) cfiles = append(cfiles, a.p.CFiles...) sfiles = append(sfiles, a.p.SFiles...) + cxxfiles = append(cxxfiles, a.p.CXXFiles...) if a.p.usesCgo() || a.p.usesSwig() { if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.p); err != nil { return } } + + // Run SWIG on each .swig and .swigcxx file. + // Each run will generate two files, a .go file and a .c or .cxx file. + // The .go file will use import "C" and is to be processed by cgo. + if a.p.usesSwig() { + outGo, outC, outCXX, err := b.swig(a.p, obj, pcCFLAGS) + if err != nil { + return err + } + cgofiles = append(cgofiles, outGo...) + cfiles = append(cfiles, outC...) + cxxfiles = append(cxxfiles, outCXX...) + } + // Run cgo. - if a.p.usesCgo() { + if a.p.usesCgo() || a.p.usesSwig() { // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc. // There is one exception: runtime/cgo's job is to bridge the // cgo and non-cgo worlds, so it necessarily has files in both. @@ -883,31 +1323,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, gccfiles, a.p.CXXFiles, a.p.MFiles) - if err != nil { - return err - } - cgoObjects = append(cgoObjects, outObj...) - gofiles = append(gofiles, outGo...) - } - - // Run SWIG. - if a.p.usesSwig() { - // In a package using SWIG, any .c or .s files are - // compiled with gcc. - gccfiles := append(cfiles, sfiles...) - cxxfiles, mfiles := a.p.CXXFiles, a.p.MFiles - cfiles = nil - sfiles = nil - - // Don't build c/c++ files twice if cgo is enabled (mainly for pkg-config). - if a.p.usesCgo() { - cxxfiles = nil - gccfiles = nil - mfiles = nil - } - - outGo, outObj, err := b.swig(a.p, obj, pcCFLAGS, gccfiles, cxxfiles, mfiles) + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles) if err != nil { return err } @@ -949,10 +1365,10 @@ func (b *builder) build(a *action) (err error) { } // Prepare Go import path list. - inc := b.includeArgs("-I", a.deps) + inc := b.includeArgs("-I", allArchiveActions(a)) // Compile Go. - ofile, out, err := buildToolchain.gc(b, a.p, a.objpkg, obj, inc, gofiles) + ofile, out, err := buildToolchain.gc(b, a.p, a.objpkg, obj, len(sfiles) > 0, inc, gofiles) if len(out) > 0 { b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out)) if err != nil { @@ -977,29 +1393,24 @@ func (b *builder) build(a *action) (err error) { switch { case strings.HasSuffix(name, _goos_goarch): targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext - if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644); err != nil { + if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644, true); err != nil { return err } case strings.HasSuffix(name, _goarch): targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext - if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644); err != nil { + if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644, true); err != nil { return err } case strings.HasSuffix(name, _goos): targ := file[:len(name)-len(_goos)] + "_GOOS." + ext - if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644); err != nil { + if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644, true); err != nil { return err } } } - objExt := archChar - if _, ok := buildToolchain.(gccgoToolchain); ok { - objExt = "o" - } - for _, file := range cfiles { - out := file[:len(file)-len(".c")] + "." + objExt + out := file[:len(file)-len(".c")] + ".o" if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil { return err } @@ -1008,7 +1419,7 @@ func (b *builder) build(a *action) (err error) { // Assemble .s files. for _, file := range sfiles { - out := file[:len(file)-len(".s")] + "." + objExt + out := file[:len(file)-len(".s")] + ".o" if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { return err } @@ -1018,7 +1429,7 @@ func (b *builder) build(a *action) (err error) { // 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. - // http://golang.org/issue/2601 + // https://golang.org/issue/2601 objects = append(objects, cgoObjects...) // Add system object files. @@ -1043,7 +1454,7 @@ func (b *builder) build(a *action) (err error) { // linker needs the whole dependency tree. all := actionList(a) all = all[:len(all)-1] // drop a - if err := buildToolchain.ld(b, a.p, a.target, all, a.objpkg, objects); err != nil { + if err := buildToolchain.ld(b, a, a.target, all, a.objpkg, objects); err != nil { return err } } @@ -1079,6 +1490,24 @@ func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err e return } +func (b *builder) installShlibname(a *action) error { + a1 := a.deps[0] + err := ioutil.WriteFile(a.target, []byte(filepath.Base(a1.target)+"\n"), 0644) + if err != nil { + return err + } + if buildX { + b.showcmd("", "echo '%s' > %s # internal", filepath.Base(a1.target), a.target) + } + return nil +} + +func (b *builder) linkShared(a *action) (err error) { + allactions := actionList(a) + allactions = allactions[:len(allactions)-1] + return buildToolchain.ldShared(b, a.deps, a.target, allactions) +} + // install is the action for installing a single package or executable. func (b *builder) install(a *action) (err error) { defer func() { @@ -1089,7 +1518,11 @@ func (b *builder) install(a *action) (err error) { a1 := a.deps[0] perm := os.FileMode(0644) if a1.link { - perm = 0755 + switch buildBuildmode { + case "c-archive", "c-shared": + default: + perm = 0755 + } } // make target directory @@ -1109,7 +1542,7 @@ func (b *builder) install(a *action) (err error) { defer os.Remove(a1.target) } - return b.moveOrCopyFile(a, a.target, a1.target, perm) + return b.moveOrCopyFile(a, a.target, a1.target, perm, false) } // includeArgs returns the -I or -L directory list for access @@ -1126,6 +1559,9 @@ func (b *builder) includeArgs(flag string, all []*action) []string { // This is the $WORK/my/package/_test directory for the // package being built, so there are few of these. for _, a1 := range all { + if a1.p == nil { + continue + } if dir := a1.pkgdir; dir != a1.p.build.PkgRoot && !incMap[dir] { incMap[dir] = true inc = append(inc, flag, dir) @@ -1138,17 +1574,12 @@ func (b *builder) includeArgs(flag string, all []*action) []string { // Finally, look in the installed package directories for each action. for _, a1 := range all { + if a1.p == nil { + continue + } if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] { incMap[dir] = true - if _, ok := buildToolchain.(gccgoToolchain); ok { - dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch) - } else { - dir = filepath.Join(dir, goos+"_"+goarch) - if buildContext.InstallSuffix != "" { - dir += "_" + buildContext.InstallSuffix - } - } - inc = append(inc, flag, dir) + inc = append(inc, flag, a1.p.build.PkgTargetRoot) } } @@ -1156,7 +1587,7 @@ func (b *builder) includeArgs(flag string, all []*action) []string { } // moveOrCopyFile is like 'mv src dst' or 'cp src dst'. -func (b *builder) moveOrCopyFile(a *action, dst, src string, perm os.FileMode) error { +func (b *builder) moveOrCopyFile(a *action, dst, src string, perm os.FileMode, force bool) error { if buildN { b.showcmd("", "mv %s %s", src, dst) return nil @@ -1173,11 +1604,11 @@ func (b *builder) moveOrCopyFile(a *action, dst, src string, perm os.FileMode) e } } - return b.copyFile(a, dst, src, perm) + return b.copyFile(a, dst, src, perm, force) } // copyFile is like 'cp src dst'. -func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { +func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode, force bool) error { if buildN || buildX { b.showcmd("", "cp %s %s", src, dst) if buildN { @@ -1198,7 +1629,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { if fi.IsDir() { return fmt.Errorf("build output %q already exists and is a directory", dst) } - if !isObject(dst) { + if !force && !isObject(dst) { return fmt.Errorf("build output %q already exists and is not an object file", dst) } } @@ -1235,10 +1666,30 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { return nil } +// Install the cgo export header file, if there is one. +func (b *builder) installHeader(a *action) error { + src := a.objdir + "_cgo_install.h" + if _, err := os.Stat(src); os.IsNotExist(err) { + // If the file does not exist, there are no exported + // functions, and we do not install anything. + return nil + } + + dir, _ := filepath.Split(a.target) + if dir != "" { + if err := b.mkdir(dir); err != nil { + return err + } + } + + return b.moveOrCopyFile(a, a.target, src, 0644, true) +} + // cover runs, in effect, // go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go func (b *builder) cover(a *action, dst, src string, perm os.FileMode, varName string) error { return b.run(a.objdir, "cover "+a.p.ImportPath, nil, + buildToolExec, tool("cover"), "-mode", a.p.coverMode, "-var", varName, @@ -1247,15 +1698,15 @@ func (b *builder) cover(a *action, dst, src string, perm os.FileMode, varName st } var objectMagic = [][]byte{ - {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive - {'\x7F', 'E', 'L', 'F'}, // ELF - {0xFE, 0xED, 0xFA, 0xCE}, // Mach-O big-endian 32-bit - {0xFE, 0xED, 0xFA, 0xCF}, // Mach-O big-endian 64-bit - {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit - {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit - {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00}, // PE (Windows) as generated by 6l/8l - {0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386 - {0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64 + {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive + {'\x7F', 'E', 'L', 'F'}, // ELF + {0xFE, 0xED, 0xFA, 0xCE}, // Mach-O big-endian 32-bit + {0xFE, 0xED, 0xFA, 0xCF}, // Mach-O big-endian 64-bit + {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit + {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit + {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00}, // PE (Windows) as generated by 6l/8l and gcc + {0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386 + {0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64 } func isObject(s string) bool { @@ -1436,7 +1887,7 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter cmd.Stdout = &buf cmd.Stderr = &buf cmd.Dir = dir - cmd.Env = mergeEnvLists(env, envForDir(cmd.Dir)) + cmd.Env = mergeEnvLists(env, envForDir(cmd.Dir, os.Environ())) err := cmd.Run() // cmd.Run will fail on Unix if some other process has the binary @@ -1478,7 +1929,7 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter // Sleeping when we observe the race seems to be the most reliable // option we have. // - // http://golang.org/issue/3001 + // https://golang.org/issue/3001 // if err != nil && nbusy < 3 && strings.Contains(err.Error(), "text file busy") { time.Sleep(100 * time.Millisecond << uint(nbusy)) @@ -1561,7 +2012,7 @@ 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, importArgs []string, gofiles []string) (ofile string, out []byte, err error) + 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 @@ -1574,8 +2025,10 @@ type toolchain interface { // an archive from a set of object files. // typically it is run in the object directory. pack(b *builder, p *Package, objDir, afile string, ofiles []string) error - // ld runs the linker to create a package starting at mainpkg. - ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error + // ld runs the linker to create an executable starting at mainpkg. + ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error + // ldShared runs the linker to create a shared library containing the pkgs built by toplevelactions + ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error compiler() string linker() string @@ -1598,7 +2051,7 @@ func (noToolchain) linker() string { return "" } -func (noToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) { +func (noToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, out []byte, err error) { return "", nil, noCompiler() } @@ -1615,7 +2068,11 @@ func (noToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s return noCompiler() } -func (noToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (noToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { + return noCompiler() +} + +func (noToolchain) ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error { return noCompiler() } @@ -1627,24 +2084,27 @@ func (noToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error type gcToolchain struct{} func (gcToolchain) compiler() string { - return tool(archChar + "g") + return tool("compile") } func (gcToolchain) linker() string { - return tool(archChar + "l") + return tool("link") } -func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { +func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { if archive != "" { ofile = archive } else { - out := "_go_." + archChar + out := "_go_.o" ofile = obj + out } gcargs := []string{"-p", p.ImportPath} + if p.Name == "main" { + gcargs[1] = "main" + } if p.Standard && p.ImportPath == "runtime" { - // runtime compiles with a special 6g flag to emit + // runtime compiles with a special gc flag to emit // additional reflect type data. gcargs = append(gcargs, "-+") } @@ -1666,24 +2126,76 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs [] if buildContext.InstallSuffix != "" { gcargs = append(gcargs, "-installsuffix", buildContext.InstallSuffix) } + if p.buildID != "" { + gcargs = append(gcargs, "-buildid", p.buildID) + } - args := stringList(tool(archChar+"g"), "-o", ofile, "-trimpath", b.work, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) + 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) + } + } + + 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") } + if asmhdr { + args = append(args, "-asmhdr", obj+"go_asm.h") + } for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) } - output, err = b.runOut(p.Dir, p.ImportPath, nil, args) + output, err = b.runOut(p.Dir, p.ImportPath, nil, args...) return ofile, output, err } func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. - inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) + inc := filepath.Join(goroot, "pkg", "include") sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-I", inc, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, 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 + } + return nil +} + +// toolVerify checks that the command line args writes the same output file +// if run using newTool instead. +// Unused now but kept around for future use. +func toolVerify(b *builder, p *Package, newTool string, ofile string, args []interface{}) error { + newArgs := make([]interface{}, len(args)) + copy(newArgs, args) + newArgs[1] = tool(newTool) + newArgs[3] = ofile + ".new" // x.6 becomes x.6.new + if err := b.run(p.Dir, p.ImportPath, nil, newArgs...); err != nil { + return err + } + data1, err := ioutil.ReadFile(ofile) + if err != nil { + return err + } + data2, err := ioutil.ReadFile(ofile + ".new") + if err != nil { + return err + } + if !bytes.Equal(data1, data2) { + return fmt.Errorf("%s and %s produced different output files:\n%s\n%s", filepath.Base(args[1].(string)), newTool, strings.Join(stringList(args...), " "), strings.Join(stringList(newArgs...), " ")) + } + os.Remove(ofile + ".new") + return nil } func (gcToolchain) pkgpath(basedir string, p *Package) string { @@ -1722,7 +2234,7 @@ func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s // Need actual pack. cmdline[0] = tool("pack") - return b.run(p.Dir, p.ImportPath, nil, cmdline) + return b.run(p.Dir, p.ImportPath, nil, buildToolExec, cmdline) } func packInternal(b *builder, afile string, ofiles []string) error { @@ -1775,21 +2287,49 @@ func packInternal(b *builder, afile string, ofiles []string) error { return dst.Close() } -func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +// setextld sets the appropriate linker flags for the specified compiler. +func setextld(ldflags []string, compiler []string) []string { + for _, f := range ldflags { + if f == "-extld" || strings.HasPrefix(f, "-extld=") { + // don't override -extld if supplied + return ldflags + } + } + ldflags = append(ldflags, "-extld="+compiler[0]) + if len(compiler) > 1 { + extldflags := false + add := strings.Join(compiler[1:], " ") + for i, f := range ldflags { + if f == "-extldflags" && i+1 < len(ldflags) { + ldflags[i+1] = add + " " + ldflags[i+1] + extldflags = true + break + } else if strings.HasPrefix(f, "-extldflags=") { + ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] + extldflags = true + break + } + } + if !extldflags { + ldflags = append(ldflags, "-extldflags="+add) + } + } + return ldflags +} + +func (gcToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { importArgs := b.includeArgs("-L", allactions) - cxx := len(p.CXXFiles) > 0 + cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 for _, a := range allactions { - if a.p != nil && len(a.p.CXXFiles) > 0 { + if a.p != nil && (len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0) { cxx = true } } - ldflags := buildLdflags - // Limit slice capacity so that concurrent appends do not race on the shared array. - ldflags = ldflags[:len(ldflags):len(ldflags)] + var ldflags []string if buildContext.InstallSuffix != "" { ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix) } - if p.omitDWARF { + if root.p.omitDWARF { ldflags = append(ldflags, "-w") } @@ -1797,56 +2337,67 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, // appropriate linker. In case of C++ code, use the compiler named // by the CXX environment variable or defaultCXX if CXX is not set. // Else, use the CC environment variable and defaultCC as fallback. - extld := false - for _, f := range ldflags { - if f == "-extld" || strings.HasPrefix(f, "-extld=") { - extld = true - break + var compiler []string + if cxx { + compiler = envList("CXX", defaultCXX) + } else { + compiler = envList("CC", defaultCC) + } + ldflags = setextld(ldflags, compiler) + ldflags = append(ldflags, "-buildmode="+ldBuildmode) + if root.p.buildID != "" { + ldflags = append(ldflags, "-buildid="+root.p.buildID) + } + ldflags = append(ldflags, buildLdflags...) + + // On OS X when using external linking to build a shared library, + // the argument passed here to -o ends up recorded in the final + // shared library in the LC_ID_DYLIB load command. + // To avoid putting the temporary output directory name there + // (and making the resulting shared library useless), + // run the link in the output directory so that -o can name + // just the final path element. + dir := "." + if goos == "darwin" && buildBuildmode == "c-shared" { + dir, out = filepath.Split(out) + } + + return b.run(dir, root.p.ImportPath, nil, buildToolExec, tool("link"), "-o", out, importArgs, ldflags, mainpkg) +} + +func (gcToolchain) ldShared(b *builder, toplevelactions []*action, out string, allactions []*action) error { + importArgs := b.includeArgs("-L", allactions) + ldflags := []string{"-installsuffix", buildContext.InstallSuffix} + ldflags = append(ldflags, "-buildmode=shared") + ldflags = append(ldflags, buildLdflags...) + cxx := false + for _, a := range allactions { + if a.p != nil && (len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0) { + cxx = true } } - if !extld { - var compiler []string - if cxx { - compiler = envList("CXX", defaultCXX) - } else { - compiler = envList("CC", defaultCC) - } - ldflags = append(ldflags, "-extld="+compiler[0]) - if len(compiler) > 1 { - extldflags := false - add := strings.Join(compiler[1:], " ") - for i, f := range ldflags { - if f == "-extldflags" && i+1 < len(ldflags) { - ldflags[i+1] = add + " " + ldflags[i+1] - extldflags = true - break - } else if strings.HasPrefix(f, "-extldflags=") { - ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] - extldflags = true - break - } - } - if !extldflags { - ldflags = append(ldflags, "-extldflags="+add) - } + // If the user has not specified the -extld option, then specify the + // appropriate linker. In case of C++ code, use the compiler named + // by the CXX environment variable or defaultCXX if CXX is not set. + // Else, use the CC environment variable and defaultCC as fallback. + var compiler []string + if cxx { + compiler = envList("CXX", defaultCXX) + } else { + compiler = envList("CC", defaultCC) + } + ldflags = setextld(ldflags, compiler) + for _, d := range toplevelactions { + if !strings.HasSuffix(d.target, ".a") { // omit unsafe etc and actions for other shared libraries + continue } + ldflags = append(ldflags, d.p.ImportPath+"="+d.target) } - return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, ldflags, mainpkg) + return b.run(".", out, nil, buildToolExec, tool("link"), "-o", out, importArgs, ldflags) } func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { - inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) - cfile = mkAbs(p.Dir, cfile) - warn := []string{"-w"} - if p.usesSwig() { - // When using SWIG, this compiler is only used to - // compile the C files generated by SWIG. - // We don't want warnings. - // See issue 9065 for details. - warn = nil - } - args := stringList(tool(archChar+"c"), "-F", "-V", warn, "-trimpath", b.work, "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile) - return b.run(p.Dir, p.ImportPath, nil, args) + return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(p.Dir, cfile)) } // The Gccgo toolchain. @@ -1870,7 +2421,7 @@ func (gccgoToolchain) linker() string { return gccgoBin } -func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { +func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { out := "_go_.o" ofile = obj + out gcargs := []string{"-g"} @@ -1896,8 +2447,8 @@ func (tools gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) } + defs = tools.maybePIC(defs) defs = append(defs, b.gccArchArgs()...) - return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-c", "-I", obj, "-o", ofile, defs, sfile) } @@ -1916,30 +2467,46 @@ func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles return b.run(p.Dir, p.ImportPath, nil, "ar", "cru", mkAbs(objDir, afile), absOfiles) } -func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error { // gccgo needs explicit linking with all package dependencies, // and all LDFLAGS from cgo dependencies. apackagesSeen := make(map[*Package]bool) afiles := []string{} + shlibs := []string{} xfiles := []string{} ldflags := b.gccArchArgs() cgoldflags := []string{} usesCgo := false - cxx := len(p.CXXFiles) > 0 - objc := len(p.MFiles) > 0 - - // Prefer the output of an install action to the output of a build action, - // because the install action will delete the output of the build action. - // Iterate over the list backward (reverse dependency order) so that we - // always see the install before the build. - for i := len(allactions) - 1; i >= 0; i-- { - a := allactions[i] - if !a.p.Standard { - if a.p != nil && !apackagesSeen[a.p] { + cxx := len(root.p.CXXFiles) > 0 || len(root.p.SwigCXXFiles) > 0 + objc := len(root.p.MFiles) > 0 + + actionsSeen := make(map[*action]bool) + // Make a pre-order depth-first traversal of the action graph, taking note of + // whether a shared library action has been seen on the way to an action (the + // construction of the graph means that if any path to a node passes through + // a shared library action, they all do). + var walk func(a *action, seenShlib bool) + walk = func(a *action, seenShlib bool) { + if actionsSeen[a] { + return + } + actionsSeen[a] = true + if a.p != nil && !seenShlib { + if a.p.Standard { + return + } + // We record the target of the first time we see a .a file + // for a package to make sure that we prefer the 'install' + // rather than the 'build' location (which may not exist any + // more). We still need to traverse the dependencies of the + // build action though so saying + // if apackagesSeen[a.p] { return } + // doesn't work. + if !apackagesSeen[a.p] { apackagesSeen[a.p] = true if a.p.fake && a.p.external { // external _tests, if present must come before - // internal _tests. Store these on a seperate list + // internal _tests. Store these on a separate list // and place them at the head after this loop. xfiles = append(xfiles, a.target) } else if a.p.fake { @@ -1950,54 +2517,174 @@ func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions [] } } } + if strings.HasSuffix(a.target, ".so") { + shlibs = append(shlibs, a.target) + seenShlib = true + } + for _, a1 := range a.deps { + walk(a1, seenShlib) + } + } + for _, a1 := range root.deps { + walk(a1, false) } afiles = append(xfiles, afiles...) for _, a := range allactions { - if a.p != nil { + // Gather CgoLDFLAGS, but not from standard packages. + // The go tool can dig up runtime/cgo from GOROOT and + // think that it should use its CgoLDFLAGS, but gccgo + // doesn't use runtime/cgo. + if a.p == nil { + continue + } + if !a.p.Standard { cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) - if len(a.p.CgoFiles) > 0 { - usesCgo = true - } - if a.p.usesSwig() { - usesCgo = true - } - if len(a.p.CXXFiles) > 0 { - cxx = true - } - if len(a.p.MFiles) > 0 { - objc = true - } + } + if len(a.p.CgoFiles) > 0 { + usesCgo = true + } + if a.p.usesSwig() { + usesCgo = true + } + if len(a.p.CXXFiles) > 0 || len(a.p.SwigCXXFiles) > 0 { + cxx = true + } + if len(a.p.MFiles) > 0 { + objc = true } } + + ldflags = append(ldflags, "-Wl,--whole-archive") ldflags = append(ldflags, afiles...) + ldflags = append(ldflags, "-Wl,--no-whole-archive") + ldflags = append(ldflags, cgoldflags...) ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) - ldflags = append(ldflags, p.CgoLDFLAGS...) - if usesCgo && goos == "linux" { - ldflags = append(ldflags, "-Wl,-E") + ldflags = append(ldflags, root.p.CgoLDFLAGS...) + + ldflags = stringList("-Wl,-(", ldflags, "-Wl,-)") + + for _, shlib := range shlibs { + ldflags = append( + ldflags, + "-L"+filepath.Dir(shlib), + "-Wl,-rpath="+filepath.Dir(shlib), + "-l"+strings.TrimSuffix( + strings.TrimPrefix(filepath.Base(shlib), "lib"), + ".so")) } - if cxx { - ldflags = append(ldflags, "-lstdc++") + + var realOut string + switch ldBuildmode { + case "exe": + if usesCgo && goos == "linux" { + ldflags = append(ldflags, "-Wl,-E") + } + + case "c-archive": + // Link the Go files into a single .o, and also link + // in -lgolibbegin. + // + // We need to use --whole-archive with -lgolibbegin + // because it doesn't define any symbols that will + // cause the contents to be pulled in; it's just + // initialization code. + // + // The user remains responsible for linking against + // -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 + // libffi. + ldflags = append(ldflags, "-Wl,-r", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive") + + // We are creating an object file, so we don't want a build ID. + ldflags = b.disableBuildID(ldflags) + + realOut = out + out = out + ".o" + + case "c-shared": + ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc") + + default: + fatalf("-buildmode=%s not supported for gccgo", ldBuildmode) + } + + switch ldBuildmode { + case "exe", "c-shared": + if cxx { + ldflags = append(ldflags, "-lstdc++") + } + if objc { + ldflags = append(ldflags, "-lobjc") + } + } + + if err := b.run(".", root.p.ImportPath, nil, tools.linker(), "-o", out, ofiles, ldflags, buildGccgoflags); err != nil { + return err + } + + switch ldBuildmode { + case "c-archive": + if err := b.run(".", root.p.ImportPath, nil, "ar", "rc", realOut, out); err != nil { + return err + } + } + return nil +} + +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) + } } - if objc { - ldflags = append(ldflags, "-lobjc") + 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(".", p.ImportPath, nil, tools.linker(), "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) + return b.run(".", out, nil, tools.linker(), args, buildGccgoflags) } -func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { - inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) +func (tools gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { + inc := filepath.Join(goroot, "pkg", "include") cfile = mkAbs(p.Dir, cfile) defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} defs = append(defs, b.gccArchArgs()...) if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) } + switch goarch { + case "386", "amd64": + defs = append(defs, "-fsplit-stack") + } + defs = tools.maybePIC(defs) return b.run(p.Dir, p.ImportPath, nil, envList("CC", defaultCC), "-Wall", "-g", "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) } +// maybePIC adds -fPIC to the list of arguments if needed. +func (tools gccgoToolchain) maybePIC(args []string) []string { + switch buildBuildmode { + case "c-shared", "shared": + args = append(args, "-fPIC") + } + return args +} + func gccgoPkgpath(p *Package) string { if p.build.IsCommand() && !p.forceLibrary { return "" @@ -2073,7 +2760,7 @@ func (b *builder) ccompile(p *Package, out string, flags []string, file string, // gccld runs the gcc linker to create an executable from a set of object files. func (b *builder) gccld(p *Package, out string, flags []string, obj []string) error { var cmd []string - if len(p.CXXFiles) > 0 { + if len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 { cmd = b.gxxCmd(p.Dir) } else { cmd = b.gccCmd(p.Dir) @@ -2132,7 +2819,7 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // 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 http://golang.org/issue/3253. + // See https://golang.org/issue/3253. if goos == "darwin" { a = append(a, "-fno-common") } @@ -2142,12 +2829,12 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string { // gccArchArgs returns arguments to pass to gcc based on the architecture. func (b *builder) gccArchArgs() []string { - switch archChar { - case "8": + switch goarch { + case "386": return []string{"-m32"} - case "6": + case "amd64", "amd64p32": return []string{"-m64"} - case "5": + case "arm": return []string{"-marm"} // not thumb } return nil @@ -2185,7 +2872,7 @@ var ( cgoLibGccFileOnce sync.Once ) -func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { +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) cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) @@ -2202,7 +2889,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi // TODO: CGOPKGPATH, CGO_FLAGS? gofiles := []string{obj + "_cgo_gotypes.go"} cfiles := []string{"_cgo_main.c", "_cgo_export.c"} - for _, fn := range p.CgoFiles { + for _, fn := range cgofiles { f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_") gofiles = append(gofiles, obj+f+"cgo1.go") cfiles = append(cfiles, f+"cgo2.c") @@ -2212,8 +2899,6 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi cgoflags := []string{} // TODO: make cgo not depend on $GOARCH? - objExt := archChar - if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } @@ -2232,23 +2917,38 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi } if _, ok := buildToolchain.(gccgoToolchain); ok { + switch goarch { + case "386", "amd64": + cgoCFLAGS = append(cgoCFLAGS, "-fsplit-stack") + } cgoflags = append(cgoflags, "-gccgo") if pkgpath := gccgoPkgpath(p); pkgpath != "" { cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath) } - objExt = "o" } - if err := b.run(p.Dir, p.ImportPath, cgoenv, cgoExe, "-objdir", obj, cgoflags, "--", cgoCPPFLAGS, cgoexeCFLAGS, p.CgoFiles); err != nil { + + switch buildBuildmode { + case "c-archive", "c-shared": + // Tell cgo that if there are any exported functions + // it should generate a header file that C code can + // #include. + 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 { return nil, nil, err } outGo = append(outGo, gofiles...) // cc _cgo_defun.c - defunObj := obj + "_cgo_defun." + objExt - if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil { - return nil, nil, err + _, 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) } - outObj = append(outObj, defunObj) // gcc var linkobj []string @@ -2362,20 +3062,15 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi } // cgo -dynimport - importC := obj + "_cgo_import.c" + importGo := obj + "_cgo_import.go" cgoflags = []string{} if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-dynlinker") // record path to dynamic linker } - if err := b.run(p.Dir, p.ImportPath, nil, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC, cgoflags); err != nil { - return nil, nil, err - } - - // cc _cgo_import.ARCH - importObj := obj + "_cgo_import." + objExt - if err := buildToolchain.cc(b, p, obj, importObj, importC); err != nil { + 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) ofile := obj + "_all.o" var gccObjs, nonGccObjs []string @@ -2388,19 +3083,8 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi } ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs) - // Some systems, such as Ubuntu, always add --build-id to - // every link, but we don't want a build ID since we are - // 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 do, but only on - // systems likely to support it, which is to say, systems that - // normally use gold or the GNU linker. - switch goos { - case "android", "dragonfly", "linux", "netbsd": - ldflags = append(ldflags, "-Wl,--build-id=none") - } + // 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 @@ -2408,8 +3092,8 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows // must be processed before the gcc-generated objects. - // Put it first. http://golang.org/issue/2601 - outObj = stringList(importObj, nonGccObjs, ofile) + // Put it first. https://golang.org/issue/2601 + outObj = stringList(nonGccObjs, ofile) return outGo, outObj, nil } @@ -2417,77 +3101,41 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi // Run SWIG on all SWIG input files. // TODO: Don't build a shared library, once SWIG emits the necessary // pragmas for external linking. -func (b *builder) swig(p *Package, obj string, pcCFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { - cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) - cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) - cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS) - - for _, file := range gccfiles { - ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o" - if err := b.gcc(p, ofile, cflags, file); err != nil { - return nil, nil, err - } - outObj = append(outObj, ofile) - } - - 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" - if err := b.gxx(p, ofile, cxxflags, file); err != nil { - return nil, nil, err - } - outObj = append(outObj, ofile) - } - - for _, file := range mfiles { - // Append .o to the file, just in case the pkg has file.c and file.cpp - ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o" - if err := b.gcc(p, ofile, cflags, file); err != nil { - return nil, nil, err - } - outObj = append(outObj, ofile) - } - +func (b *builder) swig(p *Package, obj string, pcCFLAGS []string) (outGo, outC, outCXX []string, err error) { if err := b.swigVersionCheck(); err != nil { - return nil, nil, err + return nil, nil, nil, err } intgosize, err := b.swigIntSize(obj) if err != nil { - return nil, nil, err + return nil, nil, nil, err } for _, f := range p.SwigFiles { - goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize) + goFile, cFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if goFile != "" { outGo = append(outGo, goFile) } - if objFile != "" { - outObj = append(outObj, objFile) - } - if gccObjFile != "" { - outObj = append(outObj, gccObjFile) + if cFile != "" { + outC = append(outC, cFile) } } for _, f := range p.SwigCXXFiles { - goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize) + goFile, cxxFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if goFile != "" { outGo = append(outGo, goFile) } - if objFile != "" { - outObj = append(outObj, objFile) - } - if gccObjFile != "" { - outObj = append(outObj, gccObjFile) + if cxxFile != "" { + outCXX = append(outCXX, cxxFile) } } - return outGo, outObj, nil + return outGo, outC, outCXX, nil } // Make sure SWIG is new enough. @@ -2501,20 +3149,51 @@ func (b *builder) swigDoVersionCheck() error { if err != nil { return err } - re := regexp.MustCompile(`[vV]ersion +([\d])`) + re := regexp.MustCompile(`[vV]ersion +([\d]+)([.][\d]+)?([.][\d]+)?`) matches := re.FindSubmatch(out) if matches == nil { // Can't find version number; hope for the best. return nil } + major, err := strconv.Atoi(string(matches[1])) if err != nil { // Can't find version number; hope for the best. return nil } + const errmsg = "must have SWIG version >= 3.0.6" if major < 3 { - return errors.New("must have SWIG version >= 3.0") + return errors.New(errmsg) + } + if major > 3 { + // 4.0 or later + return nil + } + + // We have SWIG version 3.x. + if len(matches[2]) > 0 { + minor, err := strconv.Atoi(string(matches[2][1:])) + if err != nil { + return nil + } + if minor > 0 { + // 3.1 or later + return nil + } } + + // We have SWIG version 3.0.x. + if len(matches[3]) > 0 { + patch, err := strconv.Atoi(string(matches[3][1:])) + if err != nil { + return nil + } + if patch < 6 { + // Before 3.0.6. + return errors.New(errmsg) + } + } + return nil } @@ -2545,14 +3224,14 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) { p := goFilesPackage(srcs) - if _, _, e := buildToolchain.gc(b, p, "", obj, nil, srcs); e != nil { + if _, _, e := buildToolchain.gc(b, p, "", obj, false, nil, srcs); e != nil { return "32", nil } return "64", nil } // Run SWIG on one SWIG input file. -func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) { +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) var cflags []string if cxx { @@ -2567,7 +3246,6 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b } base := file[:len(file)-n] goFile := base + ".go" - cBase := base + "_gc." gccBase := base + "_wrap." gccExt := "c" if cxx { @@ -2579,6 +3257,7 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b // swig args := []string{ "-go", + "-cgo", "-intgosize", intgosize, "-module", base, "-o", obj + gccBase + gccExt, @@ -2601,39 +3280,40 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b args = append(args, "-c++") } - if out, err := b.runOut(p.Dir, p.ImportPath, nil, "swig", args, file); err != nil { + out, err := b.runOut(p.Dir, p.ImportPath, nil, "swig", args, file) + if err != nil { if len(out) > 0 { - if bytes.Contains(out, []byte("Unrecognized option -intgosize")) { - return "", "", "", errors.New("must have SWIG version >= 3.0") + if bytes.Contains(out, []byte("-intgosize")) || bytes.Contains(out, []byte("-cgo")) { + return "", "", errors.New("must have SWIG version >= 3.0.6") } - b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) - return "", "", "", errPrintedOutput + b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) // swig error + return "", "", errPrintedOutput } - return "", "", "", err + return "", "", err } - - var cObj string - if !gccgo { - // cc - cObj = obj + cBase + archChar - if err := buildToolchain.cc(b, p, obj, cObj, obj+cBase+"c"); err != nil { - return "", "", "", err - } + if len(out) > 0 { + b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) // swig warning } - // gcc - gccObj := obj + gccBase + "o" - if !cxx { - if err := b.gcc(p, gccObj, cflags, obj+gccBase+gccExt); err != nil { - return "", "", "", err - } - } else { - if err := b.gxx(p, gccObj, cflags, obj+gccBase+gccExt); err != nil { - return "", "", "", err - } - } + return obj + goFile, obj + gccBase + gccExt, nil +} - return obj + goFile, cObj, gccObj, 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 +// --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 +// -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 +// 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 { + switch goos { + case "android", "dragonfly", "linux", "netbsd": + ldflags = append(ldflags, "-Wl,--build-id=none") + } + return ldflags } // An actionQueue is a priority queue of actions. @@ -2669,7 +3349,6 @@ func raceInit() { } buildGcflags = append(buildGcflags, "-race") buildLdflags = append(buildLdflags, "-race") - buildCcflags = append(buildCcflags, "-D", "RACE") if buildContext.InstallSuffix != "" { buildContext.InstallSuffix += "_" } |

