github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/cmd/go/get.go (about)

     1  // Copyright 2011 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // TODO: Dashboard upload
     6  
     7  package main
     8  
     9  import (
    10  	"fmt"
    11  	"go/build"
    12  	"os"
    13  	"path/filepath"
    14  	"regexp"
    15  	"runtime"
    16  	"strconv"
    17  	"strings"
    18  )
    19  
    20  var cmdGet = &Command{
    21  	UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]",
    22  	Short:     "download and install packages and dependencies",
    23  	Long: `
    24  Get downloads and installs the packages named by the import paths,
    25  along with their dependencies.
    26  
    27  The -d flag instructs get to stop after downloading the packages; that is,
    28  it instructs get not to install the packages.
    29  
    30  The -fix flag instructs get to run the fix tool on the downloaded packages
    31  before resolving dependencies or building the code.
    32  
    33  The -t flag instructs get to also download the packages required to build
    34  the tests for the specified packages.
    35  
    36  The -u flag instructs get to use the network to update the named packages
    37  and their dependencies.  By default, get uses the network to check out
    38  missing packages but does not use it to look for updates to existing packages.
    39  
    40  Get also accepts all the flags in the 'go build' and 'go install' commands,
    41  to control the installation. See 'go help build'.
    42  
    43  When checking out or updating a package, get looks for a branch or tag
    44  that matches the locally installed version of Go. The most important
    45  rule is that if the local installation is running version "go1", get
    46  searches for a branch or tag named "go1". If no such version exists it
    47  retrieves the most recent version of the package.
    48  
    49  For more about specifying packages, see 'go help packages'.
    50  
    51  For more about how 'go get' finds source code to
    52  download, see 'go help remote'.
    53  
    54  See also: go build, go install, go clean.
    55  	`,
    56  }
    57  
    58  var getD = cmdGet.Flag.Bool("d", false, "")
    59  var getT = cmdGet.Flag.Bool("t", false, "")
    60  var getU = cmdGet.Flag.Bool("u", false, "")
    61  var getFix = cmdGet.Flag.Bool("fix", false, "")
    62  
    63  func init() {
    64  	addBuildFlags(cmdGet)
    65  	cmdGet.Run = runGet // break init loop
    66  }
    67  
    68  func runGet(cmd *Command, args []string) {
    69  	// Phase 1.  Download/update.
    70  	var stk importStack
    71  	for _, arg := range downloadPaths(args) {
    72  		download(arg, &stk, *getT)
    73  	}
    74  	exitIfErrors()
    75  
    76  	// Phase 2. Rescan packages and reevaluate args list.
    77  
    78  	// Code we downloaded and all code that depends on it
    79  	// needs to be evicted from the package cache so that
    80  	// the information will be recomputed.  Instead of keeping
    81  	// track of the reverse dependency information, evict
    82  	// everything.
    83  	for name := range packageCache {
    84  		delete(packageCache, name)
    85  	}
    86  
    87  	args = importPaths(args)
    88  
    89  	// Phase 3.  Install.
    90  	if *getD {
    91  		// Download only.
    92  		// Check delayed until now so that importPaths
    93  		// has a chance to print errors.
    94  		return
    95  	}
    96  
    97  	runInstall(cmd, args)
    98  }
    99  
   100  // downloadPaths prepares the list of paths to pass to download.
   101  // It expands ... patterns that can be expanded.  If there is no match
   102  // for a particular pattern, downloadPaths leaves it in the result list,
   103  // in the hope that we can figure out the repository from the
   104  // initial ...-free prefix.
   105  func downloadPaths(args []string) []string {
   106  	args = importPathsNoDotExpansion(args)
   107  	var out []string
   108  	for _, a := range args {
   109  		if strings.Contains(a, "...") {
   110  			var expand []string
   111  			// Use matchPackagesInFS to avoid printing
   112  			// warnings.  They will be printed by the
   113  			// eventual call to importPaths instead.
   114  			if build.IsLocalImport(a) {
   115  				expand = matchPackagesInFS(a)
   116  			} else {
   117  				expand = matchPackages(a)
   118  			}
   119  			if len(expand) > 0 {
   120  				out = append(out, expand...)
   121  				continue
   122  			}
   123  		}
   124  		out = append(out, a)
   125  	}
   126  	return out
   127  }
   128  
   129  // downloadCache records the import paths we have already
   130  // considered during the download, to avoid duplicate work when
   131  // there is more than one dependency sequence leading to
   132  // a particular package.
   133  var downloadCache = map[string]bool{}
   134  
   135  // downloadRootCache records the version control repository
   136  // root directories we have already considered during the download.
   137  // For example, all the packages in the code.google.com/p/codesearch repo
   138  // share the same root (the directory for that path), and we only need
   139  // to run the hg commands to consider each repository once.
   140  var downloadRootCache = map[string]bool{}
   141  
   142  // download runs the download half of the get command
   143  // for the package named by the argument.
   144  func download(arg string, stk *importStack, getTestDeps bool) {
   145  	p := loadPackage(arg, stk)
   146  
   147  	// There's nothing to do if this is a package in the standard library.
   148  	if p.Standard {
   149  		return
   150  	}
   151  
   152  	// Only process each package once.
   153  	if downloadCache[arg] {
   154  		return
   155  	}
   156  	downloadCache[arg] = true
   157  
   158  	pkgs := []*Package{p}
   159  	wildcardOkay := len(*stk) == 0
   160  
   161  	// Download if the package is missing, or update if we're using -u.
   162  	if p.Dir == "" || *getU {
   163  		// The actual download.
   164  		stk.push(p.ImportPath)
   165  		err := downloadPackage(p)
   166  		if err != nil {
   167  			errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
   168  			stk.pop()
   169  			return
   170  		}
   171  
   172  		args := []string{arg}
   173  		// If the argument has a wildcard in it, re-evaluate the wildcard.
   174  		// We delay this until after reloadPackage so that the old entry
   175  		// for p has been replaced in the package cache.
   176  		if wildcardOkay && strings.Contains(arg, "...") {
   177  			if build.IsLocalImport(arg) {
   178  				args = matchPackagesInFS(arg)
   179  			} else {
   180  				args = matchPackages(arg)
   181  			}
   182  		}
   183  
   184  		// Clear all relevant package cache entries before
   185  		// doing any new loads.
   186  		for _, arg := range args {
   187  			p := packageCache[arg]
   188  			if p != nil {
   189  				delete(packageCache, p.Dir)
   190  				delete(packageCache, p.ImportPath)
   191  			}
   192  		}
   193  
   194  		pkgs = pkgs[:0]
   195  		for _, arg := range args {
   196  			stk.push(arg)
   197  			p := loadPackage(arg, stk)
   198  			stk.pop()
   199  			if p.Error != nil {
   200  				errorf("%s", p.Error)
   201  				continue
   202  			}
   203  			pkgs = append(pkgs, p)
   204  		}
   205  	}
   206  
   207  	// Process package, which might now be multiple packages
   208  	// due to wildcard expansion.
   209  	for _, p := range pkgs {
   210  		if *getFix {
   211  			run(stringList(tool("fix"), relPaths(p.allgofiles)))
   212  
   213  			// The imports might have changed, so reload again.
   214  			p = reloadPackage(arg, stk)
   215  			if p.Error != nil {
   216  				errorf("%s", p.Error)
   217  				return
   218  			}
   219  		}
   220  
   221  		// Process dependencies, now that we know what they are.
   222  		for _, dep := range p.deps {
   223  			// Don't get test dependencies recursively.
   224  			download(dep.ImportPath, stk, false)
   225  		}
   226  		if getTestDeps {
   227  			// Process test dependencies when -t is specified.
   228  			// (Don't get test dependencies for test dependencies.)
   229  			for _, path := range p.TestImports {
   230  				download(path, stk, false)
   231  			}
   232  			for _, path := range p.XTestImports {
   233  				download(path, stk, false)
   234  			}
   235  		}
   236  	}
   237  }
   238  
   239  // downloadPackage runs the create or download command
   240  // to make the first copy of or update a copy of the given package.
   241  func downloadPackage(p *Package) error {
   242  	var (
   243  		vcs            *vcsCmd
   244  		repo, rootPath string
   245  		err            error
   246  	)
   247  	if p.build.SrcRoot != "" {
   248  		// Directory exists.  Look for checkout along path to src.
   249  		vcs, rootPath, err = vcsForDir(p)
   250  		if err != nil {
   251  			return err
   252  		}
   253  		repo = "<local>" // should be unused; make distinctive
   254  	} else {
   255  		// Analyze the import path to determine the version control system,
   256  		// repository, and the import path for the root of the repository.
   257  		rr, err := repoRootForImportPath(p.ImportPath)
   258  		if err != nil {
   259  			return err
   260  		}
   261  		vcs, repo, rootPath = rr.vcs, rr.repo, rr.root
   262  	}
   263  
   264  	if p.build.SrcRoot == "" {
   265  		// Package not found.  Put in first directory of $GOPATH.
   266  		list := filepath.SplitList(buildContext.GOPATH)
   267  		if len(list) == 0 {
   268  			return fmt.Errorf("cannot download, $GOPATH not set. For more details see: go help gopath")
   269  		}
   270  		// Guard against people setting GOPATH=$GOROOT.
   271  		if list[0] == goroot {
   272  			return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath")
   273  		}
   274  		p.build.SrcRoot = filepath.Join(list[0], "src")
   275  		p.build.PkgRoot = filepath.Join(list[0], "pkg")
   276  	}
   277  	root := filepath.Join(p.build.SrcRoot, rootPath)
   278  	// If we've considered this repository already, don't do it again.
   279  	if downloadRootCache[root] {
   280  		return nil
   281  	}
   282  	downloadRootCache[root] = true
   283  
   284  	if buildV {
   285  		fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath)
   286  	}
   287  
   288  	// Check that this is an appropriate place for the repo to be checked out.
   289  	// The target directory must either not exist or have a repo checked out already.
   290  	meta := filepath.Join(root, "."+vcs.cmd)
   291  	st, err := os.Stat(meta)
   292  	if err == nil && !st.IsDir() {
   293  		return fmt.Errorf("%s exists but is not a directory", meta)
   294  	}
   295  	if err != nil {
   296  		// Metadata directory does not exist.  Prepare to checkout new copy.
   297  		// Some version control tools require the target directory not to exist.
   298  		// We require that too, just to avoid stepping on existing work.
   299  		if _, err := os.Stat(root); err == nil {
   300  			return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
   301  		}
   302  		// Some version control tools require the parent of the target to exist.
   303  		parent, _ := filepath.Split(root)
   304  		if err = os.MkdirAll(parent, 0777); err != nil {
   305  			return err
   306  		}
   307  		if err = vcs.create(root, repo); err != nil {
   308  			return err
   309  		}
   310  	} else {
   311  		// Metadata directory does exist; download incremental updates.
   312  		if err = vcs.download(root); err != nil {
   313  			return err
   314  		}
   315  	}
   316  
   317  	if buildN {
   318  		// Do not show tag sync in -n; it's noise more than anything,
   319  		// and since we're not running commands, no tag will be found.
   320  		// But avoid printing nothing.
   321  		fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd)
   322  		return nil
   323  	}
   324  
   325  	// Select and sync to appropriate version of the repository.
   326  	tags, err := vcs.tags(root)
   327  	if err != nil {
   328  		return err
   329  	}
   330  	vers := runtime.Version()
   331  	if i := strings.Index(vers, " "); i >= 0 {
   332  		vers = vers[:i]
   333  	}
   334  	if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {
   335  		return err
   336  	}
   337  
   338  	return nil
   339  }
   340  
   341  // goTag matches go release tags such as go1 and go1.2.3.
   342  // The numbers involved must be small (at most 4 digits),
   343  // have no unnecessary leading zeros, and the version cannot
   344  // end in .0 - it is go1, not go1.0 or go1.0.0.
   345  var goTag = regexp.MustCompile(
   346  	`^go((0|[1-9][0-9]{0,3})\.)*([1-9][0-9]{0,3})$`,
   347  )
   348  
   349  // selectTag returns the closest matching tag for a given version.
   350  // Closest means the latest one that is not after the current release.
   351  // Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
   352  // Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
   353  // Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
   354  //
   355  // NOTE(rsc): Eventually we will need to decide on some logic here.
   356  // For now, there is only "go1".  This matches the docs in go help get.
   357  func selectTag(goVersion string, tags []string) (match string) {
   358  	for _, t := range tags {
   359  		if t == "go1" {
   360  			return "go1"
   361  		}
   362  	}
   363  	return ""
   364  
   365  	/*
   366  		if goTag.MatchString(goVersion) {
   367  			v := goVersion
   368  			for _, t := range tags {
   369  				if !goTag.MatchString(t) {
   370  					continue
   371  				}
   372  				if cmpGoVersion(match, t) < 0 && cmpGoVersion(t, v) <= 0 {
   373  					match = t
   374  				}
   375  			}
   376  		}
   377  
   378  		return match
   379  	*/
   380  }
   381  
   382  // cmpGoVersion returns -1, 0, +1 reporting whether
   383  // x < y, x == y, or x > y.
   384  func cmpGoVersion(x, y string) int {
   385  	// Malformed strings compare less than well-formed strings.
   386  	if !goTag.MatchString(x) {
   387  		return -1
   388  	}
   389  	if !goTag.MatchString(y) {
   390  		return +1
   391  	}
   392  
   393  	// Compare numbers in sequence.
   394  	xx := strings.Split(x[len("go"):], ".")
   395  	yy := strings.Split(y[len("go"):], ".")
   396  
   397  	for i := 0; i < len(xx) && i < len(yy); i++ {
   398  		// The Atoi are guaranteed to succeed
   399  		// because the versions match goTag.
   400  		xi, _ := strconv.Atoi(xx[i])
   401  		yi, _ := strconv.Atoi(yy[i])
   402  		if xi < yi {
   403  			return -1
   404  		} else if xi > yi {
   405  			return +1
   406  		}
   407  	}
   408  
   409  	if len(xx) < len(yy) {
   410  		return -1
   411  	}
   412  	if len(xx) > len(yy) {
   413  		return +1
   414  	}
   415  	return 0
   416  }