1
0
mirror of https://github.com/golang/go synced 2024-11-13 16:50:23 -07:00

cmd/go/internal/work: factor build.go into multiple files

build.go - commands and misc helpers
action.go - action graph construction
exec.go - action graph execution
gc.go - gc toolchain
gccgo.go - gccgo toolchain

Change-Id: I39b6e2490ac05334c2321e9ad88df694a6efa82f
Reviewed-on: https://go-review.googlesource.com/70671
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Russ Cox 2017-10-13 09:28:15 -04:00
parent 1992ab7e65
commit 08362246b6
5 changed files with 3573 additions and 3485 deletions

View File

@ -0,0 +1,691 @@
// 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.
// Action graph creation (planning).
package work
import (
"bufio"
"bytes"
"container/heap"
"debug/elf"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"time"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/internal/buildid"
)
// 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 {
WorkDir string // the temporary work directory (ends in filepath.Separator)
actionCache map[cacheKey]*Action // a cache of already-constructed actions
mkdirCache map[string]bool // a cache of created directories
flagCache map[[2]string]bool // a cache of supported compiler flags
Print func(args ...interface{}) (int, error)
objdirSeq int // counter for NewObjdir
pkgSeq int
output sync.Mutex
scriptDir string // current directory in printed script
exec sync.Mutex
readySema chan bool
ready actionQueue
}
// NOTE: Much of Action would not need to be exported if not for test.
// Maybe test functionality should move into this package too?
// An Action represents a single action in the action graph.
type Action struct {
Mode string // description of action operation
Package *load.Package // the package this action works on
Deps []*Action // actions that must happen before this one
Func func(*Builder, *Action) error // the action itself (nil = no-op)
IgnoreFail bool // whether to run f even if dependencies fail
TestOutput *bytes.Buffer // test output buffer
Args []string // additional args for runProgram
triggers []*Action // inverse of deps
buildID string
// Generated files, directories.
Objdir string // directory for intermediate objects
Target string // goal of the action: the created package or executable
built string // the actual 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
}
// 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)
}
type actionJSON struct {
ID int
Mode string
Package string
Deps []int `json:",omitempty"`
IgnoreFail bool `json:",omitempty"`
Args []string `json:",omitempty"`
Link bool `json:",omitempty"`
Objdir string `json:",omitempty"`
Target string `json:",omitempty"`
Priority int `json:",omitempty"`
Failed bool `json:",omitempty"`
Built string `json:",omitempty"`
}
// cacheKey is the key for the action cache.
type cacheKey struct {
mode string
p *load.Package
}
func actionGraphJSON(a *Action) string {
var workq []*Action
var inWorkq = make(map[*Action]int)
add := func(a *Action) {
if _, ok := inWorkq[a]; ok {
return
}
inWorkq[a] = len(workq)
workq = append(workq, a)
}
add(a)
for i := 0; i < len(workq); i++ {
for _, dep := range workq[i].Deps {
add(dep)
}
}
var list []*actionJSON
for id, a := range workq {
aj := &actionJSON{
Mode: a.Mode,
ID: id,
IgnoreFail: a.IgnoreFail,
Args: a.Args,
Objdir: a.Objdir,
Target: a.Target,
Failed: a.Failed,
Priority: a.priority,
Built: a.built,
}
if a.Package != nil {
// TODO(rsc): Make this a unique key for a.Package somehow.
aj.Package = a.Package.ImportPath
}
for _, a1 := range a.Deps {
aj.Deps = append(aj.Deps, inWorkq[a1])
}
list = append(list, aj)
}
js, err := json.MarshalIndent(list, "", "\t")
if err != nil {
fmt.Fprintf(os.Stderr, "go: writing debug action graph: %v\n", err)
return ""
}
return string(js)
}
// BuildMode specifies the build mode:
// are we just building things or also installing the results?
type BuildMode int
const (
ModeBuild BuildMode = iota
ModeInstall
)
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 cfg.BuildN {
b.WorkDir = "$WORK"
} else {
b.WorkDir, err = ioutil.TempDir("", "go-build")
if err != nil {
base.Fatalf("%s", err)
}
if cfg.BuildX || cfg.BuildWork {
fmt.Fprintf(os.Stderr, "WORK=%s\n", b.WorkDir)
}
if !cfg.BuildWork {
workdir := b.WorkDir
base.AtExit(func() { os.RemoveAll(workdir) })
}
}
if _, ok := cfg.OSArchSupportsCgo[cfg.Goos+"/"+cfg.Goarch]; !ok && cfg.BuildContext.Compiler == "gc" {
fmt.Fprintf(os.Stderr, "cmd/go: unsupported GOOS/GOARCH pair %s/%s\n", cfg.Goos, cfg.Goarch)
os.Exit(2)
}
for _, tag := range cfg.BuildContext.BuildTags {
if strings.Contains(tag, ",") {
fmt.Fprintf(os.Stderr, "cmd/go: -tags space-separated list contains comma\n")
os.Exit(2)
}
}
}
// NewObjdir returns the name of a fresh object directory under b.WorkDir.
// It is up to the caller to call b.Mkdir on the result at an appropriate time.
// The result ends in a slash, so that file names in that directory
// can be constructed with direct string addition.
//
// NewObjdir must be called only from a single goroutine at a time,
// so it is safe to call during action graph construction, but it must not
// be called during action graph execution.
func (b *Builder) NewObjdir() string {
b.objdirSeq++
return filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)) + string(filepath.Separator)
}
// readpkglist returns the list of packages that were built into the shared library
// at shlibpath. For the native toolchain this list is stored, newline separated, in
// an ELF note with name "Go\x00\x00" and type 1. For GCCGO it is extracted from the
// .go_export section.
func readpkglist(shlibpath string) (pkgs []*load.Package) {
var stk load.ImportStack
if cfg.BuildToolchainName == "gccgo" {
f, _ := elf.Open(shlibpath)
sect := f.Section(".go_export")
data, _ := sect.Data()
scanner := bufio.NewScanner(bytes.NewBuffer(data))
for scanner.Scan() {
t := scanner.Text()
if strings.HasPrefix(t, "pkgpath ") {
t = strings.TrimPrefix(t, "pkgpath ")
t = strings.TrimSuffix(t, ";")
pkgs = append(pkgs, load.LoadPackage(t, &stk))
}
}
} else {
pkglistbytes, err := buildid.ReadELFNote(shlibpath, "Go\x00\x00", 1)
if err != nil {
base.Fatalf("readELFNote failed: %v", err)
}
scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes))
for scanner.Scan() {
t := scanner.Text()
pkgs = append(pkgs, load.LoadPackage(t, &stk))
}
}
return
}
// cacheAction looks up {mode, p} in the cache and returns the resulting action.
// If the cache has no such action, f() is recorded and returned.
func (b *Builder) cacheAction(mode string, p *load.Package, f func() *Action) *Action {
a := b.actionCache[cacheKey{mode, p}]
if a == nil {
a = f()
b.actionCache[cacheKey{mode, p}] = a
}
return a
}
// AutoAction returns the "right" action for go build or go install of p.
func (b *Builder) AutoAction(mode, depMode BuildMode, p *load.Package) *Action {
if p.Name == "main" {
return b.LinkAction(mode, depMode, p)
}
return b.CompileAction(mode, depMode, p)
}
// CompileAction returns the action for compiling and possibly installing
// (according to mode) the given package. The resulting action is only
// for building packages (archives), never for linking executables.
// depMode is the action (build or install) to use when building dependencies.
// To turn package main into an executable, call b.Link instead.
func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action {
if mode == ModeInstall && p.Internal.Local && p.Target == "" {
// Imported via local path. No permanent target.
mode = ModeBuild
}
if mode == ModeInstall && p.Name == "main" {
// We never install the .a file for a main package.
mode = ModeBuild
}
// Construct package build action.
a := b.cacheAction("build", p, func() *Action {
a := &Action{
Mode: "build",
Package: p,
Func: (*Builder).build,
Objdir: b.NewObjdir(),
}
a.Target = a.Objdir + "_pkg_.a"
a.built = a.Target
for _, p1 := range p.Internal.Imports {
a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1))
}
if p.Standard {
switch p.ImportPath {
case "builtin", "unsafe":
// Fake packages - nothing to build.
a.Mode = "built-in package"
a.Func = nil
return a
}
// gccgo standard library is "fake" too.
if cfg.BuildToolchainName == "gccgo" {
// the target name is needed for cgo.
a.Mode = "gccgo stdlib"
a.Target = p.Target
a.Func = nil
return a
}
}
if !p.Stale && p.Target != "" && p.Name != "main" {
// p.Stale==false implies that p.Target is up-to-date.
// Record target name for use by actions depending on this one.
a.Mode = "use installed"
a.Target = p.Target
a.Func = nil
a.built = a.Target
return a
}
return a
})
// Construct install action.
if mode == ModeInstall {
a = b.installAction(a)
}
return a
}
// LinkAction returns the action for linking p into an executable
// and possibly installing the result (according to mode).
// depMode is the action (build or install) to use when compiling dependencies.
func (b *Builder) LinkAction(mode, depMode BuildMode, p *load.Package) *Action {
// Construct link action.
a := b.cacheAction("link", p, func() *Action {
a := &Action{
Mode: "link",
Package: p,
}
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.Mode = "use installed"
a.Func = nil
a.Target = p.Target
a.built = a.Target
return a
}
a1 := b.CompileAction(ModeBuild, depMode, p)
a.Func = (*Builder).link
a.Deps = []*Action{a1}
a.Objdir = a1.Objdir
// 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.Internal.ExeName != "" {
name = p.Internal.ExeName
} else if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" && p.Target != "" {
// On OS X, the linker output name gets recorded in the
// shared library's LC_ID_DYLIB load command.
// The code invoking the linker knows to pass only the final
// path element. Arrange that the path element matches what
// we'll install it as; otherwise the library is only loadable as "a.out".
// On Windows, DLL file name is recorded in PE file
// export section, so do like on OS X.
_, name = filepath.Split(p.Target)
}
a.Target = a.Objdir + filepath.Join("exe", name) + cfg.ExeSuffix
a.built = a.Target
b.addTransitiveLinkDeps(a, a1, "")
return a
})
if mode == ModeInstall {
a = b.installAction(a)
}
return a
}
// installAction returns the action for installing the result of a1.
func (b *Builder) installAction(a1 *Action) *Action {
// If there's no actual action to build a1,
// there's nothing to install either.
// This happens if a1 corresponds to reusing an already-built object.
if a1.Func == nil {
return a1
}
p := a1.Package
return b.cacheAction(a1.Mode+"-install", p, func() *Action {
a := &Action{
Mode: a1.Mode + "-install",
Func: BuildInstallFunc,
Package: p,
Objdir: a1.Objdir,
Deps: []*Action{a1},
Target: p.Target,
built: p.Target,
}
b.addInstallHeaderAction(a)
return a
})
}
// addTransitiveLinkDeps adds to the link action a all packages
// that are transitive dependencies of a1.Deps.
// That is, if a is a link of package main, a1 is the compile of package main
// and a1.Deps is the actions for building packages directly imported by
// package main (what the compiler needs). The linker needs all packages
// transitively imported by the whole program; addTransitiveLinkDeps
// makes sure those are present in a.Deps.
// If shlib is non-empty, then a corresponds to the build and installation of shlib,
// so any rebuild of shlib should not be added as a dependency.
func (b *Builder) addTransitiveLinkDeps(a, a1 *Action, shlib string) {
// Expand Deps to include all built packages, for the linker.
// Use breadth-first search to find rebuilt-for-test packages
// before the standard ones.
// TODO(rsc): Eliminate the standard ones from the action graph,
// which will require doing a little bit more rebuilding.
workq := []*Action{a1}
haveDep := map[string]bool{}
if a1.Package != nil {
haveDep[a1.Package.ImportPath] = true
}
for i := 0; i < len(workq); i++ {
a1 := workq[i]
for _, a2 := range a1.Deps {
// TODO(rsc): Find a better discriminator than the Mode strings, once the dust settles.
if a2.Package == nil || (a2.Mode != "build-install" && a2.Mode != "build" && a2.Mode != "use installed") || haveDep[a2.Package.ImportPath] {
continue
}
haveDep[a2.Package.ImportPath] = true
a.Deps = append(a.Deps, a2)
if a2.Mode == "build-install" {
a2 = a2.Deps[0] // walk children of "build" action
}
workq = append(workq, a2)
}
}
// If this is go build -linkshared, then the link depends on the shared libraries
// in addition to the packages themselves. (The compile steps do not.)
if cfg.BuildLinkshared {
haveShlib := map[string]bool{shlib: true}
for _, a1 := range a.Deps {
p1 := a1.Package
if p1 == nil || p1.Shlib == "" || haveShlib[filepath.Base(p1.Shlib)] {
continue
}
haveShlib[filepath.Base(p1.Shlib)] = true
// TODO(rsc): The use of ModeInstall here is suspect, but if we only do ModeBuild,
// we'll end up building an overall library or executable that depends at runtime
// on other libraries that are out-of-date, which is clearly not good either.
a.Deps = append(a.Deps, b.linkSharedAction(ModeInstall, ModeInstall, p1.Shlib, nil))
}
}
}
// addInstallHeaderAction adds an install header action to a, if needed.
// The action a should be an install action as generated by either
// b.CompileAction or b.LinkAction with mode=ModeInstall,
// and so a.Deps[0] is the corresponding build action.
func (b *Builder) addInstallHeaderAction(a *Action) {
// Install header for cgo in c-archive and c-shared modes.
p := a.Package
if p.UsesCgo() && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {
hdrTarget := a.Target[:len(a.Target)-len(filepath.Ext(a.Target))] + ".h"
if cfg.BuildContext.Compiler == "gccgo" {
// For the header file, remove the "lib"
// added by go/build, so we generate pkg.h
// rather than libpkg.h.
dir, file := filepath.Split(hdrTarget)
file = strings.TrimPrefix(file, "lib")
hdrTarget = filepath.Join(dir, file)
}
ah := &Action{
Mode: "install header",
Package: a.Package,
Deps: []*Action{a.Deps[0]},
Func: (*Builder).installHeader,
Objdir: a.Deps[0].Objdir,
Target: hdrTarget,
}
a.Deps = append(a.Deps, ah)
}
}
// buildmodeShared takes the "go build" action a1 into the building of a shared library of a1.Deps.
// That is, the input a1 represents "go build pkgs" and the result represents "go build -buidmode=shared pkgs".
func (b *Builder) buildmodeShared(mode, depMode BuildMode, args []string, pkgs []*load.Package, a1 *Action) *Action {
name, err := libname(args, pkgs)
if err != nil {
base.Fatalf("%v", err)
}
return b.linkSharedAction(mode, depMode, name, a1)
}
// linkSharedAction takes a grouping action a1 corresponding to a list of built packages
// and returns an action that links them together into a shared library with the name shlib.
// If a1 is nil, shlib should be an absolute path to an existing shared library,
// and then linkSharedAction reads that library to find out the package list.
func (b *Builder) linkSharedAction(mode, depMode BuildMode, shlib string, a1 *Action) *Action {
fullShlib := shlib
shlib = filepath.Base(shlib)
a := b.cacheAction("build-shlib "+shlib, nil, func() *Action {
if a1 == nil {
// TODO(rsc): Need to find some other place to store config,
// not in pkg directory. See golang.org/issue/22196.
pkgs := readpkglist(fullShlib)
a1 = &Action{
Mode: "shlib packages",
}
for _, p := range pkgs {
a1.Deps = append(a1.Deps, b.CompileAction(mode, depMode, p))
}
}
// Add implicit dependencies to pkgs list.
// Currently buildmode=shared forces external linking mode, and
// external linking mode forces an import of runtime/cgo (and
// math on arm). So if it was not passed on the command line and
// it is not present in another shared library, add it here.
// TODO(rsc): Maybe this should only happen if "runtime" is in the original package set.
// TODO(rsc): This should probably be changed to use load.LinkerDeps(p).
// TODO(rsc): Find out and explain here why gccgo is excluded.
// If the answer is that gccgo is different in implicit linker deps, maybe
// load.LinkerDeps should be used and updated.
if cfg.BuildToolchainName != "gccgo" {
add := func(pkg string) {
for _, a2 := range a1.Deps {
if a2.Package.ImportPath == pkg {
return
}
}
var stk load.ImportStack
p := load.LoadPackage(pkg, &stk)
if p.Error != nil {
base.Fatalf("load %s: %v", pkg, p.Error)
}
load.ComputeStale(p)
// Assume that if pkg (runtime/cgo or math)
// is already accounted for in a different shared library,
// then that shared library also contains runtime,
// so that anything we do will depend on that library,
// so we don't need to include pkg in our shared library.
if p.Shlib == "" || filepath.Base(p.Shlib) == pkg {
a1.Deps = append(a1.Deps, b.CompileAction(depMode, depMode, p))
}
}
add("runtime/cgo")
if cfg.Goarch == "arm" {
add("math")
}
}
// Determine the eventual install target and compute staleness.
// TODO(rsc): This doesn't belong here and should be with the
// other staleness code. When we move to content-based staleness
// determination, that will happen for us.
// The install target is root/pkg/shlib, where root is the source root
// in which all the packages lie.
// TODO(rsc): Perhaps this cross-root check should apply to the full
// transitive package dependency list, not just the ones named
// on the command line?
pkgDir := a1.Deps[0].Package.Internal.Build.PkgTargetRoot
for _, a2 := range a1.Deps {
if dir := a2.Package.Internal.Build.PkgTargetRoot; dir != pkgDir {
// TODO(rsc): Misuse of base.Fatalf?
base.Fatalf("installing shared library: cannot use packages %s and %s from different roots %s and %s",
a1.Deps[0].Package.ImportPath,
a2.Package.ImportPath,
pkgDir,
dir)
}
}
// TODO(rsc): Find out and explain here why gccgo is different.
if cfg.BuildToolchainName == "gccgo" {
pkgDir = filepath.Join(pkgDir, "shlibs")
}
target := filepath.Join(pkgDir, shlib)
// The install target is stale if it doesn't exist or if it is older than
// any of the .a files that are written into it.
// TODO(rsc): This computation does not detect packages that
// have been removed from a wildcard used to construct the package list
// but are still present in the installed list.
// It would be possible to detect this by reading the pkg list
// out of any installed target, but content-based staleness
// determination should discover that too.
var built time.Time
if fi, err := os.Stat(target); err == nil {
built = fi.ModTime()
}
stale := cfg.BuildA
if !stale {
for _, a2 := range a1.Deps {
if a2.Target == "" {
continue
}
if a2.Func != nil {
// a2 is going to be rebuilt (reuse of existing target would have Func==nil).
stale = true
break
}
info, err := os.Stat(a2.Target)
if err != nil || info.ModTime().After(built) {
stale = true
break
}
}
}
if !stale {
return &Action{
Mode: "use installed buildmode=shared",
Target: target,
Deps: []*Action{a1},
}
}
// Link packages into a shared library.
a := &Action{
Mode: "go build -buildmode=shared",
Objdir: b.NewObjdir(),
Func: (*Builder).linkShared,
Deps: []*Action{a1},
Args: []string{target}, // awful side-channel for install action
}
a.Target = filepath.Join(a.Objdir, shlib)
b.addTransitiveLinkDeps(a, a1, shlib)
return a
})
// Install result.
if mode == ModeInstall && a.Func != nil {
buildAction := a
a = b.cacheAction("install-shlib "+shlib, nil, func() *Action {
a := &Action{
Mode: "go install -buildmode=shared",
Objdir: buildAction.Objdir,
Func: BuildInstallFunc,
Deps: []*Action{buildAction},
Target: buildAction.Args[0],
}
for _, a2 := range buildAction.Deps[0].Deps {
p := a2.Package
if p.Target == "" {
continue
}
a.Deps = append(a.Deps, &Action{
Mode: "shlibname",
Package: p,
Func: (*Builder).installShlibname,
Target: strings.TrimSuffix(p.Target, ".a") + ".shlibname",
Deps: []*Action{a.Deps[0]},
})
}
return a
})
}
return a
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,481 @@
// 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 work
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"crypto/sha1"
)
// The Go toolchain.
type gcToolchain struct{}
func (gcToolchain) compiler() string {
return base.Tool("compile")
}
func (gcToolchain) linker() string {
return base.Tool("link")
}
func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
p := a.Package
objdir := a.Objdir
if archive != "" {
ofile = archive
} else {
out := "_go_.o"
ofile = objdir + out
}
pkgpath := p.ImportPath
if cfg.BuildBuildmode == "plugin" {
pkgpath = pluginPath(p)
} else if p.Name == "main" {
pkgpath = "main"
}
gcargs := []string{"-p", pkgpath}
if p.Standard {
gcargs = append(gcargs, "-std")
}
compilingRuntime := p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal"))
if compilingRuntime {
// runtime compiles with a special gc 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.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
if p.Standard {
switch p.ImportPath {
case "bytes", "internal/poll", "net", "os", "runtime/pprof", "sync", "syscall", "time":
extFiles++
}
}
if extFiles == 0 {
gcargs = append(gcargs, "-complete")
}
if cfg.BuildContext.InstallSuffix != "" {
gcargs = append(gcargs, "-installsuffix", cfg.BuildContext.InstallSuffix)
}
if a.buildID != "" {
gcargs = append(gcargs, "-buildid", a.buildID)
}
platform := cfg.Goos + "/" + cfg.Goarch
if p.Internal.OmitDebug || platform == "nacl/amd64p32" || platform == "darwin/arm" || platform == "darwin/arm64" || cfg.Goos == "plan9" {
gcargs = append(gcargs, "-dwarf=false")
}
gcflags := buildGcflags
if compilingRuntime {
// Remove -N, if present.
// It is not possible to build the runtime with no optimizations,
// because the compiler cannot eliminate enough write barriers.
gcflags = make([]string, len(buildGcflags))
copy(gcflags, buildGcflags)
for i := 0; i < len(gcflags); i++ {
if gcflags[i] == "-N" {
copy(gcflags[i:], gcflags[i+1:])
gcflags = gcflags[:len(gcflags)-1]
i--
}
}
}
args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", b.WorkDir, gcflags, gcargs, "-D", p.Internal.LocalPrefix}
if importcfg != nil {
if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
return "", nil, err
}
args = append(args, "-importcfg", objdir+"importcfg")
}
if ofile == archive {
args = append(args, "-pack")
}
if asmhdr {
args = append(args, "-asmhdr", objdir+"go_asm.h")
}
// Add -c=N to use concurrent backend compilation, if possible.
if c := gcBackendConcurrency(gcflags); c > 1 {
args = append(args, fmt.Sprintf("-c=%d", c))
}
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
}
// gcBackendConcurrency returns the backend compiler concurrency level for a package compilation.
func gcBackendConcurrency(gcflags []string) int {
// First, check whether we can use -c at all for this compilation.
canDashC := concurrentGCBackendCompilationEnabledByDefault
switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
case "0":
canDashC = false
case "1":
canDashC = true
case "":
// Not set. Use default.
default:
log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
}
if os.Getenv("GOEXPERIMENT") != "" {
// Concurrent compilation is presumed incompatible with GOEXPERIMENTs.
canDashC = false
}
CheckFlags:
for _, flag := range gcflags {
// Concurrent compilation is presumed incompatible with any gcflags,
// except for a small whitelist of commonly used flags.
// If the user knows better, they can manually add their own -c to the gcflags.
switch flag {
case "-N", "-l", "-S", "-B", "-C", "-I":
// OK
default:
canDashC = false
break CheckFlags
}
}
if !canDashC {
return 1
}
// Decide how many concurrent backend compilations to allow.
//
// If we allow too many, in theory we might end up with p concurrent processes,
// each with c concurrent backend compiles, all fighting over the same resources.
// However, in practice, that seems not to happen too much.
// Most build graphs are surprisingly serial, so p==1 for much of the build.
// Furthermore, concurrent backend compilation is only enabled for a part
// of the overall compiler execution, so c==1 for much of the build.
// So don't worry too much about that interaction for now.
//
// However, in practice, setting c above 4 tends not to help very much.
// See the analysis in CL 41192.
//
// TODO(josharian): attempt to detect whether this particular compilation
// is likely to be a bottleneck, e.g. when:
// - it has no successor packages to compile (usually package main)
// - all paths through the build graph pass through it
// - critical path scheduling says it is high priority
// and in such a case, set c to runtime.NumCPU.
// We do this now when p==1.
if cfg.BuildP == 1 {
// No process parallelism. Max out c.
return runtime.NumCPU()
}
// Some process parallelism. Set c to min(4, numcpu).
c := 4
if ncpu := runtime.NumCPU(); ncpu < c {
c = ncpu
}
return c
}
func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
p := a.Package
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(cfg.GOROOT, "pkg", "include")
args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-trimpath", b.WorkDir, "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, buildAsmflags}
if p.ImportPath == "runtime" && cfg.Goarch == "386" {
for _, arg := range buildAsmflags {
if arg == "-dynlink" {
args = append(args, "-D=GOBUILDMODE_shared=1")
}
}
}
var ofiles []string
for _, sfile := range sfiles {
ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
ofiles = append(ofiles, ofile)
a := append(args, "-o", ofile, mkAbs(p.Dir, sfile))
if err := b.run(p.Dir, p.ImportPath, nil, a...); err != nil {
return nil, err
}
}
return ofiles, nil
}
// toolVerify checks that the command line args writes the same output file
// if run using newTool instead.
// Unused now but kept around for future use.
func toolVerify(b *Builder, p *load.Package, newTool string, ofile string, args []interface{}) error {
newArgs := make([]interface{}, len(args))
copy(newArgs, args)
newArgs[1] = base.Tool(newTool)
newArgs[3] = ofile + ".new" // x.6 becomes x.6.new
if err := b.run(p.Dir, p.ImportPath, nil, newArgs...); err != nil {
return err
}
data1, err := ioutil.ReadFile(ofile)
if err != nil {
return err
}
data2, err := ioutil.ReadFile(ofile + ".new")
if err != nil {
return err
}
if !bytes.Equal(data1, data2) {
return fmt.Errorf("%s and %s produced different output files:\n%s\n%s", filepath.Base(args[1].(string)), newTool, strings.Join(str.StringList(args...), " "), strings.Join(str.StringList(newArgs...), " "))
}
os.Remove(ofile + ".new")
return nil
}
func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
var absOfiles []string
for _, f := range ofiles {
absOfiles = append(absOfiles, mkAbs(a.Objdir, f))
}
absAfile := mkAbs(a.Objdir, afile)
// The archive file should have been created by the compiler.
// Since it used to not work that way, verify.
if !cfg.BuildN {
if _, err := os.Stat(absAfile); err != nil {
base.Fatalf("os.Stat of archive file failed: %v", err)
}
}
p := a.Package
if cfg.BuildN || cfg.BuildX {
cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles)
b.Showcmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
}
if cfg.BuildN {
return nil
}
if err := packInternal(b, absAfile, absOfiles); err != nil {
b.showOutput(p.Dir, p.ImportPath, err.Error()+"\n")
return errPrintedOutput
}
return nil
}
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()
}
// setextld sets the appropriate linker flags for the specified compiler.
func setextld(ldflags []string, compiler []string) []string {
for _, f := range ldflags {
if f == "-extld" || strings.HasPrefix(f, "-extld=") {
// don't override -extld if supplied
return ldflags
}
}
ldflags = append(ldflags, "-extld="+compiler[0])
if len(compiler) > 1 {
extldflags := false
add := strings.Join(compiler[1:], " ")
for i, f := range ldflags {
if f == "-extldflags" && i+1 < len(ldflags) {
ldflags[i+1] = add + " " + ldflags[i+1]
extldflags = true
break
} else if strings.HasPrefix(f, "-extldflags=") {
ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):]
extldflags = true
break
}
}
if !extldflags {
ldflags = append(ldflags, "-extldflags="+add)
}
}
return ldflags
}
// pluginPath computes the package path for a plugin main package.
//
// This is typically the import path of the main package p, unless the
// plugin is being built directly from source files. In that case we
// combine the package build ID with the contents of the main package
// source files. This allows us to identify two different plugins
// built from two source files with the same name.
func pluginPath(p *load.Package) string {
if p.ImportPath != "command-line-arguments" {
return p.ImportPath
}
h := sha1.New()
fmt.Fprintf(h, "build ID: %s\n", p.Internal.BuildID)
for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) {
data, err := ioutil.ReadFile(filepath.Join(p.Dir, file))
if err != nil {
base.Fatalf("go: %s", err)
}
h.Write(data)
}
return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil))
}
func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
for _, a := range root.Deps {
if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
cxx = true
}
}
var ldflags []string
if cfg.BuildContext.InstallSuffix != "" {
ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix)
}
if root.Package.Internal.OmitDebug {
ldflags = append(ldflags, "-s", "-w")
}
if cfg.BuildBuildmode == "plugin" {
ldflags = append(ldflags, "-pluginpath", pluginPath(root.Package))
}
// TODO(rsc): This is probably wrong - see golang.org/issue/22155.
if cfg.GOROOT != runtime.GOROOT() {
ldflags = append(ldflags, "-X=runtime/internal/sys.DefaultGoroot="+cfg.GOROOT)
}
// Store BuildID inside toolchain binaries as a unique identifier of the
// tool being run, for use by content-based staleness determination.
if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {
ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
}
// If the user has not specified the -extld option, then specify the
// appropriate linker. In case of C++ code, use the compiler named
// by the CXX environment variable or defaultCXX if CXX is not set.
// Else, use the CC environment variable and defaultCC as fallback.
var compiler []string
if cxx {
compiler = envList("CXX", cfg.DefaultCXX)
} else {
compiler = envList("CC", cfg.DefaultCC)
}
ldflags = append(ldflags, "-buildmode="+ldBuildmode)
if root.buildID != "" {
ldflags = append(ldflags, "-buildid="+root.buildID)
}
ldflags = append(ldflags, cfg.BuildLdflags...)
ldflags = setextld(ldflags, compiler)
// On OS X when using external linking to build a shared library,
// the argument passed here to -o ends up recorded in the final
// shared library in the LC_ID_DYLIB load command.
// To avoid putting the temporary output directory name there
// (and making the resulting shared library useless),
// run the link in the output directory so that -o can name
// just the final path element.
// On Windows, DLL file name is recorded in PE file
// export section, so do like on OS X.
dir := "."
if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" {
dir, out = filepath.Split(out)
}
return b.run(dir, root.Package.ImportPath, nil, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags, mainpkg)
}
func (gcToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
ldflags := []string{"-installsuffix", cfg.BuildContext.InstallSuffix}
ldflags = append(ldflags, "-buildmode=shared")
ldflags = append(ldflags, cfg.BuildLdflags...)
cxx := false
for _, a := range allactions {
if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
cxx = true
}
}
// If the user has not specified the -extld option, then specify the
// appropriate linker. In case of C++ code, use the compiler named
// by the CXX environment variable or defaultCXX if CXX is not set.
// Else, use the CC environment variable and defaultCC as fallback.
var compiler []string
if cxx {
compiler = envList("CXX", cfg.DefaultCXX)
} else {
compiler = envList("CC", cfg.DefaultCC)
}
ldflags = setextld(ldflags, compiler)
for _, d := range toplevelactions {
if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries
continue
}
ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
}
return b.run(".", out, nil, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags)
}
func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile))
}

View File

@ -0,0 +1,497 @@
// 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 work
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
)
// The Gccgo toolchain.
type gccgoToolchain struct{}
var GccgoName, GccgoBin string
var gccgoErr error
func init() {
GccgoName = os.Getenv("GCCGO")
if GccgoName == "" {
GccgoName = "gccgo"
}
GccgoBin, gccgoErr = exec.LookPath(GccgoName)
}
func (gccgoToolchain) compiler() string {
checkGccgoBin()
return GccgoBin
}
func (gccgoToolchain) linker() string {
checkGccgoBin()
return GccgoBin
}
func checkGccgoBin() {
if gccgoErr == nil {
return
}
fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
os.Exit(2)
}
func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
p := a.Package
objdir := a.Objdir
out := "_go_.o"
ofile = objdir + out
gcargs := []string{"-g"}
gcargs = append(gcargs, b.gccArchArgs()...)
if pkgpath := gccgoPkgpath(p); pkgpath != "" {
gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
}
if p.Internal.LocalPrefix != "" {
gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
}
args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile)
if importcfg != nil {
if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
return "", nil, err
}
args = append(args, "-fgo-importcfg="+objdir+"importcfg")
} else {
root := objdir + "_importcfgroot_"
if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
return "", nil, err
}
args = append(args, "-I", root)
}
}
args = append(args, 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
}
// buildImportcfgSymlinks builds in root a tree of symlinks
// implementing the directives from importcfg.
// This serves as a temporary transition mechanism until
// we can depend on gccgo reading an importcfg directly.
// (The Go 1.9 and later gc compilers already do.)
func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
for lineNum, line := range strings.Split(string(importcfg), "\n") {
lineNum++ // 1-based
line = strings.TrimSpace(line)
if line == "" {
continue
}
if line == "" || strings.HasPrefix(line, "#") {
continue
}
var verb, args string
if i := strings.Index(line, " "); i < 0 {
verb = line
} else {
verb, args = line[:i], strings.TrimSpace(line[i+1:])
}
var before, after string
if i := strings.Index(args, "="); i >= 0 {
before, after = args[:i], args[i+1:]
}
switch verb {
default:
base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
case "packagefile":
if before == "" || after == "" {
return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
}
archive := gccgoArchive(root, before)
if err := b.Mkdir(filepath.Dir(archive)); err != nil {
return err
}
if err := b.Symlink(after, archive); err != nil {
return err
}
case "importmap":
if before == "" || after == "" {
return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
}
beforeA := gccgoArchive(root, before)
afterA := gccgoArchive(root, after)
if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
return err
}
if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
return err
}
if err := b.Symlink(afterA, beforeA); err != nil {
return err
}
case "packageshlib":
return fmt.Errorf("gccgo -importcfg does not support shared libraries")
}
}
return nil
}
func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
p := a.Package
var ofiles []string
for _, sfile := range sfiles {
ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
ofiles = append(ofiles, ofile)
sfile = mkAbs(p.Dir, sfile)
defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
}
defs = tools.maybePIC(defs)
defs = append(defs, b.gccArchArgs()...)
err := b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
if err != nil {
return nil, err
}
}
return ofiles, nil
}
func gccgoArchive(basedir, imp string) string {
end := filepath.FromSlash(imp + ".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, a *Action, afile string, ofiles []string) error {
p := a.Package
objdir := a.Objdir
var absOfiles []string
for _, f := range ofiles {
absOfiles = append(absOfiles, mkAbs(objdir, f))
}
return b.run(p.Dir, p.ImportPath, nil, "ar", "rc", mkAbs(objdir, afile), absOfiles)
}
func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
// gccgo needs explicit linking with all package dependencies,
// and all LDFLAGS from cgo dependencies.
apackagePathsSeen := make(map[string]bool)
afiles := []string{}
shlibs := []string{}
ldflags := b.gccArchArgs()
cgoldflags := []string{}
usesCgo := false
cxx := false
objc := false
fortran := false
if root.Package != nil {
cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
objc = len(root.Package.MFiles) > 0
fortran = len(root.Package.FFiles) > 0
}
readCgoFlags := func(flagsFile string) error {
flags, err := ioutil.ReadFile(flagsFile)
if err != nil {
return err
}
const ldflagsPrefix = "_CGO_LDFLAGS="
for _, line := range strings.Split(string(flags), "\n") {
if strings.HasPrefix(line, ldflagsPrefix) {
newFlags := strings.Fields(line[len(ldflagsPrefix):])
for _, flag := range newFlags {
// Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
// but they don't mean anything to the linker so filter
// them out.
if flag != "-g" && !strings.HasPrefix(flag, "-O") {
cgoldflags = append(cgoldflags, flag)
}
}
}
}
return nil
}
newID := 0
readAndRemoveCgoFlags := func(archive string) (string, error) {
newID++
newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
if err := b.copyFile(root, newArchive, archive, 0666, false); err != nil {
return "", err
}
if cfg.BuildN || cfg.BuildX {
b.Showcmd("", "ar d %s _cgo_flags", newArchive)
if cfg.BuildN {
// TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
// Either the archive is already built and we can read them out,
// or we're printing commands to build the archive and can
// forward the _cgo_flags directly to this step.
return "", nil
}
}
err := b.run(root.Objdir, desc, nil, "ar", "x", newArchive, "_cgo_flags")
if err != nil {
return "", err
}
err = b.run(".", desc, nil, "ar", "d", newArchive, "_cgo_flags")
if err != nil {
return "", err
}
err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
if err != nil {
return "", err
}
return newArchive, nil
}
actionsSeen := make(map[*Action]bool)
// Make a pre-order depth-first traversal of the action graph, taking note of
// whether a shared library action has been seen on the way to an action (the
// construction of the graph means that if any path to a node passes through
// a shared library action, they all do).
var walk func(a *Action, seenShlib bool)
var err error
walk = func(a *Action, seenShlib bool) {
if actionsSeen[a] {
return
}
actionsSeen[a] = true
if a.Package != nil && !seenShlib {
if a.Package.Standard {
return
}
// We record the target of the first time we see a .a file
// for a package to make sure that we prefer the 'install'
// rather than the 'build' location (which may not exist any
// more). We still need to traverse the dependencies of the
// build action though so saying
// if apackagePathsSeen[a.Package.ImportPath] { return }
// doesn't work.
if !apackagePathsSeen[a.Package.ImportPath] {
apackagePathsSeen[a.Package.ImportPath] = true
target := a.Target
if len(a.Package.CgoFiles) > 0 || a.Package.UsesSwig() {
target, err = readAndRemoveCgoFlags(target)
if err != nil {
return
}
}
afiles = append(afiles, target)
}
}
if strings.HasSuffix(a.Target, ".so") {
shlibs = append(shlibs, a.Target)
seenShlib = true
}
for _, a1 := range a.Deps {
walk(a1, seenShlib)
if err != nil {
return
}
}
}
for _, a1 := range root.Deps {
walk(a1, false)
if err != nil {
return err
}
}
for _, a := range allactions {
// Gather CgoLDFLAGS, but not from standard packages.
// The go tool can dig up runtime/cgo from GOROOT and
// think that it should use its CgoLDFLAGS, but gccgo
// doesn't use runtime/cgo.
if a.Package == nil {
continue
}
if !a.Package.Standard {
cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...)
}
if len(a.Package.CgoFiles) > 0 {
usesCgo = true
}
if a.Package.UsesSwig() {
usesCgo = true
}
if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
cxx = true
}
if len(a.Package.MFiles) > 0 {
objc = true
}
if len(a.Package.FFiles) > 0 {
fortran = true
}
}
ldflags = append(ldflags, "-Wl,--whole-archive")
ldflags = append(ldflags, afiles...)
ldflags = append(ldflags, "-Wl,--no-whole-archive")
ldflags = append(ldflags, cgoldflags...)
ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
if root.Package != nil {
ldflags = append(ldflags, root.Package.CgoLDFLAGS...)
}
ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
for _, shlib := range shlibs {
ldflags = append(
ldflags,
"-L"+filepath.Dir(shlib),
"-Wl,-rpath="+filepath.Dir(shlib),
"-l"+strings.TrimSuffix(
strings.TrimPrefix(filepath.Base(shlib), "lib"),
".so"))
}
var realOut string
switch buildmode {
case "exe":
if usesCgo && cfg.Goos == "linux" {
ldflags = append(ldflags, "-Wl,-E")
}
case "c-archive":
// Link the Go files into a single .o, and also link
// in -lgolibbegin.
//
// We need to use --whole-archive with -lgolibbegin
// because it doesn't define any symbols that will
// cause the contents to be pulled in; it's just
// initialization code.
//
// The user remains responsible for linking against
// -lgo -lpthread -lm in the final link. We can't use
// -r to pick them up because we can't combine
// split-stack and non-split-stack code in a single -r
// link, and libgo picks up non-split-stack code from
// libffi.
ldflags = append(ldflags, "-Wl,-r", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive")
if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
ldflags = append(ldflags, nopie)
}
// We are creating an object file, so we don't want a build ID.
ldflags = b.disableBuildID(ldflags)
realOut = out
out = out + ".o"
case "c-shared":
ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
case "shared":
ldflags = append(ldflags, "-zdefs", "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
default:
base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
}
switch buildmode {
case "exe", "c-shared":
if cxx {
ldflags = append(ldflags, "-lstdc++")
}
if objc {
ldflags = append(ldflags, "-lobjc")
}
if fortran {
fc := os.Getenv("FC")
if fc == "" {
fc = "gfortran"
}
// support gfortran out of the box and let others pass the correct link options
// via CGO_LDFLAGS
if strings.Contains(fc, "gfortran") {
ldflags = append(ldflags, "-lgfortran")
}
}
}
if err := b.run(".", desc, nil, tools.linker(), "-o", out, ldflags, buildGccgoflags); err != nil {
return err
}
switch buildmode {
case "c-archive":
if err := b.run(".", desc, nil, "ar", "rc", realOut, out); err != nil {
return err
}
}
return nil
}
func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
}
func (tools gccgoToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
fakeRoot := &Action{Mode: "gccgo ldshared"}
fakeRoot.Deps = toplevelactions
return tools.link(b, fakeRoot, out, importcfg, allactions, "shared", out)
}
func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
p := a.Package
inc := filepath.Join(cfg.GOROOT, "pkg", "include")
cfile = mkAbs(p.Dir, cfile)
defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
defs = append(defs, b.gccArchArgs()...)
if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
}
switch cfg.Goarch {
case "386", "amd64":
defs = append(defs, "-fsplit-stack")
}
defs = tools.maybePIC(defs)
return b.run(p.Dir, p.ImportPath, nil, envList("CC", cfg.DefaultCC), "-Wall", "-g",
"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
}
// maybePIC adds -fPIC to the list of arguments if needed.
func (tools gccgoToolchain) maybePIC(args []string) []string {
switch cfg.BuildBuildmode {
case "c-shared", "shared", "plugin":
args = append(args, "-fPIC")
}
return args
}
func gccgoPkgpath(p *load.Package) string {
if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
return ""
}
return p.ImportPath
}
func gccgoCleanPkgpath(p *load.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))
}