diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-01-06 23:26:02 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-01-06 23:26:02 +0000 |
commit | 0e7627001152cf7a47310c5a99ea854236671508 (patch) | |
tree | 40ecf33efd29feac369ae050140bc3fefed352e9 /libgo/go/cmd/go/build.go | |
parent | 33dba2339488931a8630a2414be1d9d4604370e8 (diff) | |
download | gcc-0e7627001152cf7a47310c5a99ea854236671508.tar.gz |
libgo: Add sources for go, cgo, and gofmt commands.
The new commands are not yet built. That will be done
separately.
Also include a few changes to go/build to support them.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@219272 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/cmd/go/build.go')
-rw-r--r-- | libgo/go/cmd/go/build.go | 2594 |
1 files changed, 2594 insertions, 0 deletions
diff --git a/libgo/go/cmd/go/build.go b/libgo/go/cmd/go/build.go new file mode 100644 index 00000000000..c9957930a12 --- /dev/null +++ b/libgo/go/cmd/go/build.go @@ -0,0 +1,2594 @@ +// 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. + +package main + +import ( + "bufio" + "bytes" + "container/heap" + "errors" + "flag" + "fmt" + "go/build" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "time" +) + +var cmdBuild = &Command{ + UsageLine: "build [-o output] [-i] [build flags] [packages]", + Short: "compile packages and dependencies", + Long: ` +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. + +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, +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 -i flag installs the packages that are dependencies of the target. + +The build flags are shared by the build, clean, get, install, list, run, +and test commands: + + -a + force rebuilding of packages that are already up-to-date. + -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. + -race + enable data race detection. + Supported only on linux/amd64, darwin/amd64 and windows/amd64. + -v + print the names of packages as they are compiled. + -work + print the name of the temporary work directory and + do not delete it when exiting. + -x + print the commands. + + -ccflags 'arg list' + arguments to pass on each 5c, 6c, or 8c compiler invocation. + -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. + -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. + -ldflags 'flag list' + arguments to pass on each 5l, 6l, or 8l linker invocation. + -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. + +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'. + +See also: go install, go get, go clean. + `, +} + +func init() { + // break init cycle + cmdBuild.Run = runBuild + cmdInstall.Run = runInstall + + cmdBuild.Flag.BoolVar(&buildI, "i", false, "") + + addBuildFlags(cmdBuild) + addBuildFlags(cmdInstall) +} + +// Flags set by multiple commands. +var buildA bool // -a flag +var buildN bool // -n flag +var buildP = runtime.NumCPU() // -p flag +var buildV bool // -v flag +var buildX bool // -x flag +var buildI bool // -i flag +var buildO = cmdBuild.Flag.String("o", "", "output file") +var buildWork bool // -work 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 reqPkgSrc bool // req src for Imports +var buildContext = build.Default +var buildToolchain toolchain = noToolchain{} + +// buildCompiler implements flag.Var. +// It implements Set by updating both +// buildToolchain and buildContext.Compiler. +type buildCompiler struct{} + +func (c buildCompiler) Set(value string) error { + switch value { + case "gc": + buildToolchain = gcToolchain{} + case "gccgo": + buildToolchain = gccgoToolchain{} + default: + return fmt.Errorf("unknown compiler %q", value) + } + buildContext.Compiler = value + return nil +} + +func (c buildCompiler) String() string { + return buildContext.Compiler +} + +func init() { + switch build.Default.Compiler { + case "gc": + buildToolchain = gcToolchain{} + case "gccgo": + buildToolchain = gccgoToolchain{} + } +} + +// 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)(&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.BoolVar(&buildRace, "race", false, "") + switch build.Default.Compiler { + case "gc": + reqPkgSrc = true + case "gccgo": + reqPkgSrc = false + } +} + +func addBuildFlagsNX(cmd *Command) { + cmd.Flag.BoolVar(&buildN, "n", false, "") + cmd.Flag.BoolVar(&buildX, "x", false, "") +} + +func isSpaceByte(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} + +// fileExtSplit expects a filename and returns the name +// and ext (without the dot). If the file has no +// extension, ext will be empty. +func fileExtSplit(file string) (name, ext string) { + dotExt := filepath.Ext(file) + name = file[:len(file)-len(dotExt)] + if dotExt != "" { + ext = dotExt[1:] + } + return +} + +type stringsFlag []string + +func (v *stringsFlag) Set(s string) error { + var err error + *v, err = splitQuotedFields(s) + if *v == nil { + *v = []string{} + } + return err +} + +func splitQuotedFields(s string) ([]string, error) { + // Split fields allowing '' or "" around elements. + // Quotes further inside the string do not count. + var f []string + for len(s) > 0 { + for len(s) > 0 && isSpaceByte(s[0]) { + s = s[1:] + } + if len(s) == 0 { + break + } + // Accepted quoted string. No unescaping inside. + if s[0] == '"' || s[0] == '\'' { + quote := s[0] + s = s[1:] + i := 0 + for i < len(s) && s[i] != quote { + i++ + } + if i >= len(s) { + return nil, fmt.Errorf("unterminated %c string", quote) + } + f = append(f, s[:i]) + s = s[i+1:] + continue + } + i := 0 + for i < len(s) && !isSpaceByte(s[i]) { + i++ + } + f = append(f, s[:i]) + s = s[i:] + } + return f, nil +} + +func (v *stringsFlag) String() string { + return "<stringsFlag>" +} + +func runBuild(cmd *Command, args []string) { + raceInit() + var b builder + b.init() + + pkgs := packagesForBuild(args) + + if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" { + _, *buildO = path.Split(pkgs[0].ImportPath) + *buildO += exeSuffix + } + + // sanity check some often mis-used options + switch buildContext.Compiler { + case "gccgo": + if len(buildGcflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass compiler flags using -gccgoflags, not -gcflags") + } + if len(buildLdflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass linker flags using -gccgoflags, not -ldflags") + } + case "gc": + if len(buildGccgoflags) != 0 { + fmt.Println("go build: when using gc toolchain, please pass compile flags using -gcflags, and linker flags using -ldflags") + } + } + + if *buildO != "" { + if len(pkgs) > 1 { + fatalf("go build: cannot use -o with multiple packages") + } + p := pkgs[0] + p.target = "" // must build - not up to date + a := b.action(modeInstall, modeBuild, p) + a.target = *buildO + b.do(a) + return + } + + a := &action{} + depMode := modeBuild + if buildI { + depMode = modeInstall + } + for _, p := range packages(args) { + a.deps = append(a.deps, b.action(modeBuild, depMode, p)) + } + b.do(a) +} + +var cmdInstall = &Command{ + UsageLine: "install [build flags] [packages]", + Short: "compile and install packages and dependencies", + Long: ` +Install compiles and installs the packages named by the import paths, +along with their dependencies. + +For more about the build flags, see 'go help build'. +For more about specifying packages, see 'go help packages'. + +See also: go build, go get, go clean. + `, +} + +func runInstall(cmd *Command, args []string) { + raceInit() + pkgs := packagesForBuild(args) + + for _, p := range pkgs { + if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { + if p.cmdline { + errorf("go install: no install location for .go files listed on command line (GOBIN not set)") + } else if 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) + } + } + } + exitIfErrors() + + var b builder + b.init() + a := &action{} + for _, p := range pkgs { + a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) + } + b.do(a) +} + +// Global build parameters (used during package load) +var ( + goarch string + goos string + archChar string + exeSuffix string +) + +func init() { + goarch = buildContext.GOARCH + goos = buildContext.GOOS + 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. +// It does not hold per-package state, because we +// build packages in parallel, and the builder is shared. +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 + print func(args ...interface{}) (int, error) + + output sync.Mutex + scriptDir string // current directory in printed script + + exec sync.Mutex + readySema chan bool + ready actionQueue +} + +// An action represents a single action in the action graph. +type action struct { + p *Package // the package this action works on + deps []*action // actions that must happen before this one + triggers []*action // inverse of deps + cgo *action // action for cgo binary if needed + args []string // additional args for runProgram + testOutput *bytes.Buffer // test output buffer + + f func(*builder, *action) error // the action itself (nil = no-op) + ignoreFail bool // whether to run f even if dependencies fail + + // Generated files, directories. + link bool // target is executable, not just package + pkgdir string // the -I or -L argument to use when importing this package + objdir string // directory for intermediate objects + objpkg string // the intermediate package .a file created during the action + target string // goal of the action: the created package or executable + + // Execution state. + pending int // number of deps yet to complete + priority int // relative execution priority + failed bool // whether the action failed +} + +// cacheKey is the key for the action cache. +type cacheKey struct { + mode buildMode + p *Package +} + +// buildMode specifies the build mode: +// are we just building things or also installing the results? +type buildMode int + +const ( + modeBuild buildMode = iota + modeInstall +) + +var ( + goroot = filepath.Clean(runtime.GOROOT()) + gobin = os.Getenv("GOBIN") + gorootBin = filepath.Join(goroot, "bin") + gorootSrcPkg = filepath.Join(goroot, "src/pkg") + gorootPkg = filepath.Join(goroot, "pkg") + gorootSrc = filepath.Join(goroot, "src") +) + +func (b *builder) init() { + var err error + b.print = func(a ...interface{}) (int, error) { + return fmt.Fprint(os.Stderr, a...) + } + b.actionCache = make(map[cacheKey]*action) + b.mkdirCache = make(map[string]bool) + + if buildN { + b.work = "$WORK" + } else { + b.work, err = ioutil.TempDir("", "go-build") + if err != nil { + fatalf("%s", err) + } + if buildX || buildWork { + fmt.Fprintf(os.Stderr, "WORK=%s\n", b.work) + } + if !buildWork { + workdir := b.work + atexit(func() { os.RemoveAll(workdir) }) + } + } +} + +// goFilesPackage creates a package for building a collection of Go files +// (typically named on the command line). The target is named p.a for +// package p or named after the first Go file for package main. +func goFilesPackage(gofiles []string) *Package { + // TODO: Remove this restriction. + for _, f := range gofiles { + if !strings.HasSuffix(f, ".go") { + fatalf("named files must be .go files") + } + } + + var stk importStack + ctxt := buildContext + ctxt.UseAllFiles = true + + // 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 + // consistently, the files must all be in the same directory. + var dirent []os.FileInfo + var dir string + for _, file := range gofiles { + fi, err := os.Stat(file) + if err != nil { + fatalf("%s", err) + } + if fi.IsDir() { + fatalf("%s is a directory, should be a Go file", file) + } + dir1, _ := filepath.Split(file) + if dir == "" { + dir = dir1 + } else if dir != dir1 { + fatalf("named files must all be in one directory; have %s and %s", dir, dir1) + } + dirent = append(dirent, fi) + } + ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } + + if !filepath.IsAbs(dir) { + dir = filepath.Join(cwd, dir) + } + + bp, err := ctxt.ImportDir(dir, 0) + pkg := new(Package) + pkg.local = true + pkg.cmdline = true + pkg.load(&stk, bp, err) + pkg.localPrefix = dirToImportPath(dir) + pkg.ImportPath = "command-line-arguments" + pkg.target = "" + + if pkg.Name == "main" { + _, elem := filepath.Split(gofiles[0]) + exe := elem[:len(elem)-len(".go")] + exeSuffix + if *buildO == "" { + *buildO = exe + } + if gobin != "" { + pkg.target = filepath.Join(gobin, exe) + } + } else { + if *buildO == "" { + *buildO = pkg.Name + ".a" + } + } + pkg.Target = pkg.target + pkg.Stale = true + + computeStale(pkg) + return pkg +} + +// action returns the action for applying the given operation (mode) to the package. +// depMode is the action to use when building dependencies. +func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action { + key := cacheKey{mode, p} + a := b.actionCache[key] + if a != nil { + 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)) + } + + // 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 + // are writing is not the cgo we need to use. + + if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace { + if reqPkgSrc { + 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 p.Standard { + switch p.ImportPath { + case "builtin", "unsafe": + // Fake packages - nothing to build. + return a + } + // gccgo standard library is "fake" too. + if _, ok := buildToolchain.(gccgoToolchain); ok { + // the target name is needed for cgo. + a.target = p.target + return a + } + } + + if !p.Stale && p.target != "" { + // p.Stale==false implies that p.target is up-to-date. + // Record target name for use by actions depending on this one. + a.target = p.target + return a + } + + if p.local && p.target == "" { + // Imported via local path. No permanent target. + mode = modeBuild + } + work := p.pkgdir + if work == "" { + work = b.work + } + a.objdir = filepath.Join(work, a.p.ImportPath, "_obj") + string(filepath.Separator) + a.objpkg = buildToolchain.pkgpath(work, a.p) + a.link = p.Name == "main" + + switch mode { + case modeInstall: + a.f = (*builder).install + a.deps = []*action{b.action(modeBuild, depMode, p)} + a.target = a.p.target + case modeBuild: + a.f = (*builder).build + a.target = a.objpkg + if a.link { + // An executable file. (This is the name of a temporary file.) + // Because we run the temporary file in 'go run' and 'go test', + // 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 + // to create a top-level package named exe. + name := "a.out" + if p.exeName != "" { + name = p.exeName + } + a.target = a.objdir + filepath.Join("exe", name) + exeSuffix + } + } + + 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 { + seen := map[*action]bool{} + all := []*action{} + var walk func(*action) + walk = func(a *action) { + if seen[a] { + return + } + seen[a] = true + for _, a1 := range a.deps { + walk(a1) + } + all = append(all, a) + } + walk(root) + return all +} + +// 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. + // 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 + // 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 + // 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. + all := actionList(root) + for i, a := range all { + a.priority = i + } + + b.readySema = make(chan bool, len(all)) + + // Initialize per-action execution state. + for _, a := range all { + for _, a1 := range a.deps { + a1.triggers = append(a1.triggers, a) + } + a.pending = len(a.deps) + if a.pending == 0 { + b.ready.push(a) + b.readySema <- true + } + } + + // Handle runs a single action and takes care of triggering + // any actions that are runnable as a result. + handle := func(a *action) { + var err error + if a.f != nil && (!a.failed || a.ignoreFail) { + err = a.f(b, a) + } + + // The actions run in parallel but all the updates to the + // shared work state are serialized through b.exec. + b.exec.Lock() + defer b.exec.Unlock() + + if err != nil { + if err == errPrintedOutput { + setExitStatus(2) + } else { + errorf("%s", err) + } + a.failed = true + } + + for _, a0 := range a.triggers { + if a.failed { + a0.failed = true + } + if a0.pending--; a0.pending == 0 { + b.ready.push(a0) + b.readySema <- true + } + } + + if a == root { + close(b.readySema) + } + } + + var wg sync.WaitGroup + + // Kick off goroutines according to parallelism. + // If we are using the -n flag (just printing commands) + // drop the parallelism to 1, both to make the output + // deterministic and because there is no real work anyway. + par := buildP + if buildN { + par = 1 + } + for i := 0; i < par; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for { + select { + case _, ok := <-b.readySema: + if !ok { + return + } + // Receiving a value from b.readySema entitles + // us to take from the ready queue. + b.exec.Lock() + a := b.ready.pop() + b.exec.Unlock() + handle(a) + case <-interrupted: + setExitStatus(1) + return + } + } + }() + } + + 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 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). + 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, ",")) + } + // Same as above for Objective-C files + if len(a.p.MFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() { + 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, ",")) + } + defer func() { + if err != nil && err != errPrintedOutput { + err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err) + } + }() + if buildN { + // In -n mode, print a banner between packages. + // The banner is five lines so that when changes to + // different sections of the bootstrap script have to + // be merged, the banners give patch something + // to use to find its context. + fmt.Printf("\n#\n# %s\n#\n\n", a.p.ImportPath) + } + + if buildV { + fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath) + } + + if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && + !hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") { + return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix()) + } + + // Make build directory. + obj := a.objdir + if err := b.mkdir(obj); err != nil { + return err + } + + // make target directory + dir, _ := filepath.Split(a.target) + if dir != "" { + if err := b.mkdir(dir); err != nil { + return err + } + } + + var gofiles, cfiles, sfiles, objects, cgoObjects []string + + gofiles = append(gofiles, a.p.GoFiles...) + cfiles = append(cfiles, a.p.CFiles...) + sfiles = append(sfiles, a.p.SFiles...) + + // Run cgo. + if a.p.usesCgo() { + // 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. + // In that case gcc only gets the gcc_* files. + var gccfiles []string + if a.p.Standard && a.p.ImportPath == "runtime/cgo" { + filter := func(files, nongcc, gcc []string) ([]string, []string) { + for _, f := range files { + if strings.HasPrefix(f, "gcc_") { + gcc = append(gcc, f) + } else { + nongcc = append(nongcc, f) + } + } + return nongcc, gcc + } + cfiles, gccfiles = filter(cfiles, cfiles[:0], gccfiles) + sfiles, gccfiles = filter(sfiles, sfiles[:0], gccfiles) + } else { + gccfiles = append(cfiles, sfiles...) + cfiles = nil + sfiles = nil + } + + cgoExe := tool("cgo") + if a.cgo != nil && a.cgo.target != "" { + cgoExe = a.cgo.target + } + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, 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...) + cfiles = nil + sfiles = nil + outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles, a.p.MFiles) + if err != nil { + return err + } + cgoObjects = append(cgoObjects, outObj...) + gofiles = append(gofiles, outGo...) + } + + if len(gofiles) == 0 { + return &build.NoGoError{a.p.Dir} + } + + // If we're doing coverage, preprocess the .go files and put them in the work directory + if a.p.coverMode != "" { + for i, file := range gofiles { + var sourceFile string + var coverFile string + var key string + if strings.HasSuffix(file, ".cgo1.go") { + // cgo files have absolute paths + base := filepath.Base(file) + sourceFile = file + coverFile = filepath.Join(obj, base) + key = strings.TrimSuffix(base, ".cgo1.go") + ".go" + } else { + sourceFile = filepath.Join(a.p.Dir, file) + coverFile = filepath.Join(obj, file) + key = file + } + cover := a.p.coverVars[key] + if cover == nil || isTestFile(file) { + // Not covering this file. + continue + } + if err := b.cover(a, coverFile, sourceFile, 0666, cover.Var); err != nil { + return err + } + gofiles[i] = coverFile + } + } + + // Prepare Go import path list. + inc := b.includeArgs("-I", a.deps) + + // Compile Go. + ofile, out, err := buildToolchain.gc(b, a.p, a.objpkg, obj, inc, gofiles) + if len(out) > 0 { + b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out)) + if err != nil { + return errPrintedOutput + } + } + if err != nil { + return err + } + if ofile != a.objpkg { + objects = append(objects, ofile) + } + + // Copy .h files named for goos or goarch or goos_goarch + // to names using GOOS and GOARCH. + // For example, defs_linux_amd64.h becomes defs_GOOS_GOARCH.h. + _goos_goarch := "_" + goos + "_" + goarch + _goos := "_" + goos + _goarch := "_" + goarch + for _, file := range a.p.HFiles { + name, ext := fileExtSplit(file) + 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 { + 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 { + 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 { + return err + } + } + } + + objExt := archChar + if _, ok := buildToolchain.(gccgoToolchain); ok { + objExt = "o" + } + + for _, file := range cfiles { + out := file[:len(file)-len(".c")] + "." + objExt + if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil { + return err + } + objects = append(objects, out) + } + + // Assemble .s files. + for _, file := range sfiles { + out := file[:len(file)-len(".s")] + "." + objExt + if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { + return err + } + objects = append(objects, out) + } + + // 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 + objects = append(objects, cgoObjects...) + + // Add system object files. + for _, syso := range a.p.SysoFiles { + objects = append(objects, filepath.Join(a.p.Dir, syso)) + } + + // Pack into archive in obj directory. + // If the Go compiler wrote an archive, we only need to add the + // object files for non-Go sources to the archive. + // If the Go compiler wrote an archive and the package is entirely + // Go sources, there is no pack to execute at all. + if len(objects) > 0 { + if err := buildToolchain.pack(b, a.p, obj, a.objpkg, objects); err != nil { + return err + } + } + + // Link if needed. + if a.link { + // The compiler only cares about direct imports, but the + // 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 { + return err + } + } + + return nil +} + +// install is the action for installing a single package or executable. +func (b *builder) install(a *action) (err error) { + defer func() { + if err != nil && err != errPrintedOutput { + err = fmt.Errorf("go install %s: %v", a.p.ImportPath, err) + } + }() + a1 := a.deps[0] + perm := os.FileMode(0644) + if a1.link { + perm = 0755 + } + + // make target directory + dir, _ := filepath.Split(a.target) + if dir != "" { + if err := b.mkdir(dir); err != nil { + return err + } + } + + // remove object dir to keep the amount of + // 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 { + defer os.RemoveAll(a1.objdir) + defer os.Remove(a1.target) + } + + return b.moveOrCopyFile(a, a.target, a1.target, perm) +} + +// includeArgs returns the -I or -L directory list for access +// to the results of the list of actions. +func (b *builder) includeArgs(flag string, all []*action) []string { + inc := []string{} + incMap := map[string]bool{ + b.work: true, // handled later + gorootPkg: true, + "": true, // ignore empty strings + } + + // Look in the temporary space for results of test-specific actions. + // This is the $WORK/my/package/_test directory for the + // package being built, so there are few of these. + for _, a1 := range all { + if dir := a1.pkgdir; dir != a1.p.build.PkgRoot && !incMap[dir] { + incMap[dir] = true + inc = append(inc, flag, dir) + } + } + + // Also look in $WORK for any non-test packages that have + // been built but not installed. + inc = append(inc, flag, b.work) + + // Finally, look in the installed package directories for each action. + for _, a1 := range all { + 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) + } + } + + return inc +} + +// moveOrCopyFile is like 'mv src dst' or 'cp src dst'. +func (b *builder) moveOrCopyFile(a *action, dst, src string, perm os.FileMode) error { + if buildN { + b.showcmd("", "mv %s %s", src, dst) + return nil + } + + // If we can update the mode and rename to the dst, do it. + // Otherwise fall back to standard copy. + if err := os.Chmod(src, perm); err == nil { + if err := os.Rename(src, dst); err == nil { + if buildX { + b.showcmd("", "mv %s %s", src, dst) + } + return nil + } + } + + return b.copyFile(a, dst, src, perm) +} + +// copyFile is like 'cp src dst'. +func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode) error { + if buildN || buildX { + b.showcmd("", "cp %s %s", src, dst) + if buildN { + return nil + } + } + + sf, err := os.Open(src) + if err != nil { + return err + } + defer sf.Close() + + // Be careful about removing/overwriting dst. + // Do not remove/overwrite if dst exists and is a directory + // or a non-object file. + if fi, err := os.Stat(dst); err == nil { + if fi.IsDir() { + return fmt.Errorf("build output %q already exists and is a directory", dst) + } + if !isObject(dst) { + return fmt.Errorf("build output %q already exists and is not an object file", dst) + } + } + + // On Windows, remove lingering ~ file from last attempt. + if toolIsWindows { + if _, err := os.Stat(dst + "~"); err == nil { + os.Remove(dst + "~") + } + } + + os.Remove(dst) + 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. + // 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 { + os.Remove(dst + "~") + } + df, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + } + if err != nil { + return err + } + + _, err = io.Copy(df, sf) + df.Close() + if err != nil { + os.Remove(dst) + return fmt.Errorf("copying %s to %s: %v", src, dst, err) + } + return nil +} + +// 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, + tool("cover"), + "-mode", a.p.coverMode, + "-var", varName, + "-o", dst, + src) +} + +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 +} + +func isObject(s string) bool { + f, err := os.Open(s) + if err != nil { + return false + } + defer f.Close() + buf := make([]byte, 64) + io.ReadFull(f, buf) + for _, magic := range objectMagic { + if bytes.HasPrefix(buf, magic) { + return true + } + } + return false +} + +// fmtcmd formats a command in the manner of fmt.Sprintf but also: +// +// If dir is non-empty and the script is not in dir right now, +// fmtcmd inserts "cd dir\n" before the command. +// +// fmtcmd replaces the value of b.work with $WORK. +// fmtcmd replaces the value of goroot with $GOROOT. +// fmtcmd replaces the value of b.gobin with $GOBIN. +// +// fmtcmd replaces the name of the current directory with dot (.) +// but only when it is at the beginning of a space-separated token. +// +func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string { + cmd := fmt.Sprintf(format, args...) + if dir != "" && dir != "/" { + cmd = strings.Replace(" "+cmd, " "+dir, " .", -1)[1:] + if b.scriptDir != dir { + b.scriptDir = dir + cmd = "cd " + dir + "\n" + cmd + } + } + if b.work != "" { + cmd = strings.Replace(cmd, b.work, "$WORK", -1) + } + return cmd +} + +// showcmd prints the given command to standard output +// for the implementation of -n or -x. +func (b *builder) showcmd(dir string, format string, args ...interface{}) { + b.output.Lock() + defer b.output.Unlock() + b.print(b.fmtcmd(dir, format, args...) + "\n") +} + +// showOutput prints "# desc" followed by the given output. +// 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. +// For example, if fmt doesn't compile and we are in src/pkg/html, +// the output is +// +// $ go build +// # fmt +// ../fmt/print.go:1090: undefined: asdf +// $ +// +// instead of +// +// $ go build +// # fmt +// /usr/gopher/go/src/pkg/fmt/print.go:1090: undefined: asdf +// $ +// +// showOutput also replaces references to the work directory with $WORK. +// +func (b *builder) showOutput(dir, desc, out string) { + prefix := "# " + desc + suffix := "\n" + out + if reldir := shortPath(dir); reldir != dir { + suffix = strings.Replace(suffix, " "+dir, " "+reldir, -1) + suffix = strings.Replace(suffix, "\n"+dir, "\n"+reldir, -1) + } + suffix = strings.Replace(suffix, " "+b.work, " $WORK", -1) + + b.output.Lock() + defer b.output.Unlock() + b.print(prefix, suffix) +} + +// shortPath returns an absolute or relative name for path, whatever is shorter. +func shortPath(path string) string { + if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) { + return rel + } + return path +} + +// relPaths returns a copy of paths with absolute paths +// made relative to the current directory if they would be shorter. +func relPaths(paths []string) []string { + var out []string + pwd, _ := os.Getwd() + for _, p := range paths { + rel, err := filepath.Rel(pwd, p) + if err == nil && len(rel) < len(p) { + p = rel + } + out = append(out, p) + } + return out +} + +// 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 +// print this error. +var errPrintedOutput = errors.New("already printed output - no need to show error") + +var cgoLine = regexp.MustCompile(`\[[^\[\]]+\.cgo1\.go:[0-9]+\]`) +var cgoTypeSigRe = regexp.MustCompile(`\b_Ctype_\B`) + +// run runs the command given by cmdline in the directory dir. +// If the command fails, run prints information about the failure +// and returns a non-nil error. +func (b *builder) run(dir string, desc string, env []string, cmdargs ...interface{}) error { + out, err := b.runOut(dir, desc, env, cmdargs...) + if len(out) > 0 { + if desc == "" { + desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " ")) + } + b.showOutput(dir, desc, b.processOutput(out)) + if err != nil { + err = errPrintedOutput + } + } + return err +} + +// processOutput prepares the output of runOut to be output to the console. +func (b *builder) processOutput(out []byte) string { + if out[len(out)-1] != '\n' { + out = append(out, '\n') + } + messages := string(out) + // Fix up output referring to cgo-generated code to be more readable. + // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19. + // Replace *[100]_Ctype_foo with *[100]C.foo. + // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite. + if !buildX && cgoLine.MatchString(messages) { + messages = cgoLine.ReplaceAllString(messages, "") + messages = cgoTypeSigRe.ReplaceAllString(messages, "C.") + } + return messages +} + +// runOut runs the command given by cmdline in the directory dir. +// It returns the command output and any errors that occurred. +func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...interface{}) ([]byte, error) { + cmdline := stringList(cmdargs...) + if buildN || buildX { + var envcmdline string + for i := range env { + envcmdline += env[i] + envcmdline += " " + } + envcmdline += joinUnambiguously(cmdline) + b.showcmd(dir, "%s", envcmdline) + if buildN { + return nil, nil + } + } + + nbusy := 0 + for { + var buf bytes.Buffer + cmd := exec.Command(cmdline[0], cmdline[1:]...) + cmd.Stdout = &buf + cmd.Stderr = &buf + cmd.Dir = dir + cmd.Env = mergeEnvLists(env, envForDir(cmd.Dir)) + 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 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 + // a reference to the fd, keeping us from running exec. + // + // But, you might reasonably wonder, how can this happen? + // The cgo fd, like all our fds, is close-on-exec, so that we need + // not worry about other processes inheriting the fd accidentally. + // 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 + // 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 + // 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) + // only guarantees that when it returns, the exec is committed to + // 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 + // guaranteed to be gone. + // + // Sleeping when we observe the race seems to be the most reliable + // option we have. + // + // http://golang.org/issue/3001 + // + if err != nil && nbusy < 3 && strings.Contains(err.Error(), "text file busy") { + time.Sleep(100 * time.Millisecond << uint(nbusy)) + nbusy++ + continue + } + + return buf.Bytes(), err + } +} + +// joinUnambiguously prints the slice, quoting where necessary to make the +// output unambiguous. +// TODO: See issue 5279. The printing of commands needs a complete redo. +func joinUnambiguously(a []string) string { + var buf bytes.Buffer + for i, s := range a { + if i > 0 { + buf.WriteByte(' ') + } + q := strconv.Quote(s) + if s == "" || strings.Contains(s, " ") || len(q) > len(s)+2 { + buf.WriteString(q) + } else { + buf.WriteString(s) + } + } + return buf.String() +} + +// mkdir makes the named directory. +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. + if b.mkdirCache[dir] { + return nil + } + b.mkdirCache[dir] = true + + if buildN || buildX { + b.showcmd("", "mkdir -p %s", dir) + if buildN { + return nil + } + } + + if err := os.MkdirAll(dir, 0777); err != nil { + return err + } + return nil +} + +// mkAbs returns an absolute path corresponding to +// evaluating f in the directory dir. +// We always pass absolute paths of source files so that +// the error messages will include the full path to a file +// in need of attention. +func mkAbs(dir, f string) string { + // Leave absolute paths alone. + // Also, during -n mode we use the pseudo-directory $WORK + // instead of creating an actual work directory that won't be used. + // Leave paths beginning with $WORK alone too. + if filepath.IsAbs(f) || strings.HasPrefix(f, "$WORK") { + return f + } + return filepath.Join(dir, f) +} + +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) + // 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 + // 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 + // 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 + + compiler() string + linker() string +} + +type noToolchain struct{} + +func noCompiler() error { + log.Fatalf("unknown compiler %q", buildContext.Compiler) + return nil +} + +func (noToolchain) compiler() string { + noCompiler() + return "" +} + +func (noToolchain) linker() string { + noCompiler() + return "" +} + +func (noToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) { + return "", nil, noCompiler() +} + +func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { + return noCompiler() +} + +func (noToolchain) pkgpath(basedir string, p *Package) string { + noCompiler() + return "" +} + +func (noToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { + return noCompiler() +} + +func (noToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { + return noCompiler() +} + +func (noToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { + return noCompiler() +} + +// The Go toolchain. +type gcToolchain struct{} + +func (gcToolchain) compiler() string { + return tool(archChar + "g") +} + +func (gcToolchain) linker() string { + return tool(archChar + "l") +} + +func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { + if archive != "" { + ofile = archive + } else { + out := "_go_." + archChar + ofile = obj + out + } + + gcargs := []string{"-p", p.ImportPath} + if p.Standard && p.ImportPath == "runtime" { + // runtime compiles with a special 6g flag to emit + // additional reflect type data. + gcargs = append(gcargs, "-+") + } + + // If we're giving the compiler the entire package (no C etc files), tell it that, + // 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) + if p.Standard { + switch p.ImportPath { + case "os", "runtime/pprof", "sync", "time": + extFiles++ + } + } + if extFiles == 0 { + gcargs = append(gcargs, "-complete") + } + if buildContext.InstallSuffix != "" { + gcargs = append(gcargs, "-installsuffix", buildContext.InstallSuffix) + } + + args := stringList(tool(archChar+"g"), "-o", ofile, "-trimpath", b.work, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) + if ofile == archive { + args = append(args, "-pack") + } + for _, f := range gofiles { + args = append(args, mkAbs(p.Dir, f)) + } + + 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 { + sfile = mkAbs(p.Dir, sfile) + return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile) +} + +func (gcToolchain) pkgpath(basedir string, p *Package) string { + end := filepath.FromSlash(p.ImportPath + ".a") + return filepath.Join(basedir, end) +} + +func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { + var absOfiles []string + for _, f := range ofiles { + absOfiles = append(absOfiles, mkAbs(objDir, f)) + } + cmd := "c" + absAfile := mkAbs(objDir, afile) + appending := false + if _, err := os.Stat(absAfile); err == nil { + appending = true + cmd = "r" + } + + cmdline := stringList("pack", cmd, absAfile, absOfiles) + + if appending { + if buildN || buildX { + b.showcmd(p.Dir, "%s # internal", joinUnambiguously(cmdline)) + } + if buildN { + return nil + } + if err := packInternal(b, absAfile, absOfiles); err != nil { + b.showOutput(p.Dir, p.ImportPath, err.Error()+"\n") + return errPrintedOutput + } + return nil + } + + // Need actual pack. + cmdline[0] = tool("pack") + return b.run(p.Dir, p.ImportPath, nil, cmdline) +} + +func packInternal(b *builder, afile string, ofiles []string) error { + dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0) + if err != nil { + return err + } + defer dst.Close() // only for error returns or panics + w := bufio.NewWriter(dst) + + for _, ofile := range ofiles { + src, err := os.Open(ofile) + if err != nil { + return err + } + fi, err := src.Stat() + if err != nil { + src.Close() + return err + } + // Note: Not using %-16.16s format because we care + // about bytes, not runes. + name := fi.Name() + if len(name) > 16 { + name = name[:16] + } else { + name += strings.Repeat(" ", 16-len(name)) + } + size := fi.Size() + fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n", + name, 0, 0, 0, 0644, size) + n, err := io.Copy(w, src) + src.Close() + if err == nil && n < size { + err = io.ErrUnexpectedEOF + } else if err == nil && n > size { + err = fmt.Errorf("file larger than size reported by stat") + } + if err != nil { + return fmt.Errorf("copying %s to %s: %v", ofile, afile, err) + } + if size&1 != 0 { + w.WriteByte(0) + } + } + + if err := w.Flush(); err != nil { + return err + } + return dst.Close() +} + +func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { + importArgs := b.includeArgs("-L", allactions) + cxx := false + for _, a := range allactions { + if a.p != nil && len(a.p.CXXFiles) > 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)] + if buildContext.InstallSuffix != "" { + ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix) + } + if p.omitDWARF { + ldflags = append(ldflags, "-w") + } + + // 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. + extld := false + for _, f := range ldflags { + if f == "-extld" || strings.HasPrefix(f, "-extld=") { + extld = true + break + } + } + 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) + } + } + } + return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, ldflags, mainpkg) +} + +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) + args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-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) +} + +// The Gccgo toolchain. +type gccgoToolchain struct{} + +var gccgoBin, _ = exec.LookPath("gccgo") + +func (gccgoToolchain) compiler() string { + return gccgoBin +} + +func (gccgoToolchain) linker() string { + return gccgoBin +} + +func (gccgoToolchain) gc(b *builder, p *Package, archive, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { + out := p.Name + ".o" + ofile = obj + out + gcargs := []string{"-g"} + gcargs = append(gcargs, b.gccArchArgs()...) + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath) + } + if p.localPrefix != "" { + gcargs = append(gcargs, "-fgo-relative-import-path="+p.localPrefix) + } + args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) + for _, f := range gofiles { + args = append(args, mkAbs(p.Dir, f)) + } + + output, err = b.runOut(p.Dir, p.ImportPath, nil, args) + return ofile, output, err +} + +func (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+`"`) + } + defs = append(defs, b.gccArchArgs()...) + return b.run(p.Dir, p.ImportPath, nil, "gccgo", "-I", obj, "-o", ofile, defs, sfile) +} + +func (gccgoToolchain) pkgpath(basedir string, p *Package) string { + end := filepath.FromSlash(p.ImportPath + ".a") + afile := filepath.Join(basedir, end) + // add "lib" to the final element + return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) +} + +func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { + var absOfiles []string + for _, f := range ofiles { + absOfiles = append(absOfiles, mkAbs(objDir, f)) + } + 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 { + // gccgo needs explicit linking with all package dependencies, + // and all LDFLAGS from cgo dependencies. + apackagesSeen := make(map[*Package]bool) + afiles := []string{} + ldflags := b.gccArchArgs() + cgoldflags := []string{} + usesCgo := false + cxx := false + objc := false + + // 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] { + apackagesSeen[a.p] = true + if a.p.fake { + // move _test files to the top of the link order + afiles = append([]string{a.target}, afiles...) + } else { + afiles = append(afiles, a.target) + } + } + } + } + + for _, a := range allactions { + if a.p != nil { + 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 + } + } + } + ldflags = append(ldflags, afiles...) + ldflags = append(ldflags, cgoldflags...) + ldflags = append(ldflags, p.CgoLDFLAGS...) + if usesCgo && goos == "linux" { + ldflags = append(ldflags, "-Wl,-E") + } + if cxx { + ldflags = append(ldflags, "-lstdc++") + } + if objc { + ldflags = append(ldflags, "-lobjc") + } + return b.run(".", p.ImportPath, nil, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) +} + +func (gccgoToolchain) 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) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + defs = append(defs, b.gccArchArgs()...) + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) + } + // TODO: Support using clang here (during gccgo build)? + return b.run(p.Dir, p.ImportPath, nil, "gcc", "-Wall", "-g", + "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) +} + +func gccgoPkgpath(p *Package) string { + if p.build.IsCommand() && !p.forceLibrary { + return "" + } + return p.ImportPath +} + +func gccgoCleanPkgpath(p *Package) string { + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + return strings.Map(clean, gccgoPkgpath(p)) +} + +// libgcc returns the filename for libgcc, as determined by invoking gcc with +// the -print-libgcc-file-name option. +func (b *builder) libgcc(p *Package) (string, error) { + var buf bytes.Buffer + + gccCmd := b.gccCmd(p.Dir) + + prev := b.print + if buildN { + // In -n mode we temporarily swap out the builder's + // print function to capture the command-line. This + // let's us assign it to $LIBGCC and produce a valid + // buildscript for cgo packages. + b.print = func(a ...interface{}) (int, error) { + return fmt.Fprint(&buf, a...) + } + } + f, err := b.runOut(p.Dir, p.ImportPath, nil, gccCmd, "-print-libgcc-file-name") + if err != nil { + return "", fmt.Errorf("gcc -print-libgcc-file-name: %v (%s)", err, f) + } + if buildN { + s := fmt.Sprintf("LIBGCC=$(%s)\n", buf.Next(buf.Len()-1)) + b.print = prev + b.print(s) + return "$LIBGCC", nil + } + + // clang might not be able to find libgcc, and in that case, + // it will simply return "libgcc.a", which is of no use to us. + if strings.Contains(gccCmd[0], "clang") && !filepath.IsAbs(string(f)) { + return "", nil + } + + return strings.Trim(string(f), "\r\n"), nil +} + +// gcc runs the gcc C compiler to create an object from a single C file. +func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error { + return b.ccompile(p, out, flags, cfile, b.gccCmd(p.Dir)) +} + +// gxx runs the g++ C++ compiler to create an object from a single C++ file. +func (b *builder) gxx(p *Package, out string, flags []string, cxxfile string) error { + return b.ccompile(p, out, flags, cxxfile, b.gxxCmd(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 { + file = mkAbs(p.Dir, file) + return b.run(p.Dir, p.ImportPath, nil, compiler, flags, "-o", out, "-c", file) +} + +// 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 { + cmd = b.gxxCmd(p.Dir) + } else { + cmd = b.gccCmd(p.Dir) + } + return b.run(p.Dir, p.ImportPath, nil, cmd, "-o", out, obj, flags) +} + +// gccCmd returns a gcc command line prefix +// defaultCC is defined in zdefaultcc.go, written by cmd/dist. +func (b *builder) gccCmd(objdir string) []string { + return b.ccompilerCmd("CC", defaultCC, objdir) +} + +// gxxCmd returns a g++ command line prefix +// defaultCXX is defined in zdefaultcc.go, written by cmd/dist. +func (b *builder) gxxCmd(objdir string) []string { + return b.ccompilerCmd("CXX", defaultCXX, 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 { + // NOTE: env.go's mkEnv knows that the first three + // strings returned are "gcc", "-I", objdir (and cuts them off). + + compiler := envList(envvar, defcmd) + a := []string{compiler[0], "-I", objdir} + a = append(a, compiler[1:]...) + + // Definitely want -fPIC but on Windows gcc complains + // "-fPIC ignored for target (all code is position independent)" + if goos != "windows" { + a = append(a, "-fPIC") + } + a = append(a, b.gccArchArgs()...) + // gcc-4.5 and beyond require explicit "-pthread" flag + // for multithreading with pthread library. + if buildContext.CgoEnabled { + switch goos { + case "windows": + a = append(a, "-mthreads") + default: + a = append(a, "-pthread") + } + } + + if strings.Contains(a[0], "clang") { + // disable ASCII art in clang errors, if possible + a = append(a, "-fno-caret-diagnostics") + // clang is too smart about command-line arguments + a = append(a, "-Qunused-arguments") + } + + // disable word wrapping in error messages + a = append(a, "-fmessage-length=0") + + // 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. + if goos == "darwin" { + a = append(a, "-fno-common") + } + + return a +} + +// gccArchArgs returns arguments to pass to gcc based on the architecture. +func (b *builder) gccArchArgs() []string { + switch archChar { + case "8": + return []string{"-m32"} + case "6": + return []string{"-m64"} + case "5": + return []string{"-marm"} // not thumb + } + return nil +} + +// envList returns the value of the given environment variable broken +// into fields, using the default value when the variable is empty. +func envList(key, def string) []string { + v := os.Getenv(key) + if v == "" { + v = def + } + 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" + } + + cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS) + cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) + cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) + ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) + return +} + +var cgoRe = regexp.MustCompile(`[/\\:]`) + +var ( + cgoLibGccFile string + cgoLibGccErr error + cgoLibGccFileOnce sync.Once +) + +func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true) + _, cgoexeCFLAGS, _, _ := b.cflags(p, false) + + // If we are compiling Objective-C code, then we need to link against libobjc + if len(mfiles) > 0 { + cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc") + } + + if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { + out, err := b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + return nil, nil, errPrintedOutput + } + if len(out) > 0 { + cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...) + } + out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + return nil, nil, errPrintedOutput + } + if len(out) > 0 { + cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...) + } + } + + // Allows including _cgo_export.h from .[ch] files in the package. + cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj) + + // cgo + // TODO: CGOPKGPATH, CGO_FLAGS? + gofiles := []string{obj + "_cgo_gotypes.go"} + cfiles := []string{"_cgo_main.c", "_cgo_export.c"} + for _, fn := range p.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? + + objExt := archChar + + if p.Standard && p.ImportPath == "runtime/cgo" { + cgoflags = append(cgoflags, "-import_runtime_cgo=false") + } + if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/cgo") { + cgoflags = append(cgoflags, "-import_syscall=false") + } + + // Update $CGO_LDFLAGS with p.CgoLDFLAGS. + var cgoenv []string + if len(cgoLDFLAGS) > 0 { + flags := make([]string, len(cgoLDFLAGS)) + for i, f := range cgoLDFLAGS { + flags[i] = strconv.Quote(f) + } + cgoenv = []string{"CGO_LDFLAGS=" + strings.Join(flags, " ")} + } + + if _, ok := buildToolchain.(gccgoToolchain); ok { + 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 { + 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 + } + outObj = append(outObj, defunObj) + + // gcc + var linkobj []string + + var bareLDFLAGS []string + // filter out -lsomelib, -l somelib, *.{so,dll,dylib}, and (on Darwin) -framework X + 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"): + continue + default: + bareLDFLAGS = append(bareLDFLAGS, f) + } + } + + cgoLibGccFileOnce.Do(func() { + cgoLibGccFile, cgoLibGccErr = b.libgcc(p) + }) + if cgoLibGccFile == "" && cgoLibGccErr != nil { + return nil, nil, err + } + + var staticLibs []string + if goos == "windows" { + // libmingw32 and libmingwex might also use libgcc, so libgcc must come last, + // and they also have some inter-dependencies, so must use linker groups. + staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"} + } + if cgoLibGccFile != "" { + staticLibs = append(staticLibs, cgoLibGccFile) + } + + 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) + } + } + + 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 + } + 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" + 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" + 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" + if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym + cgoLDFLAGS = append(cgoLDFLAGS, "-pie") + } + if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil { + return nil, nil, err + } + if goarch == "arm" && goos == "linux" { // but we don't need -pie for normal cgo programs + cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1] + } + + if _, ok := buildToolchain.(gccgoToolchain); ok { + // we don't use dynimport when using gccgo. + return outGo, outObj, nil + } + + // cgo -dynimport + importC := obj + "_cgo_import.c" + 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 { + return nil, nil, err + } + + 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) + } + } + if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), 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. http://golang.org/issue/2601 + outObj = stringList(importObj, nonGccObjs, ofile) + + return outGo, outObj, nil +} + +// 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, 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) + } + + if err := b.swigVersionCheck(); err != nil { + return nil, nil, err + } + + intgosize, err := b.swigIntSize(obj) + if err != nil { + return nil, nil, err + } + + for _, f := range p.SwigFiles { + goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, false, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + if gccObjFile != "" { + outObj = append(outObj, gccObjFile) + } + } + for _, f := range p.SwigCXXFiles { + goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, true, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + if gccObjFile != "" { + outObj = append(outObj, gccObjFile) + } + } + return outGo, outObj, nil +} + +// Make sure SWIG is new enough. +var ( + swigCheckOnce sync.Once + swigCheck error +) + +func (b *builder) swigDoVersionCheck() error { + out, err := b.runOut("", "", nil, "swig", "-version") + if err != nil { + return err + } + re := regexp.MustCompile(`[vV]ersion +([\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 + } + if major < 3 { + return errors.New("must have SWIG version >= 3.0") + } + return nil +} + +func (b *builder) swigVersionCheck() error { + swigCheckOnce.Do(func() { + swigCheck = b.swigDoVersionCheck() + }) + return swigCheck +} + +// This code fails to build if sizeof(int) <= 32 +const swigIntSizeCode = ` +package main +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) { + if buildN { + return "$INTBITS", nil + } + src := filepath.Join(b.work, "swig_intsize.go") + if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil { + return + } + srcs := []string{src} + + p := goFilesPackage(srcs) + + if _, _, e := buildToolchain.gc(b, p, "", obj, 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, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) { + cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) + var cflags []string + if cxx { + cflags = stringList(cgoCPPFLAGS, cgoCXXFLAGS) + } else { + cflags = stringList(cgoCPPFLAGS, cgoCFLAGS) + } + + n := 5 // length of ".swig" + if cxx { + n = 8 // length of ".swigcxx" + } + base := file[:len(file)-n] + goFile := base + ".go" + cBase := base + "_gc." + gccBase := base + "_wrap." + gccExt := "c" + if cxx { + gccExt = "cxx" + } + + _, gccgo := buildToolchain.(gccgoToolchain) + + // swig + args := []string{ + "-go", + "-intgosize", intgosize, + "-module", base, + "-o", obj + gccBase + gccExt, + "-outdir", obj, + } + if gccgo { + args = append(args, "-gccgo") + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + args = append(args, "-go-pkgpath", pkgpath) + } + } + if cxx { + args = append(args, "-c++") + } + + if out, err := b.runOut(p.Dir, p.ImportPath, nil, "swig", args, file); err != nil { + if len(out) > 0 { + if bytes.Contains(out, []byte("Unrecognized option -intgosize")) { + return "", "", "", errors.New("must have SWIG version >= 3.0") + } + b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) + return "", "", "", errPrintedOutput + } + 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 + } + } + + // 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, cObj, gccObj, nil +} + +// An actionQueue is a priority queue of actions. +type actionQueue []*action + +// Implement heap.Interface +func (q *actionQueue) Len() int { return len(*q) } +func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] } +func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority } +func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*action)) } +func (q *actionQueue) Pop() interface{} { + n := len(*q) - 1 + x := (*q)[n] + *q = (*q)[:n] + return x +} + +func (q *actionQueue) push(a *action) { + heap.Push(q, a) +} + +func (q *actionQueue) pop() *action { + return heap.Pop(q).(*action) +} + +func raceInit() { + if !buildRace { + return + } + if goarch != "amd64" || goos != "linux" && goos != "darwin" && goos != "windows" { + fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + os.Exit(2) + } + buildGcflags = append(buildGcflags, "-race") + buildLdflags = append(buildLdflags, "-race") + buildCcflags = append(buildCcflags, "-D", "RACE") + if buildContext.InstallSuffix != "" { + buildContext.InstallSuffix += "_" + } + buildContext.InstallSuffix += "race" + buildContext.BuildTags = append(buildContext.BuildTags, "race") +} + +// defaultSuffix returns file extension used for command files in +// current os environment. +func defaultSuffix() string { + switch runtime.GOOS { + case "windows": + return ".bat" + case "plan9": + return ".rc" + default: + return ".bash" + } +} |