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:
parent
1992ab7e65
commit
08362246b6
691
src/cmd/go/internal/work/action.go
Normal file
691
src/cmd/go/internal/work/action.go
Normal 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
1904
src/cmd/go/internal/work/exec.go
Normal file
1904
src/cmd/go/internal/work/exec.go
Normal file
File diff suppressed because it is too large
Load Diff
481
src/cmd/go/internal/work/gc.go
Normal file
481
src/cmd/go/internal/work/gc.go
Normal 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))
|
||||||
|
}
|
497
src/cmd/go/internal/work/gccgo.go
Normal file
497
src/cmd/go/internal/work/gccgo.go
Normal 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))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user