)
var cmdBuild = &Command{
- UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [importpath... | gofiles...]",
+ UsageLine: "build [-a] [-n] [-o output] [-p n] [-v] [-x] [-work] [importpath... | gofiles...]",
Short: "compile packages and dependencies",
Long: `
Build compiles the packages named by the import paths,
The -p flag specifies the number of builds that can be run in parallel.
The default is the number of CPUs available.
+The -work flag causes build to print the name of the temporary work
+directory and not delete it when exiting.
+
For more about import paths, see 'go help importpath'.
See also: go install, go get, go clean.
var buildV bool // -v flag
var buildX bool // -x flag
var buildO = cmdBuild.Flag.String("o", "", "output file")
+var buildWork bool // -work flag
var buildContext = build.DefaultContext
cmd.Flag.IntVar(&buildP, "p", buildP, "")
cmd.Flag.BoolVar(&buildV, "v", false, "")
cmd.Flag.BoolVar(&buildX, "x", false, "")
+ cmd.Flag.BoolVar(&buildWork, "work", false, "")
// TODO(rsc): This -t flag is used by buildscript.sh but
// not documented. Should be documented but the
}
var cmdInstall = &Command{
- UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [importpath...]",
+ UsageLine: "install [-a] [-n] [-p n] [-v] [-x] [-work] [importpath...]",
Short: "compile and install packages and dependencies",
Long: `
Install compiles and installs the packages named by the import paths,
The -p flag specifies the number of builds that can be run in parallel.
The default is the number of CPUs available.
+The -work flag causes build to print the name of the temporary work
+directory and not delete it when exiting.
+
For more about import paths, see 'go help importpath'.
See also: go build, go get, go clean.
if err != nil {
fatalf("%s", err)
}
- if buildX {
+ if buildX || buildWork {
fmt.Printf("WORK=%s\n", b.work)
}
- atexit(func() { os.RemoveAll(b.work) })
+ if !buildWork {
+ atexit(func() { os.RemoveAll(b.work) })
+ }
}
}
ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dir, nil }
pwd, _ := os.Getwd()
var stk importStack
- pkg := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd+"/.", &stk)
+ pkg := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd+"/.", &stk, true)
if pkg.Error != nil {
fatalf("%s", pkg.Error)
}
// garbage down in a large build. On an operating system
// with aggressive buffering, cleaning incrementally like
// this keeps the intermediate objects from hitting the disk.
- defer os.RemoveAll(a1.objdir)
- defer os.Remove(a1.target)
+ if !buildWork {
+ defer os.RemoveAll(a1.objdir)
+ defer os.Remove(a1.target)
+ }
return b.copyFile(a.target, a1.target, perm)
}
}
defer sf.Close()
+ // Be careful about removing/overwriting dst.
+ // Do not remove/overwrite if dst exists and is a directory
+ // or a non-object file.
+ if fi, err := os.Stat(dst); err == nil {
+ if fi.IsDir() {
+ return fmt.Errorf("build output %q already exists and is a directory", dst)
+ }
+ if !isObject(dst) {
+ return fmt.Errorf("build output %q already exists and is not an object file", dst)
+ }
+ }
+
// On Windows, remove lingering ~ file from last attempt.
if toolIsWindows {
if _, err := os.Stat(dst + "~"); err == nil {
return nil
}
+var objectMagic = [][]byte{
+ {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}, // Package archive
+ {'\x7F', 'E', 'L', 'F'}, // ELF
+ {0xFE, 0xED, 0xFA, 0xCE}, // Mach-O big-endian 32-bit
+ {0xFE, 0xED, 0xFA, 0xCF}, // Mach-O big-endian 64-bit
+ {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit
+ {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit
+ {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00}, // PE (Windows) as generated by 6l/8l
+}
+
+func isObject(s string) bool {
+ f, err := os.Open(s)
+ if err != nil {
+ return false
+ }
+ defer f.Close()
+ buf := make([]byte, 64)
+ io.ReadFull(f, buf)
+ for _, magic := range objectMagic {
+ if bytes.HasPrefix(buf, magic) {
+ return true
+ }
+ }
+ return false
+}
+
// fmtcmd formats a command in the manner of fmt.Sprintf but also:
//
// If dir is non-empty and the script is not in dir right now,
import (
"bytes"
+ "fmt"
"go/build"
"go/scanner"
"os"
}
// Find basic information about package path.
+ isCmd := false
t, importPath, err := build.FindTree(arg)
dir := ""
// Maybe it is a standard command.
importPath = arg
dir = p
err = nil
+ isCmd = true
}
}
// Maybe it is a path to a standard command.
importPath = filepath.FromSlash(arg[len(cmd):])
dir = arg
err = nil
+ isCmd = true
}
}
if err != nil {
}
// Maybe we know the package by its directory.
- if p := packageCache[dir]; p != nil {
+ p := packageCache[dir]
+ if p != nil {
packageCache[importPath] = p
- return reusePackage(p, stk)
+ p = reusePackage(p, stk)
+ } else {
+ p = scanPackage(&buildContext, t, arg, importPath, dir, stk, false)
}
- return scanPackage(&buildContext, t, arg, importPath, dir, stk)
+ // If we loaded the files from the Go root's cmd/ tree,
+ // it must be a command (package main).
+ if isCmd && p.Error == nil && p.Name != "main" {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("expected package main in %q; found package %s", dir, p.Name),
+ }
+ }
+ return p
}
func reusePackage(p *Package, stk *importStack) *Package {
"exp/ebnflint": true,
}
-func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string, stk *importStack) *Package {
+func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string, stk *importStack, useAllFiles bool) *Package {
// Read the files in the directory to learn the structure
// of the package.
p := &Package{
packageCache[dir] = p
packageCache[importPath] = p
+ ctxt.UseAllFiles = useAllFiles
info, err := ctxt.ScanDir(dir)
+ useAllFiles = false // flag does not apply to dependencies
if err != nil {
p.Error = &PackageError{
ImportStack: stk.copy(),
// A Context specifies the supporting context for a build.
type Context struct {
- GOARCH string // target architecture
- GOOS string // target operating system
- CgoEnabled bool // whether cgo can be used
- BuildTags []string // additional tags to recognize in +build lines
+ GOARCH string // target architecture
+ GOOS string // target operating system
+ CgoEnabled bool // whether cgo can be used
+ BuildTags []string // additional tags to recognize in +build lines
+ UseAllFiles bool // use files regardless of +build lines, file names
// By default, ScanDir uses the operating system's
// file system calls to read directories and files.
var Sfiles []string // files with ".S" (capital S)
var di DirInfo
+ var firstFile string
imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position)
fset := token.NewFileSet()
strings.HasPrefix(name, ".") {
continue
}
- if !ctxt.goodOSArchFile(name) {
+ if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
continue
}
continue
}
- // Look for +build comments to accept or reject the file.
filename, data, err := ctxt.readFile(dir, name)
if err != nil {
return nil, err
}
- if !ctxt.shouldBuild(data) {
+
+ // Look for +build comments to accept or reject the file.
+ if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
continue
}
}
pkg := string(pf.Name.Name)
- if pkg == "main" && di.Package != "" && di.Package != "main" {
- continue
- }
if pkg == "documentation" {
continue
}
pkg = pkg[:len(pkg)-len("_test")]
}
- if pkg != di.Package && di.Package == "main" {
- // Found non-main package but was recording
- // information about package main. Reset.
- di = DirInfo{}
- }
if di.Package == "" {
di.Package = pkg
+ firstFile = name
} else if pkg != di.Package {
- return nil, fmt.Errorf("%s: found packages %s and %s", dir, pkg, di.Package)
+ return nil, fmt.Errorf("%s: found packages %s (%s) and %s (%s)", dir, di.Package, firstFile, pkg, name)
}
if pf.Doc != nil {
if di.PackageComment != nil {