Commit baf399b0 authored by Russ Cox's avatar Russ Cox

cmd/go: add go list -cgo and -export

We want package analysis tools to be able to ask cmd/go for
cgo-translated files and for the names of files holding export
type information, instead of reinventing that logic themselves.
Allow them to do so, with the new list -cgo and -export flags.

Change-Id: I860df530d8dcc130f1f328413381b5664cc81c3b
Reviewed-on: https://go-review.googlesource.com/108156
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent 5f2b4f26
...@@ -579,7 +579,7 @@ ...@@ -579,7 +579,7 @@
// //
// Usage: // Usage:
// //
// go list [-deps] [-e] [-f format] [-json] [-test] [build flags] [packages] // go list [-cgo] [-deps] [-e] [-f format] [-json] [-list] [-test] [build flags] [packages]
// //
// List lists the packages named by the import paths, one per line. // List lists the packages named by the import paths, one per line.
// //
...@@ -609,6 +609,9 @@ ...@@ -609,6 +609,9 @@
// Root string // Go root or Go path dir containing this package // Root string // Go root or Go path dir containing this package
// ConflictDir string // this directory shadows Dir in $GOPATH // ConflictDir string // this directory shadows Dir in $GOPATH
// BinaryOnly bool // binary-only package: cannot be recompiled from sources // BinaryOnly bool // binary-only package: cannot be recompiled from sources
// ForTest string // package is only for use in named test
// DepOnly bool // package is only a dependency, not explicitly listed
// Export string // file containing export data (when using -export)
// //
// // Source files // // Source files
// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) // GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
...@@ -683,9 +686,15 @@ ...@@ -683,9 +686,15 @@
// The -json flag causes the package data to be printed in JSON format // The -json flag causes the package data to be printed in JSON format
// instead of using the template format. // instead of using the template format.
// //
// The -cgo flag causes list to set CgoFiles not to the original *.go files
// importing "C" but instead to the translated files generated by the cgo
// command.
//
// The -deps flag causes list to iterate over not just the named packages // The -deps flag causes list to iterate over not just the named packages
// but also all their dependencies. It visits them in a depth-first post-order // but also all their dependencies. It visits them in a depth-first post-order
// traversal, so that a package is listed only after all its dependencies. // traversal, so that a package is listed only after all its dependencies.
// Packages not explicitly listed on the command line will have the DepOnly
// field set to true.
// //
// The -e flag changes the handling of erroneous packages, those that // The -e flag changes the handling of erroneous packages, those that
// cannot be found or are malformed. By default, the list command // cannot be found or are malformed. By default, the list command
...@@ -697,6 +706,10 @@ ...@@ -697,6 +706,10 @@
// a non-nil Error field; other information may or may not be missing // a non-nil Error field; other information may or may not be missing
// (zeroed). // (zeroed).
// //
// The -export flag causes list to set the package's Export field to
// the name of a file containing up-to-date export information for
// the given package.
//
// The -test flag causes list to report not only the named packages // The -test flag causes list to report not only the named packages
// but also their test binaries (for packages with tests), to convey to // but also their test binaries (for packages with tests), to convey to
// source code analysis tools exactly how test binaries are constructed. // source code analysis tools exactly how test binaries are constructed.
...@@ -707,7 +720,18 @@ ...@@ -707,7 +720,18 @@
// package itself). The reported import path of a package recompiled // package itself). The reported import path of a package recompiled
// for a particular test binary is followed by a space and the name of // for a particular test binary is followed by a space and the name of
// the test binary in brackets, as in "math/rand [math/rand.test]" // the test binary in brackets, as in "math/rand [math/rand.test]"
// or "regexp [sort.test]". // or "regexp [sort.test]". The ForTest field is also set to the name
// of the package being tested ("math/rand" or "sort" in the previous
// examples).
//
// The Dir, Target, Shlib, Root, ConflictDir, and Export file paths
// are all absolute paths.
//
// By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir
// (that is, paths relative to Dir, not absolute paths).
// The extra entries added by the -cgo and -test flags are absolute paths
// referring to cached copies of generated Go source files.
// Although they are Go source files, the paths may not end in ".go".
// //
// For more about build flags, see 'go help build'. // For more about build flags, see 'go help build'.
// //
......
...@@ -1977,6 +1977,55 @@ func TestGoListTest(t *testing.T) { ...@@ -1977,6 +1977,55 @@ func TestGoListTest(t *testing.T) {
tg.grepStdout(`^runtime/cgo$`, "missing runtime/cgo") tg.grepStdout(`^runtime/cgo$`, "missing runtime/cgo")
} }
func TestGoListCgo(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOCACHE", tg.tempdir)
tg.run("list", "-f", `{{join .CgoFiles "\n"}}`, "net")
if tg.stdout.String() == "" {
t.Skip("net does not use cgo")
}
if strings.Contains(tg.stdout.String(), tg.tempdir) {
t.Fatalf(".CgoFiles without -cgo unexpectedly mentioned cache %s", tg.tempdir)
}
tg.run("list", "-cgo", "-f", `{{join .CgoFiles "\n"}}`, "net")
if !strings.Contains(tg.stdout.String(), tg.tempdir) {
t.Fatalf(".CgoFiles with -cgo did not mention cache %s", tg.tempdir)
}
for _, file := range strings.Split(tg.stdout.String(), "\n") {
if file == "" {
continue
}
if _, err := os.Stat(file); err != nil {
t.Fatalf("cannot find .CgoFiles result %s: %v", file, err)
}
}
}
func TestGoListExport(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOCACHE", tg.tempdir)
tg.run("list", "-f", "{{.Export}}", "strings")
if tg.stdout.String() != "" {
t.Fatalf(".Export without -export unexpectedly set")
}
tg.run("list", "-export", "-f", "{{.Export}}", "strings")
file := strings.TrimSpace(tg.stdout.String())
if file == "" {
t.Fatalf(".Export with -export was empty")
}
if _, err := os.Stat(file); err != nil {
t.Fatalf("cannot find .Export result %s: %v", file, err)
}
}
// Issue 4096. Validate the output of unsuccessful go install foo/quxx. // Issue 4096. Validate the output of unsuccessful go install foo/quxx.
func TestUnsuccessfulGoInstallShouldMentionMissingPackage(t *testing.T) { func TestUnsuccessfulGoInstallShouldMentionMissingPackage(t *testing.T) {
tg := testgo(t) tg := testgo(t)
......
...@@ -23,7 +23,7 @@ import ( ...@@ -23,7 +23,7 @@ import (
) )
var CmdList = &base.Command{ var CmdList = &base.Command{
UsageLine: "list [-deps] [-e] [-f format] [-json] [-test] [build flags] [packages]", UsageLine: "list [-cgo] [-deps] [-e] [-f format] [-json] [-list] [-test] [build flags] [packages]",
Short: "list packages", Short: "list packages",
Long: ` Long: `
List lists the packages named by the import paths, one per line. List lists the packages named by the import paths, one per line.
...@@ -54,6 +54,9 @@ syntax of package template. The default output is equivalent to -f ...@@ -54,6 +54,9 @@ syntax of package template. The default output is equivalent to -f
Root string // Go root or Go path dir containing this package Root string // Go root or Go path dir containing this package
ConflictDir string // this directory shadows Dir in $GOPATH ConflictDir string // this directory shadows Dir in $GOPATH
BinaryOnly bool // binary-only package: cannot be recompiled from sources BinaryOnly bool // binary-only package: cannot be recompiled from sources
ForTest string // package is only for use in named test
DepOnly bool // package is only a dependency, not explicitly listed
Export string // file containing export data (when using -export)
// Source files // Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
...@@ -128,9 +131,15 @@ for the go/build package's Context type. ...@@ -128,9 +131,15 @@ for the go/build package's Context type.
The -json flag causes the package data to be printed in JSON format The -json flag causes the package data to be printed in JSON format
instead of using the template format. instead of using the template format.
The -cgo flag causes list to set CgoFiles not to the original *.go files
importing "C" but instead to the translated files generated by the cgo
command.
The -deps flag causes list to iterate over not just the named packages The -deps flag causes list to iterate over not just the named packages
but also all their dependencies. It visits them in a depth-first post-order but also all their dependencies. It visits them in a depth-first post-order
traversal, so that a package is listed only after all its dependencies. traversal, so that a package is listed only after all its dependencies.
Packages not explicitly listed on the command line will have the DepOnly
field set to true.
The -e flag changes the handling of erroneous packages, those that The -e flag changes the handling of erroneous packages, those that
cannot be found or are malformed. By default, the list command cannot be found or are malformed. By default, the list command
...@@ -142,6 +151,10 @@ printing. Erroneous packages will have a non-empty ImportPath and ...@@ -142,6 +151,10 @@ printing. Erroneous packages will have a non-empty ImportPath and
a non-nil Error field; other information may or may not be missing a non-nil Error field; other information may or may not be missing
(zeroed). (zeroed).
The -export flag causes list to set the package's Export field to
the name of a file containing up-to-date export information for
the given package.
The -test flag causes list to report not only the named packages The -test flag causes list to report not only the named packages
but also their test binaries (for packages with tests), to convey to but also their test binaries (for packages with tests), to convey to
source code analysis tools exactly how test binaries are constructed. source code analysis tools exactly how test binaries are constructed.
...@@ -152,7 +165,18 @@ dependencies specially for that test (most commonly the tested ...@@ -152,7 +165,18 @@ dependencies specially for that test (most commonly the tested
package itself). The reported import path of a package recompiled package itself). The reported import path of a package recompiled
for a particular test binary is followed by a space and the name of for a particular test binary is followed by a space and the name of
the test binary in brackets, as in "math/rand [math/rand.test]" the test binary in brackets, as in "math/rand [math/rand.test]"
or "regexp [sort.test]". or "regexp [sort.test]". The ForTest field is also set to the name
of the package being tested ("math/rand" or "sort" in the previous
examples).
The Dir, Target, Shlib, Root, ConflictDir, and Export file paths
are all absolute paths.
By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir
(that is, paths relative to Dir, not absolute paths).
The extra entries added by the -cgo and -test flags are absolute paths
referring to cached copies of generated Go source files.
Although they are Go source files, the paths may not end in ".go".
For more about build flags, see 'go help build'. For more about build flags, see 'go help build'.
...@@ -165,8 +189,10 @@ func init() { ...@@ -165,8 +189,10 @@ func init() {
work.AddBuildFlags(CmdList) work.AddBuildFlags(CmdList)
} }
var listCgo = CmdList.Flag.Bool("cgo", false, "")
var listDeps = CmdList.Flag.Bool("deps", false, "") var listDeps = CmdList.Flag.Bool("deps", false, "")
var listE = CmdList.Flag.Bool("e", false, "") var listE = CmdList.Flag.Bool("e", false, "")
var listExport = CmdList.Flag.Bool("export", false, "")
var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "") var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "")
var listJson = CmdList.Flag.Bool("json", false, "") var listJson = CmdList.Flag.Bool("json", false, "")
var listTest = CmdList.Flag.Bool("test", false, "") var listTest = CmdList.Flag.Bool("test", false, "")
...@@ -222,11 +248,22 @@ func runList(cmd *base.Command, args []string) { ...@@ -222,11 +248,22 @@ func runList(cmd *base.Command, args []string) {
pkgs = load.Packages(args) pkgs = load.Packages(args)
} }
if *listTest { if cache.Default() == nil {
c := cache.Default() // These flags return file names pointing into the build cache,
if c == nil { // so the build cache must exist.
if *listCgo {
base.Fatalf("go list -cgo requires build cache")
}
if *listExport {
base.Fatalf("go list -export requires build cache")
}
if *listTest {
base.Fatalf("go list -test requires build cache") base.Fatalf("go list -test requires build cache")
} }
}
if *listTest {
c := cache.Default()
// Add test binaries to packages to be listed. // Add test binaries to packages to be listed.
for _, p := range pkgs { for _, p := range pkgs {
if p.Error != nil { if p.Error != nil {
...@@ -279,13 +316,14 @@ func runList(cmd *base.Command, args []string) { ...@@ -279,13 +316,14 @@ func runList(cmd *base.Command, args []string) {
pkgs = load.PackageList(pkgs) pkgs = load.PackageList(pkgs)
} }
// Estimate whether staleness information is needed, // Do we need to run a build to gather information?
// since it's a little bit of work to compute.
needStale := *listJson || strings.Contains(*listFmt, ".Stale") needStale := *listJson || strings.Contains(*listFmt, ".Stale")
if needStale { if needStale || *listExport || *listCgo {
var b work.Builder var b work.Builder
b.Init() b.Init()
b.ComputeStaleOnly = true b.IsCmdList = true
b.NeedExport = *listExport
b.NeedCgoFiles = *listCgo
a := &work.Action{} a := &work.Action{}
// TODO: Use pkgsFilter? // TODO: Use pkgsFilter?
for _, p := range pkgs { for _, p := range pkgs {
......
...@@ -51,6 +51,7 @@ type PackagePublic struct { ...@@ -51,6 +51,7 @@ type PackagePublic struct {
BinaryOnly bool `json:",omitempty"` // package cannot be recompiled BinaryOnly bool `json:",omitempty"` // package cannot be recompiled
ForTest string `json:",omitempty"` // package is only for use in named test ForTest string `json:",omitempty"` // package is only for use in named test
DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed
Export string `json:",omitempty"` // file containing export data (set by go list -export)
// Stale and StaleReason remain here *only* for the list command. // Stale and StaleReason remain here *only* for the list command.
// They are only initialized in preparation for list execution. // They are only initialized in preparation for list execution.
......
...@@ -36,7 +36,10 @@ type Builder struct { ...@@ -36,7 +36,10 @@ type Builder struct {
flagCache map[[2]string]bool // a cache of supported compiler flags flagCache map[[2]string]bool // a cache of supported compiler flags
Print func(args ...interface{}) (int, error) Print func(args ...interface{}) (int, error)
ComputeStaleOnly bool // compute staleness for go list; no actual build IsCmdList bool // running as part of go list; set p.Stale and additional fields below
NeedError bool // list needs p.Error
NeedExport bool // list needs p.Export
NeedCgoFiles bool // list needs p.CgoFiles to cgo-generated files, not originals
objdirSeq int // counter for NewObjdir objdirSeq int // counter for NewObjdir
pkgSeq int pkgSeq int
......
...@@ -414,7 +414,7 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID ...@@ -414,7 +414,7 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
// already up-to-date, then to avoid a rebuild, report the package // already up-to-date, then to avoid a rebuild, report the package
// as up-to-date as well. See "Build IDs" comment above. // as up-to-date as well. See "Build IDs" comment above.
// TODO(rsc): Rewrite this code to use a TryCache func on the link action. // TODO(rsc): Rewrite this code to use a TryCache func on the link action.
if target != "" && !cfg.BuildA && a.Mode == "build" && len(a.triggers) == 1 && a.triggers[0].Mode == "link" { if target != "" && !cfg.BuildA && !b.NeedExport && a.Mode == "build" && len(a.triggers) == 1 && a.triggers[0].Mode == "link" {
buildID, err := buildid.ReadFile(target) buildID, err := buildid.ReadFile(target)
if err == nil { if err == nil {
id := strings.Split(buildID, buildIDSeparator) id := strings.Split(buildID, buildIDSeparator)
...@@ -455,8 +455,8 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID ...@@ -455,8 +455,8 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
return true return true
} }
if b.ComputeStaleOnly { if b.IsCmdList {
// Invoked during go list only to compute and record staleness. // Invoked during go list to compute and record staleness.
if p := a.Package; p != nil && !p.Stale { if p := a.Package; p != nil && !p.Stale {
p.Stale = true p.Stale = true
if cfg.BuildA { if cfg.BuildA {
...@@ -521,10 +521,6 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID ...@@ -521,10 +521,6 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
a.output = []byte{} a.output = []byte{}
} }
if b.ComputeStaleOnly {
return true
}
return false return false
} }
...@@ -609,11 +605,17 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error { ...@@ -609,11 +605,17 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error {
panic("internal error: a.output not set") panic("internal error: a.output not set")
} }
outputID, _, err := c.Put(a.actionID, r) outputID, _, err := c.Put(a.actionID, r)
r.Close()
if err == nil && cfg.BuildX { if err == nil && cfg.BuildX {
b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cp", target, c.OutputFile(outputID)))) b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cp", target, c.OutputFile(outputID))))
} }
if b.NeedExport {
if err != nil {
return err
}
a.Package.Export = c.OutputFile(outputID)
}
c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output) c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output)
r.Close()
} }
} }
......
...@@ -54,7 +54,7 @@ func actionList(root *Action) []*Action { ...@@ -54,7 +54,7 @@ func actionList(root *Action) []*Action {
// do runs the action graph rooted at root. // do runs the action graph rooted at root.
func (b *Builder) Do(root *Action) { func (b *Builder) Do(root *Action) {
if c := cache.Default(); c != nil && !b.ComputeStaleOnly { if c := cache.Default(); c != nil && !b.IsCmdList {
// If we're doing real work, take time at the end to trim the cache. // If we're doing real work, take time at the end to trim the cache.
defer c.Trim() defer c.Trim()
} }
...@@ -296,11 +296,11 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { ...@@ -296,11 +296,11 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
return h.Sum() return h.Sum()
} }
// needCgoHeader reports whether the actions triggered by this one // needCgoHdr reports whether the actions triggered by this one
// expect to be able to access the cgo-generated header file. // expect to be able to access the cgo-generated header file.
func needCgoHeader(a *Action) bool { func (b *Builder) needCgoHdr(a *Action) bool {
// If this build triggers a header install, run cgo to get the header. // If this build triggers a header install, run cgo to get the header.
if (a.Package.UsesCgo() || a.Package.UsesSwig()) && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") { if !b.IsCmdList && (a.Package.UsesCgo() || a.Package.UsesSwig()) && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {
for _, t1 := range a.triggers { for _, t1 := range a.triggers {
if t1.Mode == "install header" { if t1.Mode == "install header" {
return true return true
...@@ -317,19 +317,54 @@ func needCgoHeader(a *Action) bool { ...@@ -317,19 +317,54 @@ func needCgoHeader(a *Action) bool {
return false return false
} }
const (
needBuild uint32 = 1 << iota
needCgoHdr
needVet
needCgoFiles
needStale
)
// build is the action for building a single package. // build is the action for building a single package.
// Note that any new influence on this logic must be reported in b.buildActionID above as well. // Note that any new influence on this logic must be reported in b.buildActionID above as well.
func (b *Builder) build(a *Action) (err error) { func (b *Builder) build(a *Action) (err error) {
p := a.Package p := a.Package
bit := func(x uint32, b bool) uint32 {
if b {
return x
}
return 0
}
cached := false cached := false
needCgo := needCgoHeader(a) need := bit(needBuild, !b.IsCmdList || b.NeedExport) |
bit(needCgoHdr, b.needCgoHdr(a)) |
bit(needVet, a.needVet) |
bit(needCgoFiles, b.NeedCgoFiles && (p.UsesCgo() || p.UsesSwig()))
// Save p.CgoFiles now, because we may modify it for go list.
cgofiles := append([]string{}, p.CgoFiles...)
if !p.BinaryOnly { if !p.BinaryOnly {
if b.useCache(a, p, b.buildActionID(a), p.Target) { if b.useCache(a, p, b.buildActionID(a), p.Target) {
if b.ComputeStaleOnly || !needCgo && !a.needVet { // We found the main output in the cache.
return nil // If we don't need any other outputs, we can stop.
need &^= needBuild
if b.NeedExport {
p.Export = a.built
} }
if need&needCgoFiles != 0 && b.loadCachedCgoFiles(a) {
need &^= needCgoFiles
}
// Otherwise, we need to write files to a.Objdir (needVet, needCgoHdr).
// Remember that we might have them in cache
// and check again after we create a.Objdir.
cached = true cached = true
a.output = []byte{} // start saving output in case we miss any cache results
}
if need == 0 {
return nil
} }
defer b.flushOutput(a) defer b.flushOutput(a)
} }
...@@ -338,6 +373,9 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -338,6 +373,9 @@ func (b *Builder) build(a *Action) (err error) {
if err != nil && err != errPrintedOutput { if err != nil && err != errPrintedOutput {
err = fmt.Errorf("go build %s: %v", a.Package.ImportPath, err) err = fmt.Errorf("go build %s: %v", a.Package.ImportPath, err)
} }
if err != nil && b.IsCmdList && b.NeedError && p.Error == nil {
p.Error = &load.PackageError{Err: err.Error()}
}
}() }()
if cfg.BuildN { if cfg.BuildN {
// In -n mode, print a banner between packages. // In -n mode, print a banner between packages.
...@@ -357,16 +395,16 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -357,16 +395,16 @@ func (b *Builder) build(a *Action) (err error) {
if err == nil { if err == nil {
a.built = a.Package.Target a.built = a.Package.Target
a.Target = a.Package.Target a.Target = a.Package.Target
if b.NeedExport {
a.Package.Export = a.Package.Target
}
a.buildID = b.fileHash(a.Package.Target) a.buildID = b.fileHash(a.Package.Target)
a.Package.Stale = false a.Package.Stale = false
a.Package.StaleReason = "binary-only package" a.Package.StaleReason = "binary-only package"
return nil return nil
} }
if b.ComputeStaleOnly { a.Package.Stale = true
a.Package.Stale = true a.Package.StaleReason = "missing or invalid binary-only package"
a.Package.StaleReason = "missing or invalid binary-only package"
return nil
}
return fmt.Errorf("missing or invalid binary-only package") return fmt.Errorf("missing or invalid binary-only package")
} }
...@@ -375,8 +413,21 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -375,8 +413,21 @@ func (b *Builder) build(a *Action) (err error) {
} }
objdir := a.Objdir objdir := a.Objdir
if cached && (!needCgo || b.loadCachedCgo(a)) && (!a.needVet || b.loadCachedVet(a)) { if cached {
return nil if need&needCgoHdr != 0 && b.loadCachedCgoHdr(a) {
need &^= needCgoHdr
}
// Load cached vet config, but only if that's all we have left
// (need == needVet, not testing just the one bit).
// If we are going to do a full build anyway,
// we're going to regenerate the files below anyway.
if need == needVet && b.loadCachedVet(a) {
need &^= needVet
}
if need == 0 {
return nil
}
} }
// make target directory // make target directory
...@@ -387,9 +438,8 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -387,9 +438,8 @@ func (b *Builder) build(a *Action) (err error) {
} }
} }
var gofiles, cgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string var gofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
gofiles = append(gofiles, a.Package.GoFiles...) gofiles = append(gofiles, a.Package.GoFiles...)
cgofiles = append(cgofiles, a.Package.CgoFiles...)
cfiles = append(cfiles, a.Package.CFiles...) cfiles = append(cfiles, a.Package.CFiles...)
sfiles = append(sfiles, a.Package.SFiles...) sfiles = append(sfiles, a.Package.SFiles...)
cxxfiles = append(cxxfiles, a.Package.CXXFiles...) cxxfiles = append(cxxfiles, a.Package.CXXFiles...)
...@@ -500,19 +550,27 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -500,19 +550,27 @@ func (b *Builder) build(a *Action) (err error) {
} }
b.cacheGofiles(a, gofiles) b.cacheGofiles(a, gofiles)
// Running cgo generated the cgo header.
need &^= needCgoHdr
// Sanity check only, since Package.load already checked as well. // Sanity check only, since Package.load already checked as well.
if len(gofiles) == 0 { if len(gofiles) == 0 {
return &load.NoGoError{Package: a.Package} return &load.NoGoError{Package: a.Package}
} }
// Prepare Go vet config if needed. // Prepare Go vet config if needed.
if a.needVet { if need&needVet != 0 {
buildVetConfig(a, gofiles) buildVetConfig(a, gofiles)
need &^= needVet
} }
if cached { if need&needCgoFiles != 0 {
// The cached package file is OK, so we don't need to run the compile. if !b.loadCachedCgoFiles(a) {
// We've only gone this far in order to prepare the vet configuration return fmt.Errorf("failed to cache translated CgoFiles")
// or cgo header, and now we have. }
need &^= needCgoFiles
}
if need == 0 {
// Nothing left to do.
return nil return nil
} }
...@@ -656,17 +714,25 @@ func (b *Builder) cacheObjdirFile(a *Action, c *cache.Cache, name string) error ...@@ -656,17 +714,25 @@ func (b *Builder) cacheObjdirFile(a *Action, c *cache.Cache, name string) error
return err return err
} }
func (b *Builder) loadCachedObjdirFile(a *Action, c *cache.Cache, name string) error { func (b *Builder) findCachedObjdirFile(a *Action, c *cache.Cache, name string) (string, error) {
entry, err := c.Get(cache.Subkey(a.actionID, name)) entry, err := c.Get(cache.Subkey(a.actionID, name))
if err != nil { if err != nil {
return err return "", err
} }
out := c.OutputFile(entry.OutputID) out := c.OutputFile(entry.OutputID)
info, err := os.Stat(out) info, err := os.Stat(out)
if err != nil || info.Size() != entry.Size { if err != nil || info.Size() != entry.Size {
return fmt.Errorf("not in cache") return "", fmt.Errorf("not in cache")
}
return out, nil
}
func (b *Builder) loadCachedObjdirFile(a *Action, c *cache.Cache, name string) error {
cached, err := b.findCachedObjdirFile(a, c, name)
if err != nil {
return err
} }
return b.copyFile(a.Objdir+name, out, 0666, true) return b.copyFile(a.Objdir+name, cached, 0666, true)
} }
func (b *Builder) cacheCgoHdr(a *Action) { func (b *Builder) cacheCgoHdr(a *Action) {
...@@ -677,7 +743,7 @@ func (b *Builder) cacheCgoHdr(a *Action) { ...@@ -677,7 +743,7 @@ func (b *Builder) cacheCgoHdr(a *Action) {
b.cacheObjdirFile(a, c, "_cgo_install.h") b.cacheObjdirFile(a, c, "_cgo_install.h")
} }
func (b *Builder) loadCachedCgo(a *Action) bool { func (b *Builder) loadCachedCgoHdr(a *Action) bool {
c := cache.Default() c := cache.Default()
if c == nil { if c == nil {
return false return false
...@@ -737,6 +803,33 @@ func (b *Builder) loadCachedVet(a *Action) bool { ...@@ -737,6 +803,33 @@ func (b *Builder) loadCachedVet(a *Action) bool {
return true return true
} }
func (b *Builder) loadCachedCgoFiles(a *Action) bool {
c := cache.Default()
if c == nil {
return false
}
list, _, err := c.GetBytes(cache.Subkey(a.actionID, "gofiles"))
if err != nil {
return false
}
var files []string
for _, name := range strings.Split(string(list), "\n") {
if name == "" { // end of list
continue
}
if strings.HasPrefix(name, "./") {
continue
}
file, err := b.findCachedObjdirFile(a, c, name)
if err != nil {
return false
}
files = append(files, file)
}
a.Package.CgoFiles = files
return true
}
type vetConfig struct { type vetConfig struct {
Compiler string Compiler string
Dir string Dir string
...@@ -939,7 +1032,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { ...@@ -939,7 +1032,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
// link is the action for linking a single command. // link is the action for linking a single command.
// Note that any new influence on this logic must be reported in b.linkActionID above as well. // Note that any new influence on this logic must be reported in b.linkActionID above as well.
func (b *Builder) link(a *Action) (err error) { func (b *Builder) link(a *Action) (err error) {
if b.useCache(a, a.Package, b.linkActionID(a), a.Package.Target) { if b.useCache(a, a.Package, b.linkActionID(a), a.Package.Target) || b.IsCmdList {
return nil return nil
} }
defer b.flushOutput(a) defer b.flushOutput(a)
...@@ -1172,7 +1265,7 @@ func (b *Builder) linkSharedActionID(a *Action) cache.ActionID { ...@@ -1172,7 +1265,7 @@ func (b *Builder) linkSharedActionID(a *Action) cache.ActionID {
} }
func (b *Builder) linkShared(a *Action) (err error) { func (b *Builder) linkShared(a *Action) (err error) {
if b.useCache(a, nil, b.linkSharedActionID(a), a.Target) { if b.useCache(a, nil, b.linkSharedActionID(a), a.Target) || b.IsCmdList {
return nil return nil
} }
defer b.flushOutput(a) defer b.flushOutput(a)
...@@ -1236,13 +1329,17 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) { ...@@ -1236,13 +1329,17 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
// We want to hide that awful detail as much as possible, so don't // We want to hide that awful detail as much as possible, so don't
// advertise it by touching the mtimes (usually the libraries are up // advertise it by touching the mtimes (usually the libraries are up
// to date). // to date).
if !a.buggyInstall && !b.ComputeStaleOnly { if !a.buggyInstall && !b.IsCmdList {
now := time.Now() now := time.Now()
os.Chtimes(a.Target, now, now) os.Chtimes(a.Target, now, now)
} }
return nil return nil
} }
if b.ComputeStaleOnly {
// If we're building for go list -export,
// never install anything; just keep the cache reference.
if b.IsCmdList {
a.built = a1.built
return nil return nil
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment