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.go425
1 files changed, 309 insertions, 116 deletions
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index cbe36f52e..caffa1f05 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -14,6 +14,7 @@ import (
"io/ioutil"
"os"
"os/exec"
+ "path"
"path/filepath"
"regexp"
"runtime"
@@ -22,7 +23,7 @@ import (
)
var cmdBuild = &Command{
- UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [importpath... | gofiles...]",
+ UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [-work] [importpath... | gofiles...]",
Short: "compile packages and dependencies",
Long: `
Build compiles the packages named by the import paths,
@@ -47,6 +48,9 @@ It is an error to use -o when the command line specifies multiple packages.
The -p flag specifies the number of builds that can be run in parallel.
The default is the number of CPUs available.
+The -work flag causes build to print the name of the temporary work
+directory and not delete it when exiting.
+
For more about import paths, see 'go help importpath'.
See also: go install, go get, go clean.
@@ -69,6 +73,7 @@ var buildP = runtime.NumCPU() // -p flag
var buildV bool // -v flag
var buildX bool // -x flag
var buildO = cmdBuild.Flag.String("o", "", "output file")
+var buildWork bool // -work flag
var buildContext = build.DefaultContext
@@ -79,6 +84,7 @@ func addBuildFlags(cmd *Command) {
cmd.Flag.IntVar(&buildP, "p", buildP, "")
cmd.Flag.BoolVar(&buildV, "v", false, "")
cmd.Flag.BoolVar(&buildX, "x", false, "")
+ cmd.Flag.BoolVar(&buildWork, "work", false, "")
// TODO(rsc): This -t flag is used by buildscript.sh but
// not documented. Should be documented but the
@@ -113,7 +119,10 @@ func runBuild(cmd *Command, args []string) {
}
if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" {
- *buildO = "a.out"
+ _, *buildO = path.Split(pkgs[0].ImportPath)
+ if b.goos == "windows" {
+ *buildO += ".exe"
+ }
}
if *buildO != "" {
@@ -136,7 +145,7 @@ func runBuild(cmd *Command, args []string) {
}
var cmdInstall = &Command{
- UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [importpath...]",
+ UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [-work] [importpath...]",
Short: "compile and install packages and dependencies",
Long: `
Install compiles and installs the packages named by the import paths,
@@ -150,6 +159,9 @@ The -x flag prints the commands.
The -p flag specifies the number of builds that can be run in parallel.
The default is the number of CPUs available.
+The -work flag causes build to print the name of the temporary work
+directory and not delete it when exiting.
+
For more about import paths, see 'go help importpath'.
See also: go build, go get, go clean.
@@ -174,14 +186,13 @@ func runInstall(cmd *Command, args []string) {
type builder struct {
work string // the temporary work directory (ends in filepath.Separator)
arch string // e.g., "6"
- goroot string // the $GOROOT
goarch string // the $GOARCH
goos string // the $GOOS
- gobin string // the $GOBIN
exe string // the executable suffix - "" or ".exe"
gcflags []string // additional flags for Go compiler
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
@@ -231,14 +242,18 @@ const (
modeInstall
)
+var (
+ gobin = build.Path[0].BinDir()
+ goroot = build.Path[0].Path
+)
+
func (b *builder) init() {
var err error
+ b.print = fmt.Print
b.actionCache = make(map[cacheKey]*action)
b.mkdirCache = make(map[string]bool)
b.goarch = buildContext.GOARCH
b.goos = buildContext.GOOS
- b.goroot = build.Path[0].Path
- b.gobin = build.Path[0].BinDir()
if b.goos == "windows" {
b.exe = ".exe"
}
@@ -256,10 +271,12 @@ func (b *builder) init() {
if err != nil {
fatalf("%s", err)
}
- if buildX {
+ if buildX || buildWork {
fmt.Printf("WORK=%s\n", b.work)
}
- atexit(func() { os.RemoveAll(b.work) })
+ if !buildWork {
+ atexit(func() { os.RemoveAll(b.work) })
+ }
}
}
@@ -293,10 +310,21 @@ func goFilesPackage(gofiles []string, target string) *Package {
ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dir, nil }
pwd, _ := os.Getwd()
var stk importStack
- pkg := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd+"/.", &stk)
+ pkg := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd+"/.", &stk, true)
if pkg.Error != nil {
fatalf("%s", pkg.Error)
}
+ printed := map[error]bool{}
+ for _, err := range pkg.DepsErrors {
+ // Since these are errors in dependencies,
+ // the same error might show up multiple times,
+ // once in each package that depends on it.
+ // Only print each once.
+ if !printed[err] {
+ printed[err] = true
+ errorf("%s", err)
+ }
+ }
if target != "" {
pkg.target = target
} else if pkg.Name == "main" {
@@ -305,6 +333,7 @@ func goFilesPackage(gofiles []string, target string) *Package {
pkg.target = pkg.Name + ".a"
}
pkg.ImportPath = "_/" + pkg.target
+ exitIfErrors()
return pkg
}
@@ -328,14 +357,21 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
a.deps = append(a.deps, b.action(depMode, depMode, p1))
}
- if len(p.CgoFiles) > 0 {
- var stk importStack
- p1 := loadPackage("cmd/cgo", &stk)
- if p1.Error != nil {
- fatalf("load cmd/cgo: %v", p1.Error)
+ // 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 b.goos == runtime.GOOS && b.goarch == runtime.GOARCH {
+ 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)
}
- a.cgo = b.action(depMode, depMode, p1)
- a.deps = append(a.deps, a.cgo)
}
if p.Standard {
@@ -344,6 +380,12 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
// 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 && !buildA && p.target != "" {
@@ -354,7 +396,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
}
a.objdir = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator)
- a.objpkg = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a"))
+ a.objpkg = buildToolchain.pkgpath(b.work, a.p)
a.link = p.Name == "main"
switch mode {
@@ -367,8 +409,6 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
a.target = a.objpkg
if a.link {
// An executable file.
- // Have to use something other than .a for the suffix.
- // It is easier on Windows if we use .exe, so use .exe everywhere.
// (This is the name of a temporary file.)
a.target = a.objdir + "a.out" + b.exe
}
@@ -445,7 +485,7 @@ func (b *builder) do(root *action) {
if err != nil {
if err == errPrintedOutput {
- exitStatus = 2
+ setExitStatus(2)
} else {
errorf("%s", err)
}
@@ -544,7 +584,11 @@ func (b *builder) build(a *action) error {
sfiles = nil
}
- outGo, outObj, err := b.cgo(a.p, a.cgo.target, obj, gccfiles)
+ cgoExe := tool("cgo")
+ if a.cgo != nil {
+ cgoExe = a.cgo.target
+ }
+ outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles)
if err != nil {
return err
}
@@ -557,17 +601,11 @@ func (b *builder) build(a *action) error {
// Compile Go.
if len(gofiles) > 0 {
- out := "_go_." + b.arch
- gcargs := []string{"-p", a.p.ImportPath}
- if a.p.Standard && a.p.ImportPath == "runtime" {
- // runtime compiles with a special 6g flag to emit
- // additional reflect type data.
- gcargs = append(gcargs, "-+")
- }
- if err := b.gc(a.p, obj+out, gcargs, inc, gofiles); err != nil {
+ if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil {
return err
+ } else {
+ objects = append(objects, out)
}
- objects = append(objects, out)
}
// Copy .h files named for goos or goarch or goos_goarch
@@ -598,7 +636,7 @@ func (b *builder) build(a *action) error {
for _, file := range cfiles {
out := file[:len(file)-len(".c")] + "." + b.arch
- if err := b.cc(a.p, obj, obj+out, file); err != nil {
+ if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil {
return err
}
objects = append(objects, out)
@@ -607,7 +645,7 @@ func (b *builder) build(a *action) error {
// Assemble .s files.
for _, file := range sfiles {
out := file[:len(file)-len(".s")] + "." + b.arch
- if err := b.asm(a.p, obj, obj+out, file); err != nil {
+ if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil {
return err
}
objects = append(objects, out)
@@ -620,7 +658,7 @@ func (b *builder) build(a *action) error {
objects = append(objects, cgoObjects...)
// Pack into archive in obj directory
- if err := b.gopack(a.p, obj, a.objpkg, objects); err != nil {
+ if err := buildToolchain.pack(b, a.p, obj, a.objpkg, objects); err != nil {
return err
}
@@ -630,8 +668,7 @@ func (b *builder) build(a *action) error {
// linker needs the whole dependency tree.
all := actionList(a)
all = all[:len(all)-1] // drop a
- inc := b.includeArgs("-L", all)
- if err := b.ld(a.p, a.target, inc, a.objpkg); err != nil {
+ if err := buildToolchain.ld(b, a.p, a.target, all, a.objpkg, objects); err != nil {
return err
}
}
@@ -655,6 +692,15 @@ func (b *builder) install(a *action) error {
}
}
+ // 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.copyFile(a.target, a1.target, perm)
}
@@ -685,6 +731,9 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
// Finally, look in the installed package directories for each action.
for _, a1 := range all {
if dir := a1.pkgdir; dir == a1.p.t.PkgDir() && !incMap[dir] {
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ dir = filepath.Join(filepath.Dir(dir), "gccgo", filepath.Base(dir))
+ }
incMap[dir] = true
inc = append(inc, flag, dir)
}
@@ -693,33 +742,6 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
return inc
}
-// removeByRenaming removes file name by moving it to a tmp
-// directory and deleting the target if possible.
-func removeByRenaming(name string) error {
- f, err := ioutil.TempFile("", "")
- if err != nil {
- return err
- }
- tmpname := f.Name()
- f.Close()
- err = os.Remove(tmpname)
- if err != nil {
- return err
- }
- err = os.Rename(name, tmpname)
- if err != nil {
- // assume name file does not exists,
- // otherwise later code will fail.
- return nil
- }
- err = os.Remove(tmpname)
- if err != nil {
- // TODO(brainman): file is locked and can't be deleted.
- // We need to come up with a better way of doing it.
- }
- return nil
-}
-
// copyFile is like 'cp src dst'.
func (b *builder) copyFile(dst, src string, perm os.FileMode) error {
if buildN || buildX {
@@ -734,23 +756,42 @@ func (b *builder) copyFile(dst, src string, perm os.FileMode) error {
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 {
- if runtime.GOOS != "windows" {
- return err
- }
- // Windows does not allow to replace binary file
- // while it is executing. We will cheat.
- err = removeByRenaming(dst)
- if err != nil {
- return err
+ 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 remove 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
- }
}
+ if err != nil {
+ return err
+ }
+
_, err = io.Copy(df, sf)
df.Close()
if err != nil {
@@ -760,13 +801,39 @@ func (b *builder) copyFile(dst, src string, perm os.FileMode) error {
return nil
}
+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
+}
+
+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 b.goroot with $GOROOT.
+// 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 (.)
@@ -781,9 +848,11 @@ func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string
cmd = "cd " + dir + "\n" + cmd
}
}
- cmd = strings.Replace(cmd, b.work, "$WORK", -1)
- cmd = strings.Replace(cmd, b.gobin, "$GOBIN", -1)
- cmd = strings.Replace(cmd, b.goroot, "$GOROOT", -1)
+ if b.work != "" {
+ cmd = strings.Replace(cmd, b.work, "$WORK", -1)
+ }
+ cmd = strings.Replace(cmd, gobin, "$GOBIN", -1)
+ cmd = strings.Replace(cmd, goroot, "$GOROOT", -1)
return cmd
}
@@ -792,7 +861,7 @@ func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string
func (b *builder) showcmd(dir string, format string, args ...interface{}) {
b.output.Lock()
defer b.output.Unlock()
- fmt.Println(b.fmtcmd(dir, format, args...))
+ b.print(b.fmtcmd(dir, format, args...) + "\n")
}
// showOutput prints "# desc" followed by the given output.
@@ -829,7 +898,7 @@ func (b *builder) showOutput(dir, desc, out string) {
b.output.Lock()
defer b.output.Unlock()
- fmt.Print(prefix, suffix)
+ b.print(prefix, suffix)
}
// relPaths returns a copy of paths with absolute paths
@@ -935,49 +1004,153 @@ func mkAbs(dir, f string) string {
return filepath.Join(dir, f)
}
-// gc runs the Go compiler in a specific directory on a set of files
-// to generate the named output file.
-func (b *builder) gc(p *Package, ofile string, gcargs, importArgs []string, gofiles []string) error {
- args := stringList(b.arch+"g", "-o", ofile, b.gcflags, gcargs, importArgs)
+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.
+ gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, 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 creates the appropriate destination path for a 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
+}
+
+type goToolchain struct{}
+type gccgoToolchain struct{}
+
+var buildToolchain toolchain
+
+func init() {
+ if os.Getenv("GC") == "gccgo" {
+ buildToolchain = gccgoToolchain{}
+ } else {
+ buildToolchain = goToolchain{}
+ }
+}
+
+// The Go toolchain.
+
+func (goToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+ out := "_go_." + b.arch
+ 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, "-+")
+ }
+
+ args := stringList(tool(b.arch+"g"), "-o", ofile, b.gcflags, gcargs, importArgs)
for _, f := range gofiles {
args = append(args, mkAbs(p.Dir, f))
}
- return b.run(p.Dir, p.ImportPath, args)
+ return ofile, b.run(p.Dir, p.ImportPath, args)
}
-// asm runs the assembler in a specific directory on a specific file
-// to generate the named output file.
-func (b *builder) asm(p *Package, obj, ofile, sfile string) error {
+func (goToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
sfile = mkAbs(p.Dir, sfile)
- return b.run(p.Dir, p.ImportPath, b.arch+"a", "-I", obj, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile)
+ return b.run(p.Dir, p.ImportPath, tool(b.arch+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile)
+}
+
+func (goToolchain) pkgpath(basedir string, p *Package) string {
+ return filepath.Join(basedir, filepath.FromSlash(p.ImportPath+".a"))
}
-// gopack runs the assembler in a specific directory to create
-// an archive from a set of object files.
-// typically it is run in the object directory.
-func (b *builder) gopack(p *Package, objDir, afile string, ofiles []string) error {
+func (goToolchain) 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, "gopack", "grc", mkAbs(objDir, afile), absOfiles)
+ return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles)
}
-// ld runs the linker to create a package starting at mainpkg.
-func (b *builder) ld(p *Package, out string, importArgs []string, mainpkg string) error {
- return b.run(p.Dir, p.ImportPath, b.arch+"l", "-o", out, importArgs, mainpkg)
+func (goToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
+ importArgs := b.includeArgs("-L", allactions)
+ return b.run(p.Dir, p.ImportPath, tool(b.arch+"l"), "-o", out, importArgs, mainpkg)
}
-// cc runs the gc-toolchain C compiler in a directory on a C file
-// to produce an output file.
-func (b *builder) cc(p *Package, objdir, ofile, cfile string) error {
- inc := filepath.Join(b.goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch))
+func (goToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch))
cfile = mkAbs(p.Dir, cfile)
- return b.run(p.Dir, p.ImportPath, b.arch+"c", "-FVw",
+ return b.run(p.Dir, p.ImportPath, tool(b.arch+"c"), "-FVw",
"-I", objdir, "-I", inc, "-o", ofile,
"-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, cfile)
}
+// The Gccgo toolchain.
+
+func (gccgoToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) {
+ out := p.Name + ".o"
+ ofile = obj + out
+ gcargs := []string{"-g"}
+ if p.Name != "main" {
+ if p.fake {
+ gcargs = append(gcargs, "-fgo-prefix=fake_"+p.ImportPath)
+ } else {
+ gcargs = append(gcargs, "-fgo-prefix=go_"+p.ImportPath)
+ }
+ }
+ args := stringList("gccgo", importArgs, "-c", b.gcflags, gcargs, "-o", ofile)
+ for _, f := range gofiles {
+ args = append(args, mkAbs(p.Dir, f))
+ }
+ return ofile, b.run(p.Dir, p.ImportPath, args)
+}
+
+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_"+b.goos, "-DGOARCH_"+b.goarch, sfile)
+}
+
+func (gccgoToolchain) pkgpath(basedir string, p *Package) string {
+ afile := filepath.Join(basedir, filepath.FromSlash(p.ImportPath+".a"))
+ // prepend "lib" to the basename
+ 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, "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
+ afiles := []string{}
+ ldflags := []string{}
+ seen := map[*Package]bool{}
+ for _, a := range allactions {
+ if a.p != nil && !seen[a.p] {
+ seen[a.p] = true
+ if !a.p.Standard {
+ afiles = append(afiles, a.target)
+ }
+ ldflags = append(ldflags, a.p.CgoLDFLAGS...)
+ }
+ }
+ return b.run(p.Dir, p.ImportPath, "gccgo", "-o", out, ofiles, "-Wl,-(", afiles, ldflags, "-Wl,-)")
+}
+
+func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
+ inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goarch))
+ cfile = mkAbs(p.Dir, cfile)
+ return b.run(p.Dir, p.ImportPath, "gcc", "-Wall", "-g",
+ "-I", objdir, "-I", inc, "-o", ofile,
+ "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, "-c", cfile)
+}
+
// 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 {
cfile = mkAbs(p.Dir, cfile)
@@ -1018,34 +1191,44 @@ func (b *builder) gccCmd(objdir string) []string {
return a
}
+func envList(key string) []string {
+ return strings.Fields(os.Getenv(key))
+}
+
var cgoRe = regexp.MustCompile(`[/\\:]`)
func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
- if b.goos != runtime.GOOS {
+ if b.goos != toolGOOS {
return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
}
- outObj = append(outObj, "") // for importObj, at end of function
+ cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.info.CgoCFLAGS)
+ cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.info.CgoLDFLAGS)
- cgoCFLAGS := stringList(p.info.CgoCFLAGS)
- cgoLDFLAGS := stringList(p.info.CgoLDFLAGS)
if pkgs := p.info.CgoPkgConfig; len(pkgs) > 0 {
out, err := b.runOut(p.Dir, p.ImportPath, "pkg-config", "--cflags", pkgs)
if err != nil {
- return nil, nil, err
+ 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 {
cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...)
}
out, err = b.runOut(p.Dir, p.ImportPath, "pkg-config", "--libs", pkgs)
if err != nil {
- return nil, nil, err
+ 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.
+ cgoCFLAGS = append(cgoCFLAGS, "-I", obj)
+
// cgo
// TODO: CGOPKGPATH, CGO_FLAGS?
gofiles := []string{obj + "_cgo_gotypes.go"}
@@ -1056,19 +1239,24 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
cfiles = append(cfiles, f+"cgo2.c")
}
defunC := obj + "_cgo_defun.c"
+
+ cgoflags := []string{}
// TODO: make cgo not depend on $GOARCH?
- var runtimeFlag []string
+
if p.Standard && p.ImportPath == "runtime/cgo" {
- runtimeFlag = []string{"-import_runtime_cgo=false"}
+ cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
- if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, runtimeFlag, "--", p.CgoFiles); err != nil {
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ cgoflags = append(cgoflags, "-gccgo")
+ }
+ if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, cgoflags, "--", cgoCFLAGS, p.CgoFiles); err != nil {
return nil, nil, err
}
outGo = append(outGo, gofiles...)
// cc _cgo_defun.c
defunObj := obj + "_cgo_defun." + b.arch
- if err := b.cc(p, obj, defunObj, defunC); err != nil {
+ if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {
return nil, nil, err
}
outObj = append(outObj, defunObj)
@@ -1098,6 +1286,11 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
return nil, nil, err
}
+ 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 {
@@ -1106,14 +1299,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
// cc _cgo_import.ARCH
importObj := obj + "_cgo_import." + b.arch
- if err := b.cc(p, obj, importObj, importC); err != nil {
+ if err := buildToolchain.cc(b, p, obj, importObj, importC); 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. We left room above. http://golang.org/issue/2601
- outObj[0] = importObj
+ // Put it first. http://golang.org/issue/2601
+ outObj = append([]string{importObj}, outObj...)
return outGo, outObj, nil
}