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