github.com/gsquire/gb@v0.4.4-0.20161112235727-3982dc872064/install.go (about)

     1  package gb
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"runtime"
     7  	"time"
     8  
     9  	"github.com/constabulary/gb/internal/debug"
    10  )
    11  
    12  // pkgpath returns the destination for object cached for this Package.
    13  func pkgpath(pkg *Package) string {
    14  	importpath := filepath.FromSlash(pkg.ImportPath) + ".a"
    15  	switch {
    16  	case pkg.isCrossCompile():
    17  		return filepath.Join(pkg.Pkgdir(), importpath)
    18  	case pkg.Standard && pkg.race:
    19  		// race enabled standard lib
    20  		return filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch+"_race", importpath)
    21  	case pkg.Standard:
    22  		// standard lib
    23  		return filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch, importpath)
    24  	default:
    25  		return filepath.Join(pkg.Pkgdir(), importpath)
    26  	}
    27  }
    28  
    29  // installpath returns the distination to cache this package's compiled .a file.
    30  // pkgpath and installpath differ in that the former returns the location where you will find
    31  // a previously cached .a file, the latter returns the location where an installed file
    32  // will be placed.
    33  //
    34  // The difference is subtle. pkgpath must deal with the possibility that the file is from the
    35  // standard library and is previously compiled. installpath will always return a path for the
    36  // project's pkg/ directory in the case that the stdlib is out of date, or not compiled for
    37  // a specific architecture.
    38  func installpath(pkg *Package) string {
    39  	if pkg.TestScope {
    40  		panic("installpath called with test scope")
    41  	}
    42  	return filepath.Join(pkg.Pkgdir(), filepath.FromSlash(pkg.ImportPath)+".a")
    43  }
    44  
    45  // isStale returns true if the source pkg is considered to be stale with
    46  // respect to its installed version.
    47  func isStale(pkg *Package) bool {
    48  	switch pkg.ImportPath {
    49  	case "C", "unsafe":
    50  		// synthetic packages are never stale
    51  		return false
    52  	}
    53  
    54  	if !pkg.Standard && pkg.Force {
    55  		return true
    56  	}
    57  
    58  	// tests are always stale, they are never installed
    59  	if pkg.TestScope {
    60  		return true
    61  	}
    62  
    63  	// Package is stale if completely unbuilt.
    64  	var built time.Time
    65  	if fi, err := os.Stat(pkgpath(pkg)); err == nil {
    66  		built = fi.ModTime()
    67  	}
    68  
    69  	if built.IsZero() {
    70  		debug.Debugf("%s is missing", pkgpath(pkg))
    71  		return true
    72  	}
    73  
    74  	olderThan := func(file string) bool {
    75  		fi, err := os.Stat(file)
    76  		return err != nil || fi.ModTime().After(built)
    77  	}
    78  
    79  	newerThan := func(file string) bool {
    80  		fi, err := os.Stat(file)
    81  		return err != nil || fi.ModTime().Before(built)
    82  	}
    83  
    84  	// As a courtesy to developers installing new versions of the compiler
    85  	// frequently, define that packages are stale if they are
    86  	// older than the compiler, and commands if they are older than
    87  	// the linker.  This heuristic will not work if the binaries are
    88  	// back-dated, as some binary distributions may do, but it does handle
    89  	// a very common case.
    90  	if !pkg.Standard {
    91  		if olderThan(pkg.tc.compiler()) {
    92  			debug.Debugf("%s is older than %s", pkgpath(pkg), pkg.tc.compiler())
    93  			return true
    94  		}
    95  		if pkg.isMain() && olderThan(pkg.tc.linker()) {
    96  			debug.Debugf("%s is older than %s", pkgpath(pkg), pkg.tc.compiler())
    97  			return true
    98  		}
    99  	}
   100  
   101  	if pkg.Standard && !pkg.isCrossCompile() {
   102  		// if this is a standard lib package, and we are not cross compiling
   103  		// then assume the package is up to date. This also works around
   104  		// golang/go#13769.
   105  		return false
   106  	}
   107  
   108  	// Package is stale if a dependency is newer.
   109  	for _, p := range pkg.Imports {
   110  		if p.ImportPath == "C" || p.ImportPath == "unsafe" {
   111  			continue // ignore stale imports of synthetic packages
   112  		}
   113  		if olderThan(pkgpath(p)) {
   114  			debug.Debugf("%s is older than %s", pkgpath(pkg), pkgpath(p))
   115  			return true
   116  		}
   117  	}
   118  
   119  	// if the main package is up to date but _newer_ than the binary (which
   120  	// could have been removed), then consider it stale.
   121  	if pkg.isMain() && newerThan(pkg.Binfile()) {
   122  		debug.Debugf("%s is newer than %s", pkgpath(pkg), pkg.Binfile())
   123  		return true
   124  	}
   125  
   126  	srcs := stringList(pkg.GoFiles, pkg.CFiles, pkg.CXXFiles, pkg.MFiles, pkg.HFiles, pkg.SFiles, pkg.CgoFiles, pkg.SysoFiles, pkg.SwigFiles, pkg.SwigCXXFiles)
   127  
   128  	for _, src := range srcs {
   129  		if olderThan(filepath.Join(pkg.Dir, src)) {
   130  			debug.Debugf("%s is older than %s", pkgpath(pkg), filepath.Join(pkg.Dir, src))
   131  			return true
   132  		}
   133  	}
   134  
   135  	return false
   136  }
   137  
   138  func stringList(args ...[]string) []string {
   139  	var l []string
   140  	for _, arg := range args {
   141  		l = append(l, arg...)
   142  	}
   143  	return l
   144  }