summaryrefslogtreecommitdiff
path: root/src/cmd/go/build.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/build.go')
-rw-r--r--src/cmd/go/build.go679
1 files changed, 598 insertions, 81 deletions
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 767ddfd40..38fc43ef1 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -8,6 +8,7 @@ import (
"bytes"
"container/heap"
"errors"
+ "flag"
"fmt"
"go/build"
"io"
@@ -59,7 +60,12 @@ The build flags are shared by the build, install, run, and test commands:
do not delete it when exiting.
-x
print the commands.
+ -race
+ enable data race detection.
+ Supported only on linux/amd64, darwin/amd64 and windows/amd64.
+ -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'
@@ -73,6 +79,9 @@ The build flags are shared by the build, install, run, and test commands:
See the documentation for the go/build package for
more information about build tags.
+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,
see 'go help gopath'.
@@ -99,8 +108,10 @@ var buildX bool // -x 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 buildContext = build.Default
var buildToolchain toolchain = noToolchain{}
@@ -115,7 +126,7 @@ func (c buildCompiler) Set(value string) error {
case "gc":
buildToolchain = gcToolchain{}
case "gccgo":
- buildToolchain = gccgcToolchain{}
+ buildToolchain = gccgoToolchain{}
default:
return fmt.Errorf("unknown compiler %q", value)
}
@@ -132,7 +143,7 @@ func init() {
case "gc":
buildToolchain = gcToolchain{}
case "gccgo":
- buildToolchain = gccgcToolchain{}
+ buildToolchain = gccgoToolchain{}
}
}
@@ -146,10 +157,12 @@ func addBuildFlags(cmd *Command) {
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, "")
}
func addBuildFlagsNX(cmd *Command) {
@@ -157,11 +170,52 @@ func addBuildFlagsNX(cmd *Command) {
cmd.Flag.BoolVar(&buildX, "x", false, "")
}
+func isSpaceByte(c byte) bool {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
+}
+
type stringsFlag []string
func (v *stringsFlag) Set(s string) error {
- *v = strings.Fields(s)
- return nil
+ var err error
+ *v, err = splitQuotedFields(s)
+ 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 {
@@ -169,6 +223,7 @@ func (v *stringsFlag) String() string {
}
func runBuild(cmd *Command, args []string) {
+ raceInit()
var b builder
b.init()
@@ -179,6 +234,21 @@ func runBuild(cmd *Command, args []string) {
*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")
@@ -213,11 +283,12 @@ 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") {
- errorf("go install: no install location for %s", p.ImportPath)
+ errorf("go install: no install location for directory %s outside GOPATH", p.Dir)
}
}
exitIfErrors()
@@ -437,7 +508,7 @@ 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 {
+ if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace {
if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" {
var stk importStack
p1 := loadPackage("cmd/cgo", &stk)
@@ -456,7 +527,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
return a
}
// gccgo standard library is "fake" too.
- if _, ok := buildToolchain.(gccgcToolchain); ok {
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
// the target name is needed for cgo.
a.target = p.target
return a
@@ -487,9 +558,18 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
a.f = (*builder).build
a.target = a.objpkg
if a.link {
- // An executable file.
- // (This is the name of a temporary file.)
- a.target = a.objdir + "a.out" + exeSuffix
+ // 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
}
}
@@ -535,7 +615,6 @@ func (b *builder) do(root *action) {
}
b.readySema = make(chan bool, len(all))
- done := make(chan bool)
// Initialize per-action execution state.
for _, a := range all {
@@ -583,10 +662,11 @@ func (b *builder) do(root *action) {
if a == root {
close(b.readySema)
- done <- true
}
}
+ 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
@@ -596,19 +676,40 @@ func (b *builder) do(root *action) {
par = 1
}
for i := 0; i < par; i++ {
+ wg.Add(1)
go func() {
- for _ = range b.readySema {
- // Receiving a value from b.sema entitles
- // us to take from the ready queue.
- b.exec.Lock()
- a := b.ready.pop()
- b.exec.Unlock()
- handle(a)
+ 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
+ }
}
}()
}
- <-done
+ 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.
@@ -631,12 +732,25 @@ func (b *builder) build(a *action) (err error) {
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.bash", buildContext.GOOS, buildContext.GOARCH)
+ }
+
// 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...)
@@ -680,16 +794,37 @@ func (b *builder) build(a *action) (err error) {
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)
+ if err != nil {
+ return err
+ }
+ cgoObjects = append(cgoObjects, outObj...)
+ gofiles = append(gofiles, outGo...)
+ }
+
// Prepare Go import path list.
inc := b.includeArgs("-I", a.deps)
// Compile Go.
if len(gofiles) > 0 {
- if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil {
+ ofile, out, err := buildToolchain.gc(b, a.p, 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
- } else {
- objects = append(objects, out)
}
+ objects = append(objects, ofile)
}
// Copy .h files named for goos or goarch or goos_goarch
@@ -718,8 +853,13 @@ func (b *builder) build(a *action) (err error) {
}
}
+ objExt := archChar
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ objExt = "o"
+ }
+
for _, file := range cfiles {
- out := file[:len(file)-len(".c")] + "." + archChar
+ out := file[:len(file)-len(".c")] + "." + objExt
if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil {
return err
}
@@ -728,7 +868,7 @@ func (b *builder) build(a *action) (err error) {
// Assemble .s files.
for _, file := range sfiles {
- out := file[:len(file)-len(".s")] + "." + archChar
+ out := file[:len(file)-len(".s")] + "." + objExt
if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil {
return err
}
@@ -795,6 +935,20 @@ func (b *builder) install(a *action) (err error) {
defer os.Remove(a1.target)
}
+ if a.p.usesSwig() {
+ for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) {
+ dir = a.p.swigDir(&buildContext)
+ if err := b.mkdir(dir); err != nil {
+ return err
+ }
+ soname := a.p.swigSoname(f)
+ target := filepath.Join(dir, soname)
+ if err = b.copyFile(a, target, soname, perm); err != nil {
+ return err
+ }
+ }
+ }
+
return b.copyFile(a, a.target, a1.target, perm)
}
@@ -826,10 +980,13 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
for _, a1 := range all {
if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
incMap[dir] = true
- if _, ok := buildToolchain.(gccgcToolchain); ok {
- dir = filepath.Join(dir, "gccgo")
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch)
} else {
dir = filepath.Join(dir, goos+"_"+goarch)
+ if buildRace {
+ dir += "_race"
+ }
}
inc = append(inc, flag, dir)
}
@@ -905,6 +1062,8 @@ var objectMagic = [][]byte{
{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 {
@@ -1024,19 +1183,18 @@ func relPaths(paths []string) []string {
// print this error.
var errPrintedOutput = errors.New("already printed output - no need to show error")
+var cgoLine = regexp.MustCompile(`\[[^\[\]]+\.cgo1\.go:[0-9]+\]`)
+
// 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, cmdargs ...interface{}) error {
out, err := b.runOut(dir, desc, cmdargs...)
if len(out) > 0 {
- if out[len(out)-1] != '\n' {
- out = append(out, '\n')
- }
if desc == "" {
desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " "))
}
- b.showOutput(dir, desc, string(out))
+ b.showOutput(dir, desc, b.processOutput(out))
if err != nil {
err = errPrintedOutput
}
@@ -1044,6 +1202,23 @@ func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error {
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 _Ctype_foo with 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 = strings.Replace(messages, "type _Ctype_", "type C.", -1)
+ }
+ 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, cmdargs ...interface{}) ([]byte, error) {
@@ -1062,7 +1237,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt
cmd.Stdout = &buf
cmd.Stderr = &buf
cmd.Dir = dir
- // TODO: cmd.Env
+ cmd.Env = envForDir(cmd.Dir)
err := cmd.Run()
// cmd.Run will fail on Unix if some other process has the binary
@@ -1077,7 +1252,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt
// 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
+ // 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.
@@ -1161,7 +1336,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, obj string, importArgs []string, gofiles []string) (ofile string, err error)
+ gc(b *builder, p *Package, 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
@@ -1198,8 +1373,8 @@ func (noToolchain) linker() string {
return ""
}
-func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
- return "", noCompiler()
+func (noToolchain) gc(b *builder, p *Package, 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 {
@@ -1234,7 +1409,7 @@ func (gcToolchain) linker() string {
return tool(archChar + "l")
}
-func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) {
out := "_go_." + archChar
ofile = obj + out
gcargs := []string{"-p", p.ImportPath}
@@ -1244,16 +1419,33 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g
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.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")
+ }
+
args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs)
for _, f := range gofiles {
args = append(args, mkAbs(p.Dir, f))
}
- return ofile, b.run(p.Dir, p.ImportPath, args)
+
+ output, err = b.runOut(p.Dir, p.ImportPath, 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, tool(archChar+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile)
+ return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile)
}
func (gcToolchain) pkgpath(basedir string, p *Package) string {
@@ -1266,62 +1458,86 @@ func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s
for _, f := range ofiles {
absOfiles = append(absOfiles, mkAbs(objDir, f))
}
- return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles)
+ return b.run(p.Dir, p.ImportPath, tool("pack"), "grcP", b.work, mkAbs(objDir, afile), absOfiles)
}
func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
importArgs := b.includeArgs("-L", allactions)
- return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, buildLdflags, mainpkg)
+ swigDirs := make(map[string]bool)
+ swigArg := []string{}
+ for _, a := range allactions {
+ if a.p != nil && a.p.usesSwig() {
+ sd := a.p.swigDir(&buildContext)
+ if len(swigArg) == 0 {
+ swigArg = []string{"-r", sd}
+ } else if !swigDirs[sd] {
+ swigArg[1] += ":"
+ swigArg[1] += sd
+ }
+ swigDirs[sd] = true
+ }
+ }
+ return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, swigArg, buildLdflags, 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)
- return b.run(p.Dir, p.ImportPath, tool(archChar+"c"), "-FVw",
- "-I", objdir, "-I", inc, "-o", ofile,
- "-DGOOS_"+goos, "-DGOARCH_"+goarch, cfile)
+ args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile)
+ return b.run(p.Dir, p.ImportPath, args)
}
// The Gccgo toolchain.
-type gccgcToolchain struct{}
+type gccgoToolchain struct{}
var gccgoBin, _ = exec.LookPath("gccgo")
-func (gccgcToolchain) compiler() string {
+func (gccgoToolchain) compiler() string {
return gccgoBin
}
-func (gccgcToolchain) linker() string {
+func (gccgoToolchain) linker() string {
return gccgoBin
}
-func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+func (gccgoToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) {
out := p.Name + ".o"
ofile = obj + out
gcargs := []string{"-g"}
- if prefix := gccgoPrefix(p); prefix != "" {
- gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p))
+ 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))
}
- return ofile, b.run(p.Dir, p.ImportPath, args)
+
+ output, err = b.runOut(p.Dir, p.ImportPath, args)
+ return ofile, output, err
}
-func (gccgcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
+func (gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
sfile = mkAbs(p.Dir, sfile)
- return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, 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, "gccgo", "-I", obj, "-o", ofile, defs, sfile)
}
-func (gccgcToolchain) pkgpath(basedir string, p *Package) string {
+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 (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error {
+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))
@@ -1329,12 +1545,14 @@ func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles
return b.run(p.Dir, p.ImportPath, "ar", "cru", mkAbs(objDir, afile), absOfiles)
}
-func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
+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.
afiles := make(map[*Package]string)
- ldflags := []string{}
+ sfiles := make(map[*Package][]string)
+ ldflags := b.gccArchArgs()
cgoldflags := []string{}
+ usesCgo := false
for _, a := range allactions {
if a.p != nil {
if !a.p.Standard {
@@ -1343,31 +1561,99 @@ func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions []
}
}
cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...)
+ if len(a.p.CgoFiles) > 0 {
+ usesCgo = true
+ }
+ if a.p.usesSwig() {
+ sd := a.p.swigDir(&buildContext)
+ for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) {
+ soname := a.p.swigSoname(f)
+ sfiles[a.p] = append(sfiles[a.p], filepath.Join(sd, soname))
+ }
+ usesCgo = true
+ }
}
}
for _, afile := range afiles {
ldflags = append(ldflags, afile)
}
+ for _, sfiles := range sfiles {
+ ldflags = append(ldflags, sfiles...)
+ }
ldflags = append(ldflags, cgoldflags...)
- return b.run(".", p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(", ldflags, "-Wl,-)")
+ if usesCgo && goos == "linux" {
+ ldflags = append(ldflags, "-Wl,-E")
+ }
+ return b.run(".", p.ImportPath, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags)
}
-func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+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, "gcc", "-Wall", "-g",
- "-I", objdir, "-I", inc, "-o", ofile,
- "-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile)
+ "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
}
-func gccgoPrefix(p *Package) string {
- switch {
- case p.build.IsCommand() && !p.forceLibrary:
+func gccgoPkgpath(p *Package) string {
+ if p.build.IsCommand() && !p.forceLibrary {
return ""
- case p.fake:
- return "fake_" + p.ImportPath
}
- return "go_" + p.ImportPath
+ 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{}) (n int, err error) {
+ return fmt.Fprint(&buf, a...)
+ }
+ }
+ f, err := b.runOut(p.Dir, p.ImportPath, 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.
@@ -1386,20 +1672,19 @@ func (b *builder) gccCmd(objdir string) []string {
// NOTE: env.go's mkEnv knows that the first three
// strings returned are "gcc", "-I", objdir (and cuts them off).
- // TODO: HOST_CC?
- a := []string{"gcc", "-I", objdir, "-g", "-O2"}
+ gcc := strings.Fields(os.Getenv("CC"))
+ if len(gcc) == 0 {
+ gcc = append(gcc, "gcc")
+ }
+ a := []string{gcc[0], "-I", objdir, "-g", "-O2"}
+ a = append(a, gcc[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")
}
- switch archChar {
- case "8":
- a = append(a, "-m32")
- case "6":
- a = append(a, "-m64")
- }
+ a = append(a, b.gccArchArgs()...)
// gcc-4.5 and beyond require explicit "-pthread" flag
// for multithreading with pthread library.
if buildContext.CgoEnabled {
@@ -1411,6 +1696,11 @@ func (b *builder) gccCmd(objdir string) []string {
}
}
+ // clang is too smart about command-line arguments
+ if strings.Contains(a[0], "clang") {
+ a = append(a, "-Qunused-arguments")
+ }
+
// 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.
@@ -1421,12 +1711,31 @@ func (b *builder) gccCmd(objdir string) []string {
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
+}
+
func envList(key string) []string {
return strings.Fields(os.Getenv(key))
}
var cgoRe = regexp.MustCompile(`[/\\:]`)
+var (
+ cgoLibGccFile string
+ cgoLibGccErr error
+ cgoLibGccFileOnce sync.Once
+)
+
func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
if goos != toolGOOS {
return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
@@ -1478,10 +1787,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
- if _, ok := buildToolchain.(gccgcToolchain); ok {
+ if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/cgo") {
+ cgoflags = append(cgoflags, "-import_syscall=false")
+ }
+
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
cgoflags = append(cgoflags, "-gccgo")
- if prefix := gccgoPrefix(p); prefix != "" {
- cgoflags = append(cgoflags, "-gccgoprefix="+gccgoPrefix(p))
+ if pkgpath := gccgoPkgpath(p); pkgpath != "" {
+ cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath)
}
objExt = "o"
}
@@ -1499,6 +1812,36 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
// gcc
var linkobj []string
+
+ var bareLDFLAGS []string
+ // filter out -lsomelib, and -framework X if on Darwin
+ for i := 0; i < len(cgoLDFLAGS); i++ {
+ f := cgoLDFLAGS[i]
+ if !strings.HasPrefix(f, "-l") {
+ if goos == "darwin" && f == "-framework" { // skip the -framework X
+ i += 1
+ continue
+ }
+ 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
+ staticLibs = []string{"-lmingwex", "-lmingw32"}
+ }
+ if cgoLibGccFile != "" {
+ staticLibs = append(staticLibs, cgoLibGccFile)
+ }
+
for _, cfile := range cfiles {
ofile := obj + cfile[:len(cfile)-1] + "o"
if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil {
@@ -1517,19 +1860,30 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
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.(gccgcToolchain); ok {
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
// we don't use dynimport when using gccgo.
return outGo, outObj, nil
}
// cgo -dynimport
importC := obj + "_cgo_import.c"
- if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil {
+ 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, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC, cgoflags); err != nil {
return nil, nil, err
}
@@ -1539,14 +1893,162 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
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 = append([]string{importObj}, outObj...)
+ outObj = stringList(importObj, nonGccObjs, ofile)
return outGo, outObj, nil
}
+// Run SWIG on all SWIG input files.
+func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) {
+
+ intgosize, err := b.swigIntSize(obj)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ for _, f := range p.SwigFiles {
+ goFile, objFile, 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)
+ }
+ }
+ for _, f := range p.SwigCXXFiles {
+ goFile, objFile, 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)
+ }
+ }
+ return outGo, outObj, nil
+}
+
+// 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) {
+ 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 string, err error) {
+ 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"
+ }
+ soname := p.swigSoname(file)
+
+ _, gccgo := buildToolchain.(gccgoToolchain)
+
+ // swig
+ args := []string{
+ "-go",
+ "-intgosize", intgosize,
+ "-module", base,
+ "-soname", soname,
+ "-o", obj + gccBase + gccExt,
+ "-outdir", obj,
+ }
+ if gccgo {
+ args = append(args, "-gccgo")
+ }
+ if cxx {
+ args = append(args, "-c++")
+ }
+
+ if out, err := b.runOut(p.Dir, p.ImportPath, "swig", args, file); err != nil {
+ if len(out) > 0 {
+ if bytes.Contains(out, []byte("Unrecognized option -intgosize")) {
+ return "", "", errors.New("must have SWIG version >= 2.0.9\n")
+ }
+ 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 err := b.gcc(p, gccObj, []string{"-g", "-fPIC", "-O2"}, obj+gccBase+gccExt); err != nil {
+ return "", "", err
+ }
+
+ // create shared library
+ osldflags := map[string][]string{
+ "darwin": {"-dynamiclib", "-Wl,-undefined,dynamic_lookup"},
+ "freebsd": {"-shared", "-lpthread", "-lm"},
+ "linux": {"-shared", "-lpthread", "-lm"},
+ "windows": {"-shared", "-lm", "-mthreads"},
+ }
+ var cxxlib []string
+ if cxx {
+ cxxlib = []string{"-lstdc++"}
+ }
+ ldflags := stringList(osldflags[goos], cxxlib)
+ b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags)
+
+ return obj + goFile, cObj, nil
+}
+
// An actionQueue is a priority queue of actions.
type actionQueue []*action
@@ -1569,3 +2071,18 @@ func (q *actionQueue) push(a *action) {
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")
+ buildContext.InstallTag = "race"
+ buildContext.BuildTags = append(buildContext.BuildTags, "race")
+}