// importPaths returns the import paths to use for the given command line. func importPaths(args []string) []string { args = importPathsNoDotExpansion(args) var out []string for _, a := range args { if strings.Contains(a, "...") { if build.IsLocalImport(a) { out = append(out, allPackagesInFS(a)...) } else { out = append(out, allPackages(a)...) } continue } out = append(out, a) } return out }
// loadImport scans the directory named by path, which must be an import path, // but possibly a local import path (an absolute file system path or one beginning // with ./ or ../). A local relative path is interpreted relative to srcDir. // It returns a *Package describing the package found in that directory. func loadImport(path string, srcDir string, stk *importStack, importPos []token.Position) *Package { stk.push(path) defer stk.pop() // Determine canonical identifier for this package. // For a local import the identifier is the pseudo-import path // we create from the full directory to the package. // Otherwise it is the usual import path. importPath := path isLocal := build.IsLocalImport(path) if isLocal { importPath = dirToImportPath(filepath.Join(srcDir, path)) } if p := packageCache[importPath]; p != nil { return reusePackage(p, stk) } p := new(Package) p.local = isLocal p.ImportPath = importPath packageCache[importPath] = p // Load package. // Import always returns bp != nil, even if an error occurs, // in order to return partial information. // // TODO: After Go 1, decide when to pass build.AllowBinary here. // See issue 3268 for mistakes to avoid. bp, err := buildContext.Import(path, srcDir, 0) bp.ImportPath = importPath if gobin != "" { bp.BinDir = gobin } p.load(stk, bp, err) if p.Error != nil && len(importPos) > 0 { pos := importPos[0] pos.Filename = shortPath(pos.Filename) p.Error.Pos = pos.String() } return p }
// downloadPaths prepares the list of paths to pass to download. // It expands ... patterns that can be expanded. If there is no match // for a particular pattern, downloadPaths leaves it in the result list, // in the hope that we can figure out the repository from the // initial ...-free prefix. func downloadPaths(args []string) []string { args = importPathsNoDotExpansion(args) var out []string for _, a := range args { if strings.Contains(a, "...") { var expand []string // Use matchPackagesInFS to avoid printing // warnings. They will be printed by the // eventual call to importPaths instead. if build.IsLocalImport(a) { expand = matchPackagesInFS(a) } else { expand = matchPackages(a) } if len(expand) > 0 { out = append(out, expand...) continue } } out = append(out, a) } return out }
func runTest(cmd *Command, args []string) { var pkgArgs []string pkgArgs, testArgs = testFlags(args) raceInit() pkgs := packagesForBuild(pkgArgs) if len(pkgs) == 0 { fatalf("no packages to test") } if testC && len(pkgs) != 1 { fatalf("cannot use -c flag with multiple packages") } if testProfile && len(pkgs) != 1 { fatalf("cannot use test profile flag with multiple packages") } // If a test timeout was given and is parseable, set our kill timeout // to that timeout plus one minute. This is a backup alarm in case // the test wedges with a goroutine spinning and its background // timer does not get a chance to fire. if dt, err := time.ParseDuration(testTimeout); err == nil && dt > 0 { testKillTimeout = dt + 1*time.Minute } // show passing test output (after buffering) with -v flag. // must buffer because tests are running in parallel, and // otherwise the output will get mixed. testShowPass = testV // stream test output (no buffering) when no package has // been given on the command line (implicit current directory) // or when benchmarking. // Also stream if we're showing output anyway with a // single package under test. In that case, streaming the // output produces the same result as not streaming, // just more immediately. testStreamOutput = len(pkgArgs) == 0 || testBench || (len(pkgs) <= 1 && testShowPass) var b builder b.init() if testI { buildV = testV deps := map[string]bool{ // Dependencies for testmain. "testing": true, "regexp": true, } for _, p := range pkgs { // Dependencies for each test. for _, path := range p.Imports { deps[path] = true } for _, path := range p.TestImports { deps[path] = true } for _, path := range p.XTestImports { deps[path] = true } } // translate C to runtime/cgo if deps["C"] { delete(deps, "C") deps["runtime/cgo"] = true if buildContext.GOOS == runtime.GOOS && buildContext.GOARCH == runtime.GOARCH { deps["cmd/cgo"] = true } } // Ignore pseudo-packages. delete(deps, "unsafe") all := []string{} for path := range deps { if !build.IsLocalImport(path) { all = append(all, path) } } sort.Strings(all) a := &action{} for _, p := range packagesForBuild(all) { a.deps = append(a.deps, b.action(modeInstall, modeInstall, p)) } b.do(a) if !testC { return } b.init() } var builds, runs, prints []*action // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { buildTest, runTest, printTest, err := b.test(p) if err != nil { str := err.Error() if strings.HasPrefix(str, "\n") { str = str[1:] } if p.ImportPath != "" { errorf("# %s\n%s", p.ImportPath, str) } else { errorf("%s", str) } continue } builds = append(builds, buildTest) runs = append(runs, runTest) prints = append(prints, printTest) } // Ultimately the goal is to print the output. root := &action{deps: prints} // Force the printing of results to happen in order, // one at a time. for i, a := range prints { if i > 0 { a.deps = append(a.deps, prints[i-1]) } } // If we are benchmarking, force everything to // happen in serial. Could instead allow all the // builds to run before any benchmarks start, // but try this for now. if testBench { for i, a := range builds { if i > 0 { // Make build of test i depend on // completing the run of test i-1. a.deps = append(a.deps, runs[i-1]) } } } // If we are building any out-of-date packages other // than those under test, warn. okBuild := map[*Package]bool{} for _, p := range pkgs { okBuild[p] = true } warned := false for _, a := range actionList(root) { if a.p != nil && a.f != nil && !okBuild[a.p] && !a.p.fake && !a.p.local { okBuild[a.p] = true // don't warn again if !warned { fmt.Fprintf(os.Stderr, "warning: building out-of-date packages:\n") warned = true } fmt.Fprintf(os.Stderr, "\t%s\n", a.p.ImportPath) } } if warned { args := strings.Join(pkgArgs, " ") if args != "" { args = " " + args } extraOpts := "" if buildRace { extraOpts = "-race " } fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args) } b.do(root) }
// download runs the download half of the get command // for the package named by the argument. func download(arg string, stk *importStack) { p := loadPackage(arg, stk) // There's nothing to do if this is a package in the standard library. if p.Standard { return } // Only process each package once. if downloadCache[arg] { return } downloadCache[arg] = true pkgs := []*Package{p} wildcardOkay := len(*stk) == 0 // Download if the package is missing, or update if we're using -u. if p.Dir == "" || *getU { // The actual download. stk.push(p.ImportPath) err := downloadPackage(p) if err != nil { errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()}) stk.pop() return } args := []string{arg} // If the argument has a wildcard in it, re-evaluate the wildcard. // We delay this until after reloadPackage so that the old entry // for p has been replaced in the package cache. if wildcardOkay && strings.Contains(arg, "...") { if build.IsLocalImport(arg) { args = matchPackagesInFS(arg) } else { args = matchPackages(arg) } } // Clear all relevant package cache entries before // doing any new loads. for _, arg := range args { p := packageCache[arg] if p != nil { delete(packageCache, p.Dir) delete(packageCache, p.ImportPath) } } pkgs = pkgs[:0] for _, arg := range args { stk.push(arg) p := loadPackage(arg, stk) stk.pop() if p.Error != nil { errorf("%s", p.Error) continue } pkgs = append(pkgs, p) } } // Process package, which might now be multiple packages // due to wildcard expansion. for _, p := range pkgs { if *getFix { run(stringList(tool("fix"), relPaths(p.allgofiles))) // The imports might have changed, so reload again. p = reloadPackage(arg, stk) if p.Error != nil { errorf("%s", p.Error) return } } // Process dependencies, now that we know what they are. for _, dep := range p.deps { download(dep.ImportPath, stk) } } }
func main() { _ = go11tag flag.Usage = usage flag.Parse() log.SetFlags(0) args := flag.Args() if len(args) < 1 { usage() } if args[0] == "help" { help(args[1:]) return } // Diagnose common mistake: GOPATH==GOROOT. // This setting is equivalent to not setting GOPATH at all, // which is not what most people want when they do it. if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) } else { for _, p := range filepath.SplitList(gopath) { // Note: using HasPrefix instead of Contains because a ~ can appear // in the middle of directory elements, such as /tmp/git-1.8.2~rc3 // or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell. if strings.HasPrefix(p, "~") { fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p) os.Exit(2) } if build.IsLocalImport(p) { fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) os.Exit(2) } } } if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() { fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot) os.Exit(2) } for _, cmd := range commands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { args = args[1:] } else { cmd.Flag.Parse(args[1:]) args = cmd.Flag.Args() } cmd.Run(cmd, args) exit() return } } fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0]) setExitStatus(2) exit() }
// loadPackage is like loadImport but is used for command-line arguments, // not for paths found in import statements. In addition to ordinary import paths, // loadPackage accepts pseudo-paths beginning with cmd/ to denote commands // in the Go command directory, as well as paths to those directories. func loadPackage(arg string, stk *importStack) *Package { if build.IsLocalImport(arg) { dir := arg if !filepath.IsAbs(dir) { if abs, err := filepath.Abs(dir); err == nil { // interpret relative to current directory dir = abs } } if sub, ok := hasSubdir(gorootSrc, dir); ok && strings.HasPrefix(sub, "cmd/") && !strings.Contains(sub[4:], "/") { arg = sub } } if strings.HasPrefix(arg, "cmd/") { if p := cmdCache[arg]; p != nil { return p } stk.push(arg) defer stk.pop() if strings.Contains(arg[4:], "/") { p := &Package{ Error: &PackageError{ ImportStack: stk.copy(), Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"), }, } return p } bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0) bp.ImportPath = arg bp.Goroot = true bp.BinDir = gorootBin if gobin != "" { bp.BinDir = gobin } bp.Root = goroot bp.SrcRoot = gorootSrc p := new(Package) cmdCache[arg] = p p.load(stk, bp, err) if p.Error == nil && p.Name != "main" { p.Error = &PackageError{ ImportStack: stk.copy(), Err: fmt.Sprintf("expected package main but found package %s in %s", p.Name, p.Dir), } } return p } // Wasn't a command; must be a package. // If it is a local import path but names a standard package, // we treat it as if the user specified the standard package. // This lets you run go test ./ioutil in package io and be // referring to io/ioutil rather than a hypothetical import of // "./ioutil". if build.IsLocalImport(arg) { bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } } return loadImport(arg, cwd, stk, nil) }