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 }