diff options
Diffstat (limited to 'src/cmd/go/pkg.go')
-rw-r--r-- | src/cmd/go/pkg.go | 183 |
1 files changed, 151 insertions, 32 deletions
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 30bbfad55..7fc61fd86 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -36,12 +36,15 @@ type Package struct { Root string `json:",omitempty"` // Go root or Go path dir containing this package // Source files - GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string `json:",omitempty"` // .go sources files that import "C" - CFiles []string `json:",omitempty"` // .c source files - HFiles []string `json:",omitempty"` // .h source files - SFiles []string `json:",omitempty"` // .s source files - SysoFiles []string `json:",omitempty"` // .syso system object files added to package + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string `json:",omitempty"` // .go sources files that import "C" + IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints + CFiles []string `json:",omitempty"` // .c source files + HFiles []string `json:",omitempty"` // .h source files + SFiles []string `json:",omitempty"` // .s source files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package + SwigFiles []string `json:",omitempty"` // .swig files + SwigCXXFiles []string `json:",omitempty"` // .swigcxx files // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler @@ -69,12 +72,14 @@ type Package struct { imports []*Package deps []*Package gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths + allgofiles []string // gofiles + IgnoredGoFiles, absolute paths target string // installed file for this package (may be executable) fake bool // synthesized package forceBuild bool // this package must be rebuilt forceLibrary bool // this package is a library (even if named "main") local bool // imported via local path (./ or ../) localPrefix string // interpret ./ and ../ imports relative to this prefix + exeName string // desired name for temporary executable } func (p *Package) copyBuild(pp *build.Package) { @@ -90,10 +95,13 @@ func (p *Package) copyBuild(pp *build.Package) { p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") p.GoFiles = pp.GoFiles p.CgoFiles = pp.CgoFiles + p.IgnoredGoFiles = pp.IgnoredGoFiles p.CFiles = pp.CFiles p.HFiles = pp.HFiles p.SFiles = pp.SFiles p.SysoFiles = pp.SysoFiles + p.SwigFiles = pp.SwigFiles + p.SwigCXXFiles = pp.SwigCXXFiles p.CgoCFLAGS = pp.CgoCFLAGS p.CgoLDFLAGS = pp.CgoLDFLAGS p.CgoPkgConfig = pp.CgoPkgConfig @@ -117,6 +125,9 @@ func (p *PackageError) Error() string { // is the most important thing. return p.Pos + ": " + p.Err } + if len(p.ImportStack) == 0 { + return p.Err + } return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err } @@ -246,7 +257,7 @@ func reusePackage(p *Package, stk *importStack) *Package { if p.Error == nil { p.Error = &PackageError{ ImportStack: stk.copy(), - Err: "import loop", + Err: "import cycle not allowed", } } p.Incomplete = true @@ -260,13 +271,11 @@ func reusePackage(p *Package, stk *importStack) *Package { // isGoTool is the list of directories for Go programs that are installed in // $GOROOT/pkg/tool. var isGoTool = map[string]bool{ - "cmd/api": true, - "cmd/cgo": true, - "cmd/fix": true, - "cmd/vet": true, - "cmd/yacc": true, - "exp/gotype": true, - "exp/ebnflint": true, + "cmd/api": true, + "cmd/cgo": true, + "cmd/fix": true, + "cmd/vet": true, + "cmd/yacc": true, } // expandScanner expands a scanner.List error into all the errors in the list. @@ -316,11 +325,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Install cross-compiled binaries to subdirectories of bin. elem = full } - p.target = filepath.Join(p.build.BinDir, elem) - if p.Goroot && isGoTool[p.ImportPath] { + if p.build.BinDir != "" { + p.target = filepath.Join(p.build.BinDir, elem) + } + if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) { p.target = filepath.Join(gorootPkg, "tool", full) } - if buildContext.GOOS == "windows" { + if p.target != "" && buildContext.GOOS == "windows" { p.target += ".exe" } } else if p.local { @@ -340,6 +351,11 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") + // When race detection enabled everything depends on runtime/race. + // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies. + if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) { + importPaths = append(importPaths, "runtime/race") + } } // Build list of full paths to all Go files in the package, @@ -350,6 +366,38 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } sort.Strings(p.gofiles) + p.allgofiles = stringList(p.IgnoredGoFiles) + for i := range p.allgofiles { + p.allgofiles[i] = filepath.Join(p.Dir, p.allgofiles[i]) + } + p.allgofiles = append(p.allgofiles, p.gofiles...) + sort.Strings(p.allgofiles) + + // Check for case-insensitive collision of input files. + // To avoid problems on case-insensitive files, we reject any package + // where two different input files have equal names under a case-insensitive + // comparison. + f1, f2 := foldDup(stringList( + p.GoFiles, + p.CgoFiles, + p.IgnoredGoFiles, + p.CFiles, + p.HFiles, + p.SFiles, + p.SysoFiles, + p.SwigFiles, + p.SwigCXXFiles, + p.TestGoFiles, + p.XTestGoFiles, + )) + if f1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive file name collision: %q and %q", f1, f2), + } + return p + } + // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) deps := make(map[string]bool) @@ -403,11 +451,47 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { p.target = "" } - p.Target = p.target + + // In the absence of errors lower in the dependency tree, + // check for case-insensitive collisions of import paths. + if len(p.DepsErrors) == 0 { + dep1, dep2 := foldDup(p.Deps) + if dep1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive import collision: %q and %q", dep1, dep2), + } + return p + } + } + return p } +// usesSwig returns whether the package needs to run SWIG. +func (p *Package) usesSwig() bool { + return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 +} + +// swigSoname returns the name of the shared library we create for a +// SWIG input file. +func (p *Package) swigSoname(file string) string { + return strings.Replace(p.ImportPath, "/", "-", -1) + "-" + strings.Replace(file, ".", "-", -1) + ".so" +} + +// swigDir returns the name of the shared SWIG directory for a +// package. +func (p *Package) swigDir(ctxt *build.Context) string { + dir := p.build.PkgRoot + if ctxt.Compiler == "gccgo" { + dir = filepath.Join(dir, "gccgo_"+ctxt.GOOS+"_"+ctxt.GOARCH) + } else { + dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH) + } + return filepath.Join(dir, "swig") +} + // packageList returns the list of packages in the dag rooted at roots // as visited in a depth-first post-order traversal. func packageList(roots []*Package) []*Package { @@ -459,7 +543,7 @@ func isStale(p *Package, topRoot map[string]bool) bool { // distributions of Go packages, although such binaries are // only useful with the specific version of the toolchain that // created them. - if len(p.gofiles) == 0 { + if len(p.gofiles) == 0 && !p.usesSwig() { return false } @@ -491,14 +575,19 @@ func isStale(p *Package, topRoot map[string]bool) bool { // As a courtesy to developers installing new versions of the compiler // frequently, define that packages are stale if they are // older than the compiler, and commands if they are older than - // the linker. This heuristic will not work if the binaries are back-dated, - // as some binary distributions may do, but it does handle a very - // common case. See issue 3036. - if olderThan(buildToolchain.compiler()) { - return true - } - if p.build.IsCommand() && olderThan(buildToolchain.linker()) { - return true + // the linker. This heuristic will not work if the binaries are + // back-dated, as some binary distributions may do, but it does handle + // a very common case. + // See issue 3036. + // Assume code in $GOROOT is up to date, since it may not be writeable. + // See issue 4106. + if p.Root != goroot { + if olderThan(buildToolchain.compiler()) { + return true + } + if p.build.IsCommand() && olderThan(buildToolchain.linker()) { + return true + } } // Have installed copy, probably built using current compilers, @@ -522,6 +611,21 @@ func isStale(p *Package, topRoot map[string]bool) bool { } } + for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) { + if olderThan(filepath.Join(p.Dir, src)) { + return true + } + soname := p.swigSoname(src) + fi, err := os.Stat(soname) + if err != nil { + return true + } + fiSrc, err := os.Stat(src) + if err != nil || fiSrc.ModTime().After(fi.ModTime()) { + return true + } + } + return false } @@ -546,13 +650,24 @@ func loadPackage(arg string, stk *importStack) *Package { arg = sub } } - if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") { + if strings.HasPrefix(arg, "cmd/") { if p := cmdCache[arg]; p != nil { return p } stk.push(arg) defer stk.pop() - bp, err := build.ImportDir(filepath.Join(gorootSrc, arg), 0) + + if strings.Contains(arg[4:], "/") { + p := &Package{ + Error: &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"), + }, + } + return p + } + + bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0) bp.ImportPath = arg bp.Goroot = true bp.BinDir = gorootBin @@ -580,7 +695,7 @@ func loadPackage(arg string, stk *importStack) *Package { // referring to io/ioutil rather than a hypothetical import of // "./ioutil". if build.IsLocalImport(arg) { - bp, _ := build.ImportDir(filepath.Join(cwd, arg), build.FindOnly) + bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } @@ -621,10 +736,14 @@ func packagesAndErrors(args []string) []*Package { args = importPaths(args) var pkgs []*Package var stk importStack + var set = make(map[string]bool) + for _, arg := range args { - pkgs = append(pkgs, loadPackage(arg, &stk)) + if !set[arg] { + pkgs = append(pkgs, loadPackage(arg, &stk)) + set[arg] = true + } } - computeStale(pkgs...) return pkgs |