// computeStale computes the Stale flag in the package dag that starts
// at the named pkgs (command-line arguments).
func computeStale(pkgs ...*Package) {
- topRoot := map[string]bool{}
- for _, p := range pkgs {
- topRoot[p.Root] = true
- }
-
for _, p := range packageList(pkgs) {
- p.Stale = isStale(p, topRoot)
+ p.Stale = isStale(p)
}
}
var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
// isStale reports whether package p needs to be rebuilt.
-func isStale(p *Package, topRoot map[string]bool) bool {
+func isStale(p *Package) bool {
if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
// fake, builtin package
return false
return false
}
- // If we are running a release copy of Go, do not rebuild the standard packages.
- // They may not be writable anyway, but they are certainly not changing.
- // This makes 'go build -a' skip the standard packages when using an official release.
- // See issue 4106 and issue 8290.
- pkgBuildA := buildA
- if p.Standard && isGoRelease {
- pkgBuildA = false
- }
-
- if pkgBuildA || p.target == "" || p.Stale {
+ // If there's no install target or it's already marked stale, we have to rebuild.
+ if p.target == "" || p.Stale {
return true
}
return true
}
+ // If we are running a release copy of Go, do not rebuild the standard packages.
+ // They may not be writable anyway, but they are certainly not changing.
+ // This makes 'go build' and 'go build -a' skip the standard packages when
+ // using an official release. See issue 3036, issue 3149, issue 4106, issue 8290.
+ // (If a change to a release tree must be made by hand, the way to force the
+ // install is to run make.bash, which will remove the old package archives
+ // before rebuilding.)
+ if p.Standard && isGoRelease {
+ return false
+ }
+
+ // If the -a flag is given, rebuild everything (except standard packages; see above).
+ if buildA {
+ return true
+ }
+
olderThan := func(file string) bool {
fi, err := os.Stat(file)
return err != nil || fi.ModTime().After(built)
// 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.
+ // Exclude $GOROOT, under the assumption that people working on
+ // the compiler may want to control when everything gets rebuilt,
+ // and people updating the Go repository will run make.bash or all.bash
+ // and get a full rebuild anyway.
+ // Excluding $GOROOT used to also fix issue 4106, but that's now
+ // taken care of above (at least when the installed Go is a released version).
if p.Root != goroot {
if olderThan(buildToolchain.compiler()) {
return true
}
}
- // Have installed copy, probably built using current compilers,
- // built with the right set of source files,
- // and built after its imported packages. The only reason now
- // that we'd have to rebuild it is if the sources were newer than
- // the package. If a package p is not in the same tree as any
- // package named on the command-line, assume it is up-to-date
- // no matter what the modification times on the source files indicate.
- // This avoids rebuilding $GOROOT packages when people are
- // working outside the Go root, and it effectively makes each tree
- // listed in $GOPATH a separate compilation world.
- // See issue 3149.
- if p.Root != "" && !topRoot[p.Root] {
- return false
- }
+ // Note: Until Go 1.5, we had an additional shortcut here.
+ // We built a list of the workspace roots ($GOROOT, each $GOPATH)
+ // containing targets directly named on the command line,
+ // and if p were not in any of those, it would be treated as up-to-date
+ // as long as it is built. The goal was to avoid rebuilding a system-installed
+ // $GOROOT, unless something from $GOROOT were explicitly named
+ // on the command line (like go install math).
+ // That's now handled by the isGoRelease clause above.
+ // The other effect of the shortcut was to isolate different entries in
+ // $GOPATH from each other. This had the unfortunate effect that
+ // if you had (say), GOPATH listing two entries, one for commands
+ // and one for libraries, and you did a 'git pull' in the library one
+ // and then tried 'go install commands/...', it would build the new libraries
+ // during the first build (because they wouldn't have been installed at all)
+ // but then subsequent builds would not rebuild the libraries, even if the
+ // mtimes indicate they are stale, because the different GOPATH entries
+ // were treated differently. This behavior was confusing when using
+ // non-trivial GOPATHs, which were particularly common with some
+ // code management conventions, like the original godep.
+ // Since the $GOROOT case (the original motivation) is handled separately,
+ // we no longer put a barrier between the different $GOPATH entries.
+ //
+ // One implication of this is that if there is a system directory for
+ // non-standard Go packages that is included in $GOPATH, the mtimes
+ // on those compiled packages must be no earlier than the mtimes
+ // on the source files. Since most distributions use the same mtime
+ // for all files in a tree, they will be unaffected. People using plain
+ // tar x to extract system-installed packages will need to adjust mtimes,
+ // but it's better to force them to get the mtimes right than to ignore
+ // the mtimes and thereby do the wrong thing in common use cases.
+ //
+ // So there is no GOPATH vs GOPATH shortcut here anymore.
+ //
+ // If something needs to come back here, we could try writing a dummy
+ // file with a random name to the $GOPATH/pkg directory (and removing it)
+ // to test for write access, and then skip GOPATH roots we don't have write
+ // access to. But hopefully we can just use the mtimes always.
srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles)
for _, src := range srcs {
fi
rm -r $d
+TEST 'go install rebuilds stale packages in other GOPATH'
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d/d1:$d/d2
+mkdir -p $d/d1/src/p1 $d/d2/src/p2
+echo 'package p1
+
+import "p2"
+
+func F() { p2.F() }
+' > $d/d1/src/p1/p1.go
+echo 'package p2
+
+func F() {}
+' > $d/d2/src/p2/p2.go
+if ! ./testgo install p1; then
+ echo "./testgo install p1 failed"
+ ok=false
+elif [ "$(./testgo list -f '{{.Stale}}' p1)" != false ]; then
+ echo "./testgo list mypkg claims p1 is stale, incorrectly"
+ ok=false
+elif [ "$(./testgo list -f '{{.Stale}}' p2)" != false ]; then
+ echo "./testgo list mypkg claims p2 is stale, incorrectly"
+ ok=false
+else
+ sleep 1
+ echo 'func G() {}' >>$d/d2/src/p2/p2.go
+ if [ "$(./testgo list -f '{{.Stale}}' p2)" != true ]; then
+ echo "./testgo list mypkg claims p2 is NOT stale, incorrectly"
+ ok=false
+ elif [ "$(./testgo list -f '{{.Stale}}' p1)" != true ]; then
+ echo "./testgo list mypkg claims p1 is NOT stale, incorrectly"
+ ok=false
+ fi
+
+ if ! ./testgo install p1; then
+ echo "./testgo install p1 failed second time"
+ ok=false
+ else
+ if [ "$(./testgo list -f '{{.Stale}}' p2)" != false ]; then
+ echo "./testgo list mypkg claims p2 is stale after reinstall, incorrectly"
+ ok=false
+ elif [ "$(./testgo list -f '{{.Stale}}' p1)" != false ]; then
+ echo "./testgo list mypkg claims p1 is stale after reinstall, incorrectly"
+ ok=false
+ fi
+ fi
+fi
+rm -r $d
+
TEST 'go install detects removed files'
d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
export GOPATH=$d
package mypkg' >$d/src/mypkg/z.go
if ! ./testgo install mypkg; then
- echo "testgo install mypkg failed"
+ echo "./testgo install mypkg failed"
ok=false
elif [ "$(./testgo list -f '{{.Stale}}' mypkg)" != false ]; then
echo "./testgo list mypkg claims mypkg is stale, incorrectly"