Commit eb93b20c authored by Russ Cox's avatar Russ Cox

cmd/go: split out cmd/go/internal/load

This is one CL in a long sequence of changes to break up the
go command from one package into a plausible group of packages.

This sequence is concerned only with moving code, not changing
or cleaning up code. There will still be more cleanup after this sequence.

The entire sequence will be submitted together: it is not a goal
for the tree to build at every step.

For #18653.

Change-Id: Ic802483e50598def638f1e2e706d5fdf7822d32d
Reviewed-on: https://go-review.googlesource.com/36196Reviewed-by: default avatarDavid Crawshaw <crawshaw@golang.org>
parent 461c3e52
...@@ -6,8 +6,8 @@ package main ...@@ -6,8 +6,8 @@ package main
import ( import (
"bytes" "bytes"
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
......
This diff is collapsed.
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
...@@ -77,12 +78,12 @@ func init() { ...@@ -77,12 +78,12 @@ func init() {
} }
func runClean(cmd *base.Command, args []string) { func runClean(cmd *base.Command, args []string) {
for _, pkg := range packagesAndErrors(args) { for _, pkg := range load.PackagesAndErrors(args) {
clean(pkg) clean(pkg)
} }
} }
var cleaned = map[*Package]bool{} var cleaned = map[*load.Package]bool{}
// TODO: These are dregs left by Makefile-based builds. // TODO: These are dregs left by Makefile-based builds.
// Eventually, can stop deleting these. // Eventually, can stop deleting these.
...@@ -107,7 +108,7 @@ var cleanExt = map[string]bool{ ...@@ -107,7 +108,7 @@ var cleanExt = map[string]bool{
".so": true, ".so": true,
} }
func clean(p *Package) { func clean(p *load.Package) {
if cleaned[p] { if cleaned[p] {
return return
} }
...@@ -209,17 +210,17 @@ func clean(p *Package) { ...@@ -209,17 +210,17 @@ func clean(p *Package) {
} }
} }
if cleanI && p.target != "" { if cleanI && p.Internal.Target != "" {
if cfg.BuildN || cfg.BuildX { if cfg.BuildN || cfg.BuildX {
b.showcmd("", "rm -f %s", p.target) b.showcmd("", "rm -f %s", p.Internal.Target)
} }
if !cfg.BuildN { if !cfg.BuildN {
removeFile(p.target) removeFile(p.Internal.Target)
} }
} }
if cleanR { if cleanR {
for _, p1 := range p.imports { for _, p1 := range p.Internal.Imports {
clean(p1) clean(p1)
} }
} }
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
) )
var cmdDoc = &base.Command{ var cmdDoc = &base.Command{
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
...@@ -33,14 +34,14 @@ func mkEnv() []cfg.EnvVar { ...@@ -33,14 +34,14 @@ func mkEnv() []cfg.EnvVar {
env := []cfg.EnvVar{ env := []cfg.EnvVar{
{"GOARCH", cfg.Goarch}, {"GOARCH", cfg.Goarch},
{"GOBIN", gobin}, {"GOBIN", cfg.GOBIN},
{"GOEXE", cfg.ExeSuffix}, {"GOEXE", cfg.ExeSuffix},
{"GOHOSTARCH", runtime.GOARCH}, {"GOHOSTARCH", runtime.GOARCH},
{"GOHOSTOS", runtime.GOOS}, {"GOHOSTOS", runtime.GOOS},
{"GOOS", cfg.Goos}, {"GOOS", cfg.Goos},
{"GOPATH", cfg.BuildContext.GOPATH}, {"GOPATH", cfg.BuildContext.GOPATH},
{"GORACE", os.Getenv("GORACE")}, {"GORACE", os.Getenv("GORACE")},
{"GOROOT", goroot}, {"GOROOT", cfg.GOROOT},
{"GOTOOLDIR", base.ToolDir}, {"GOTOOLDIR", base.ToolDir},
// disable escape codes in clang errors // disable escape codes in clang errors
...@@ -88,7 +89,7 @@ func findEnv(env []cfg.EnvVar, name string) string { ...@@ -88,7 +89,7 @@ func findEnv(env []cfg.EnvVar, name string) string {
func extraEnvVars() []cfg.EnvVar { func extraEnvVars() []cfg.EnvVar {
var b builder var b builder
b.init() b.init()
cppflags, cflags, cxxflags, fflags, ldflags := b.cflags(&Package{}) cppflags, cflags, cxxflags, fflags, ldflags := b.cflags(&load.Package{})
return []cfg.EnvVar{ return []cfg.EnvVar{
{"PKG_CONFIG", b.pkgconfigCmd()}, {"PKG_CONFIG", b.pkgconfigCmd()},
{"CGO_CFLAGS", strings.Join(cflags, " ")}, {"CGO_CFLAGS", strings.Join(cflags, " ")},
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str" "cmd/go/internal/str"
) )
...@@ -27,10 +28,10 @@ See also: go fmt, go vet. ...@@ -27,10 +28,10 @@ See also: go fmt, go vet.
} }
func runFix(cmd *base.Command, args []string) { func runFix(cmd *base.Command, args []string) {
for _, pkg := range packages(args) { for _, pkg := range load.Packages(args) {
// Use pkg.gofiles instead of pkg.Dir so that // Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package, // the command only applies to this package,
// not to packages in subdirectories. // not to packages in subdirectories.
base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), base.RelPaths(pkg.allgofiles))) base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), base.RelPaths(pkg.Internal.AllGoFiles)))
} }
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str" "cmd/go/internal/str"
"os" "os"
"path/filepath" "path/filepath"
...@@ -38,11 +39,11 @@ See also: go fix, go vet. ...@@ -38,11 +39,11 @@ See also: go fix, go vet.
func runFmt(cmd *base.Command, args []string) { func runFmt(cmd *base.Command, args []string) {
gofmt := gofmtPath() gofmt := gofmtPath()
for _, pkg := range packages(args) { for _, pkg := range load.Packages(args) {
// Use pkg.gofiles instead of pkg.Dir so that // Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package, // the command only applies to this package,
// not to packages in subdirectories. // not to packages in subdirectories.
base.Run(str.StringList(gofmt, "-l", "-w", base.RelPaths(pkg.allgofiles))) base.Run(str.StringList(gofmt, "-l", "-w", base.RelPaths(pkg.Internal.AllGoFiles)))
} }
} }
...@@ -52,12 +53,12 @@ func gofmtPath() string { ...@@ -52,12 +53,12 @@ func gofmtPath() string {
gofmt += base.ToolWindowsExtension gofmt += base.ToolWindowsExtension
} }
gofmtPath := filepath.Join(gobin, gofmt) gofmtPath := filepath.Join(cfg.GOBIN, gofmt)
if _, err := os.Stat(gofmtPath); err == nil { if _, err := os.Stat(gofmtPath); err == nil {
return gofmtPath return gofmtPath
} }
gofmtPath = filepath.Join(goroot, "bin", gofmt) gofmtPath = filepath.Join(cfg.GOROOT, "bin", gofmt)
if _, err := os.Stat(gofmtPath); err == nil { if _, err := os.Stat(gofmtPath); err == nil {
return gofmtPath return gofmtPath
} }
......
...@@ -7,8 +7,9 @@ package main ...@@ -7,8 +7,9 @@ package main
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"fmt" "fmt"
"io" "io"
"log" "log"
...@@ -138,7 +139,7 @@ func init() { ...@@ -138,7 +139,7 @@ func init() {
} }
func runGenerate(cmd *base.Command, args []string) { func runGenerate(cmd *base.Command, args []string) {
ignoreImports = true load.IgnoreImports = true
if generateRunFlag != "" { if generateRunFlag != "" {
var err error var err error
...@@ -148,8 +149,8 @@ func runGenerate(cmd *base.Command, args []string) { ...@@ -148,8 +149,8 @@ func runGenerate(cmd *base.Command, args []string) {
} }
} }
// Even if the arguments are .go files, this loop suffices. // Even if the arguments are .go files, this loop suffices.
for _, pkg := range packages(args) { for _, pkg := range load.Packages(args) {
for _, file := range pkg.gofiles { for _, file := range pkg.Internal.GoFiles {
if !generate(pkg.Name, file) { if !generate(pkg.Name, file) {
break break
} }
......
This diff is collapsed.
...@@ -7,10 +7,13 @@ ...@@ -7,10 +7,13 @@
package base package base
import ( import (
"bytes"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/str" "cmd/go/internal/str"
"errors"
"flag" "flag"
"fmt" "fmt"
"go/scanner"
"log" "log"
"os" "os"
"os/exec" "os/exec"
...@@ -145,3 +148,25 @@ func RunStdin(cmdline []string) { ...@@ -145,3 +148,25 @@ func RunStdin(cmdline []string) {
// Usage is the usage-reporting function, filled in by package main // Usage is the usage-reporting function, filled in by package main
// but here for reference by other packages. // but here for reference by other packages.
var Usage func() var Usage func()
// ExpandScanner expands a scanner.List error into all the errors in the list.
// The default Error method only shows the first error
// and does not shorten paths.
func ExpandScanner(err error) error {
// Look for parser errors.
if err, ok := err.(scanner.ErrorList); ok {
// Prepare error with \n before each message.
// When printed in something like context: %v
// this will put the leading file positions each on
// its own line. It will also show all the errors
// instead of just the first, as err.Error does.
var buf bytes.Buffer
for _, e := range err {
e.Pos.Filename = ShortPath(e.Pos.Filename)
buf.WriteString("\n")
buf.WriteString(e.Error())
}
return errors.New(buf.String())
}
return err
}
...@@ -9,6 +9,8 @@ package cfg ...@@ -9,6 +9,8 @@ package cfg
import ( import (
"flag" "flag"
"go/build" "go/build"
"os"
"path/filepath"
"runtime" "runtime"
) )
...@@ -64,3 +66,11 @@ func AddBuildFlagsNX(flags *flag.FlagSet) { ...@@ -64,3 +66,11 @@ func AddBuildFlagsNX(flags *flag.FlagSet) {
flags.BoolVar(&BuildN, "n", false, "") flags.BoolVar(&BuildN, "n", false, "")
flags.BoolVar(&BuildX, "x", false, "") flags.BoolVar(&BuildX, "x", false, "")
} }
var (
GOROOT = filepath.Clean(runtime.GOROOT())
GOBIN = os.Getenv("GOBIN")
GOROOTbin = filepath.Join(GOROOT, "bin")
GOROOTpkg = filepath.Join(GOROOT, "pkg")
GOROOTsrc = filepath.Join(GOROOT, "src")
)
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package main package load
import "testing" import "testing"
......
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package load
import (
"path/filepath"
"strings"
)
// hasSubdir reports whether dir is a subdirectory of
// (possibly multiple levels below) root.
// If so, it sets rel to the path fragment that must be
// appended to root to reach dir.
func hasSubdir(root, dir string) (rel string, ok bool) {
if p, err := filepath.EvalSymlinks(root); err == nil {
root = p
}
if p, err := filepath.EvalSymlinks(dir); err == nil {
dir = p
}
const sep = string(filepath.Separator)
root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) {
root += sep
}
dir = filepath.Clean(dir)
if !strings.HasPrefix(dir, root) {
return "", false
}
return filepath.ToSlash(dir[len(root):]), true
}
// hasPathPrefix reports whether the path s begins with the
// elements in prefix.
func hasPathPrefix(s, prefix string) bool {
switch {
default:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == '/' {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
}
}
// expandPath returns the symlink-expanded form of path.
func expandPath(p string) string {
x, err := filepath.EvalSymlinks(p)
if err == nil {
return x
}
return p
}
// hasFilePathPrefix reports whether the filesystem path s begins with the
// elements in prefix.
func hasFilePathPrefix(s, prefix string) bool {
sv := strings.ToUpper(filepath.VolumeName(s))
pv := strings.ToUpper(filepath.VolumeName(prefix))
s = s[len(sv):]
prefix = prefix[len(pv):]
switch {
default:
return false
case sv != pv:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
}
}
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package load
import (
"cmd/go/internal/cfg"
"fmt"
"go/build"
"log"
"os"
"path"
"path/filepath"
"regexp"
"strings"
)
// allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT matching pattern.
// The pattern is either "all" (all packages), "std" (standard packages),
// "cmd" (standard commands), or a path including "...".
func allPackages(pattern string) []string {
pkgs := MatchPackages(pattern)
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs
}
// allPackagesInFS is like allPackages but is passed a pattern
// beginning ./ or ../, meaning it should scan the tree rooted
// at the given directory. There are ... in the pattern too.
func allPackagesInFS(pattern string) []string {
pkgs := MatchPackagesInFS(pattern)
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs
}
// MatchPackages returns a list of package paths matching pattern
// (see go help packages for pattern syntax).
func MatchPackages(pattern string) []string {
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if !IsMetaPackage(pattern) {
match = matchPattern(pattern)
treeCanMatch = treeCanMatchPattern(pattern)
}
have := map[string]bool{
"builtin": true, // ignore pseudo-package that exists only for documentation
}
if !cfg.BuildContext.CgoEnabled {
have["runtime/cgo"] = true // ignore during walk
}
var pkgs []string
for _, src := range cfg.BuildContext.SrcDirs() {
if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
continue
}
src = filepath.Clean(src) + string(filepath.Separator)
root := src
if pattern == "cmd" {
root += "cmd" + string(filepath.Separator)
}
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() || path == src {
return nil
}
// Avoid .foo, _foo, and testdata directory trees.
_, elem := filepath.Split(path)
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
return filepath.SkipDir
}
name := filepath.ToSlash(path[len(src):])
if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
// The name "std" is only the standard library.
// If the name is cmd, it's the root of the command tree.
return filepath.SkipDir
}
if !treeCanMatch(name) {
return filepath.SkipDir
}
if have[name] {
return nil
}
have[name] = true
if !match(name) {
return nil
}
_, err = cfg.BuildContext.ImportDir(path, 0)
if err != nil {
if _, noGo := err.(*build.NoGoError); noGo {
return nil
}
}
pkgs = append(pkgs, name)
return nil
})
}
return pkgs
}
// MatchPackagesInFS returns a list of package paths matching pattern,
// which must begin with ./ or ../
// (see go help packages for pattern syntax).
func MatchPackagesInFS(pattern string) []string {
// Find directory to begin the scan.
// Could be smarter but this one optimization
// is enough for now, since ... is usually at the
// end of a path.
i := strings.Index(pattern, "...")
dir, _ := path.Split(pattern[:i])
// pattern begins with ./ or ../.
// path.Clean will discard the ./ but not the ../.
// We need to preserve the ./ for pattern matching
// and in the returned import paths.
prefix := ""
if strings.HasPrefix(pattern, "./") {
prefix = "./"
}
match := matchPattern(pattern)
var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() {
return nil
}
if path == dir {
// filepath.Walk starts at dir and recurses. For the recursive case,
// the path is the result of filepath.Join, which calls filepath.Clean.
// The initial case is not Cleaned, though, so we do this explicitly.
//
// This converts a path like "./io/" to "io". Without this step, running
// "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
// package, because prepending the prefix "./" to the unclean path would
// result in "././io", and match("././io") returns false.
path = filepath.Clean(path)
}
// Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
_, elem := filepath.Split(path)
dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
return filepath.SkipDir
}
name := prefix + filepath.ToSlash(path)
if !match(name) {
return nil
}
// We keep the directory if we can import it, or if we can't import it
// due to invalid Go source files. This means that directories containing
// parse errors will be built (and fail) instead of being silently skipped
// as not matching the pattern. Go 1.5 and earlier skipped, but that
// behavior means people miss serious mistakes.
// See golang.org/issue/11407.
if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
if _, noGo := err.(*build.NoGoError); !noGo {
log.Print(err)
}
return nil
}
pkgs = append(pkgs, name)
return nil
})
return pkgs
}
// treeCanMatchPattern(pattern)(name) reports whether
// name or children of name can possibly match pattern.
// Pattern is the same limited glob accepted by matchPattern.
func treeCanMatchPattern(pattern string) func(name string) bool {
wildCard := false
if i := strings.Index(pattern, "..."); i >= 0 {
wildCard = true
pattern = pattern[:i]
}
return func(name string) bool {
return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
wildCard && strings.HasPrefix(name, pattern)
}
}
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
func matchPattern(pattern string) func(name string) bool {
re := regexp.QuoteMeta(pattern)
re = strings.Replace(re, `\.\.\.`, `.*`, -1)
// Special case: foo/... matches foo too.
if strings.HasSuffix(re, `/.*`) {
re = re[:len(re)-len(`/.*`)] + `(/.*)?`
}
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
return reg.MatchString(name)
}
}
// 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
}
// ImportPathsNoDotExpansion returns the import paths to use for the given
// command line, but it does no ... expansion.
func ImportPathsNoDotExpansion(args []string) []string {
if len(args) == 0 {
return []string{"."}
}
var out []string
for _, a := range args {
// Arguments are supposed to be import paths, but
// as a courtesy to Windows developers, rewrite \ to /
// in command-line arguments. Handles .\... and so on.
if filepath.Separator == '\\' {
a = strings.Replace(a, `\`, `/`, -1)
}
// Put argument in canonical form, but preserve leading ./.
if strings.HasPrefix(a, "./") {
a = "./" + path.Clean(a)
if a == "./." {
a = "."
}
} else {
a = path.Clean(a)
}
if IsMetaPackage(a) {
out = append(out, allPackages(a)...)
continue
}
out = append(out, a)
}
return out
}
// isMetaPackage checks if name is a reserved package name that expands to multiple packages.
func IsMetaPackage(name string) bool {
return name == "std" || name == "cmd" || name == "all"
}
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
// +build testgo // +build testgo
package main package load
import "os" import "os"
......
...@@ -6,8 +6,9 @@ package main ...@@ -6,8 +6,9 @@ package main
import ( import (
"bufio" "bufio"
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"encoding/json" "encoding/json"
"io" "io"
"os" "os"
...@@ -152,9 +153,9 @@ func runList(cmd *base.Command, args []string) { ...@@ -152,9 +153,9 @@ func runList(cmd *base.Command, args []string) {
out := newTrackingWriter(os.Stdout) out := newTrackingWriter(os.Stdout)
defer out.w.Flush() defer out.w.Flush()
var do func(*Package) var do func(*load.PackagePublic)
if *listJson { if *listJson {
do = func(p *Package) { do = func(p *load.PackagePublic) {
b, err := json.MarshalIndent(p, "", "\t") b, err := json.MarshalIndent(p, "", "\t")
if err != nil { if err != nil {
out.Flush() out.Flush()
...@@ -179,7 +180,7 @@ func runList(cmd *base.Command, args []string) { ...@@ -179,7 +180,7 @@ func runList(cmd *base.Command, args []string) {
if err != nil { if err != nil {
base.Fatalf("%s", err) base.Fatalf("%s", err)
} }
do = func(p *Package) { do = func(p *load.PackagePublic) {
if err := tmpl.Execute(out, p); err != nil { if err := tmpl.Execute(out, p); err != nil {
out.Flush() out.Flush()
base.Fatalf("%s", err) base.Fatalf("%s", err)
...@@ -190,17 +191,17 @@ func runList(cmd *base.Command, args []string) { ...@@ -190,17 +191,17 @@ func runList(cmd *base.Command, args []string) {
} }
} }
load := packages loadpkgs := load.Packages
if *listE { if *listE {
load = packagesAndErrors loadpkgs = load.PackagesAndErrors
} }
for _, pkg := range load(args) { for _, pkg := range loadpkgs(args) {
// Show vendor-expanded paths in listing // Show vendor-expanded paths in listing
pkg.TestImports = pkg.vendored(pkg.TestImports) pkg.TestImports = pkg.Vendored(pkg.TestImports)
pkg.XTestImports = pkg.vendored(pkg.XTestImports) pkg.XTestImports = pkg.Vendored(pkg.XTestImports)
do(pkg) do(&pkg.PackagePublic)
} }
} }
......
...@@ -7,12 +7,9 @@ package main ...@@ -7,12 +7,9 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"go/build"
"log" "log"
"os" "os"
"path"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strings" "strings"
...@@ -89,8 +86,8 @@ func main() { ...@@ -89,8 +86,8 @@ func main() {
} }
} }
if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() { if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() {
fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot) fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT)
os.Exit(2) os.Exit(2)
} }
...@@ -145,57 +142,6 @@ func mainUsage() { ...@@ -145,57 +142,6 @@ func mainUsage() {
os.Exit(2) os.Exit(2)
} }
// importPathsNoDotExpansion returns the import paths to use for the given
// command line, but it does no ... expansion.
func importPathsNoDotExpansion(args []string) []string {
if len(args) == 0 {
return []string{"."}
}
var out []string
for _, a := range args {
// Arguments are supposed to be import paths, but
// as a courtesy to Windows developers, rewrite \ to /
// in command-line arguments. Handles .\... and so on.
if filepath.Separator == '\\' {
a = strings.Replace(a, `\`, `/`, -1)
}
// Put argument in canonical form, but preserve leading ./.
if strings.HasPrefix(a, "./") {
a = "./" + path.Clean(a)
if a == "./." {
a = "."
}
} else {
a = path.Clean(a)
}
if isMetaPackage(a) {
out = append(out, allPackages(a)...)
continue
}
out = append(out, a)
}
return out
}
// 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
}
// envForDir returns a copy of the environment // envForDir returns a copy of the environment
// suitable for running in the given directory. // suitable for running in the given directory.
// The environment is the current process's environment // The environment is the current process's environment
...@@ -225,235 +171,3 @@ NextVar: ...@@ -225,235 +171,3 @@ NextVar:
} }
return out return out
} }
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
func matchPattern(pattern string) func(name string) bool {
re := regexp.QuoteMeta(pattern)
re = strings.Replace(re, `\.\.\.`, `.*`, -1)
// Special case: foo/... matches foo too.
if strings.HasSuffix(re, `/.*`) {
re = re[:len(re)-len(`/.*`)] + `(/.*)?`
}
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
return reg.MatchString(name)
}
}
// hasPathPrefix reports whether the path s begins with the
// elements in prefix.
func hasPathPrefix(s, prefix string) bool {
switch {
default:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == '/' {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
}
}
// hasFilePathPrefix reports whether the filesystem path s begins with the
// elements in prefix.
func hasFilePathPrefix(s, prefix string) bool {
sv := strings.ToUpper(filepath.VolumeName(s))
pv := strings.ToUpper(filepath.VolumeName(prefix))
s = s[len(sv):]
prefix = prefix[len(pv):]
switch {
default:
return false
case sv != pv:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
}
}
// expandPath returns the symlink-expanded form of path.
func expandPath(p string) string {
x, err := filepath.EvalSymlinks(p)
if err == nil {
return x
}
return p
}
// treeCanMatchPattern(pattern)(name) reports whether
// name or children of name can possibly match pattern.
// Pattern is the same limited glob accepted by matchPattern.
func treeCanMatchPattern(pattern string) func(name string) bool {
wildCard := false
if i := strings.Index(pattern, "..."); i >= 0 {
wildCard = true
pattern = pattern[:i]
}
return func(name string) bool {
return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
wildCard && strings.HasPrefix(name, pattern)
}
}
// allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT matching pattern.
// The pattern is either "all" (all packages), "std" (standard packages),
// "cmd" (standard commands), or a path including "...".
func allPackages(pattern string) []string {
pkgs := matchPackages(pattern)
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs
}
func matchPackages(pattern string) []string {
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if !isMetaPackage(pattern) {
match = matchPattern(pattern)
treeCanMatch = treeCanMatchPattern(pattern)
}
have := map[string]bool{
"builtin": true, // ignore pseudo-package that exists only for documentation
}
if !cfg.BuildContext.CgoEnabled {
have["runtime/cgo"] = true // ignore during walk
}
var pkgs []string
for _, src := range cfg.BuildContext.SrcDirs() {
if (pattern == "std" || pattern == "cmd") && src != gorootSrc {
continue
}
src = filepath.Clean(src) + string(filepath.Separator)
root := src
if pattern == "cmd" {
root += "cmd" + string(filepath.Separator)
}
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() || path == src {
return nil
}
// Avoid .foo, _foo, and testdata directory trees.
_, elem := filepath.Split(path)
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
return filepath.SkipDir
}
name := filepath.ToSlash(path[len(src):])
if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
// The name "std" is only the standard library.
// If the name is cmd, it's the root of the command tree.
return filepath.SkipDir
}
if !treeCanMatch(name) {
return filepath.SkipDir
}
if have[name] {
return nil
}
have[name] = true
if !match(name) {
return nil
}
_, err = cfg.BuildContext.ImportDir(path, 0)
if err != nil {
if _, noGo := err.(*build.NoGoError); noGo {
return nil
}
}
pkgs = append(pkgs, name)
return nil
})
}
return pkgs
}
// allPackagesInFS is like allPackages but is passed a pattern
// beginning ./ or ../, meaning it should scan the tree rooted
// at the given directory. There are ... in the pattern too.
func allPackagesInFS(pattern string) []string {
pkgs := matchPackagesInFS(pattern)
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs
}
func matchPackagesInFS(pattern string) []string {
// Find directory to begin the scan.
// Could be smarter but this one optimization
// is enough for now, since ... is usually at the
// end of a path.
i := strings.Index(pattern, "...")
dir, _ := path.Split(pattern[:i])
// pattern begins with ./ or ../.
// path.Clean will discard the ./ but not the ../.
// We need to preserve the ./ for pattern matching
// and in the returned import paths.
prefix := ""
if strings.HasPrefix(pattern, "./") {
prefix = "./"
}
match := matchPattern(pattern)
var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() {
return nil
}
if path == dir {
// filepath.Walk starts at dir and recurses. For the recursive case,
// the path is the result of filepath.Join, which calls filepath.Clean.
// The initial case is not Cleaned, though, so we do this explicitly.
//
// This converts a path like "./io/" to "io". Without this step, running
// "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
// package, because prepending the prefix "./" to the unclean path would
// result in "././io", and match("././io") returns false.
path = filepath.Clean(path)
}
// Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
_, elem := filepath.Split(path)
dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
return filepath.SkipDir
}
name := prefix + filepath.ToSlash(path)
if !match(name) {
return nil
}
// We keep the directory if we can import it, or if we can't import it
// due to invalid Go source files. This means that directories containing
// parse errors will be built (and fail) instead of being silently skipped
// as not matching the pattern. Go 1.5 and earlier skipped, but that
// behavior means people miss serious mistakes.
// See golang.org/issue/11407.
if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
if _, noGo := err.(*build.NoGoError); !noGo {
log.Print(err)
}
return nil
}
pkgs = append(pkgs, name)
return nil
})
return pkgs
}
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str" "cmd/go/internal/str"
"io/ioutil" "io/ioutil"
"os" "os"
...@@ -87,71 +88,79 @@ func TestParseMetaGoImports(t *testing.T) { ...@@ -87,71 +88,79 @@ func TestParseMetaGoImports(t *testing.T) {
} }
} }
func pkgImportPath(path string) *load.Package {
return &load.Package{
PackagePublic: load.PackagePublic{
ImportPath: path,
},
}
}
func TestSharedLibName(t *testing.T) { func TestSharedLibName(t *testing.T) {
// TODO(avdva) - make these values platform-specific // TODO(avdva) - make these values platform-specific
prefix := "lib" prefix := "lib"
suffix := ".so" suffix := ".so"
testData := []struct { testData := []struct {
args []string args []string
pkgs []*Package pkgs []*load.Package
expected string expected string
expectErr bool expectErr bool
rootedAt string rootedAt string
}{ }{
{ {
args: []string{"std"}, args: []string{"std"},
pkgs: []*Package{}, pkgs: []*load.Package{},
expected: "std", expected: "std",
}, },
{ {
args: []string{"std", "cmd"}, args: []string{"std", "cmd"},
pkgs: []*Package{}, pkgs: []*load.Package{},
expected: "std,cmd", expected: "std,cmd",
}, },
{ {
args: []string{}, args: []string{},
pkgs: []*Package{&Package{ImportPath: "gopkg.in/somelib"}}, pkgs: []*load.Package{pkgImportPath("gopkg.in/somelib")},
expected: "gopkg.in-somelib", expected: "gopkg.in-somelib",
}, },
{ {
args: []string{"./..."}, args: []string{"./..."},
pkgs: []*Package{&Package{ImportPath: "somelib"}}, pkgs: []*load.Package{pkgImportPath("somelib")},
expected: "somelib", expected: "somelib",
rootedAt: "somelib", rootedAt: "somelib",
}, },
{ {
args: []string{"../somelib", "../somelib"}, args: []string{"../somelib", "../somelib"},
pkgs: []*Package{&Package{ImportPath: "somelib"}}, pkgs: []*load.Package{pkgImportPath("somelib")},
expected: "somelib", expected: "somelib",
}, },
{ {
args: []string{"../lib1", "../lib2"}, args: []string{"../lib1", "../lib2"},
pkgs: []*Package{&Package{ImportPath: "gopkg.in/lib1"}, &Package{ImportPath: "gopkg.in/lib2"}}, pkgs: []*load.Package{pkgImportPath("gopkg.in/lib1"), pkgImportPath("gopkg.in/lib2")},
expected: "gopkg.in-lib1,gopkg.in-lib2", expected: "gopkg.in-lib1,gopkg.in-lib2",
}, },
{ {
args: []string{"./..."}, args: []string{"./..."},
pkgs: []*Package{ pkgs: []*load.Package{
&Package{ImportPath: "gopkg.in/dir/lib1"}, pkgImportPath("gopkg.in/dir/lib1"),
&Package{ImportPath: "gopkg.in/lib2"}, pkgImportPath("gopkg.in/lib2"),
&Package{ImportPath: "gopkg.in/lib3"}, pkgImportPath("gopkg.in/lib3"),
}, },
expected: "gopkg.in", expected: "gopkg.in",
rootedAt: "gopkg.in", rootedAt: "gopkg.in",
}, },
{ {
args: []string{"std", "../lib2"}, args: []string{"std", "../lib2"},
pkgs: []*Package{}, pkgs: []*load.Package{},
expectErr: true, expectErr: true,
}, },
{ {
args: []string{"all", "./"}, args: []string{"all", "./"},
pkgs: []*Package{}, pkgs: []*load.Package{},
expectErr: true, expectErr: true,
}, },
{ {
args: []string{"cmd", "fmt"}, args: []string{"cmd", "fmt"},
pkgs: []*Package{}, pkgs: []*load.Package{},
expectErr: true, expectErr: true,
}, },
} }
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str" "cmd/go/internal/str"
"fmt" "fmt"
"os" "os"
...@@ -87,17 +88,17 @@ func runRun(cmd *base.Command, args []string) { ...@@ -87,17 +88,17 @@ func runRun(cmd *base.Command, args []string) {
base.Fatalf("go run: cannot run *_test.go files (%s)", file) base.Fatalf("go run: cannot run *_test.go files (%s)", file)
} }
} }
p := goFilesPackage(files) p := load.GoFilesPackage(files)
if p.Error != nil { if p.Error != nil {
base.Fatalf("%s", p.Error) base.Fatalf("%s", p.Error)
} }
p.omitDWARF = true p.Internal.OmitDWARF = true
if len(p.DepsErrors) > 0 { if len(p.DepsErrors) > 0 {
// Since these are errors in dependencies, // Since these are errors in dependencies,
// the same error might show up multiple times, // the same error might show up multiple times,
// once in each package that depends on it. // once in each package that depends on it.
// Only print each once. // Only print each once.
printed := map[*PackageError]bool{} printed := map[*load.PackageError]bool{}
for _, err := range p.DepsErrors { for _, err := range p.DepsErrors {
if !printed[err] { if !printed[err] {
printed[err] = true printed[err] = true
...@@ -109,7 +110,7 @@ func runRun(cmd *base.Command, args []string) { ...@@ -109,7 +110,7 @@ func runRun(cmd *base.Command, args []string) {
if p.Name != "main" { if p.Name != "main" {
base.Fatalf("go run: cannot run non-main package") base.Fatalf("go run: cannot run non-main package")
} }
p.target = "" // must build - not up to date p.Internal.Target = "" // must build - not up to date
var src string var src string
if len(p.GoFiles) > 0 { if len(p.GoFiles) > 0 {
src = p.GoFiles[0] src = p.GoFiles[0]
...@@ -124,7 +125,7 @@ func runRun(cmd *base.Command, args []string) { ...@@ -124,7 +125,7 @@ func runRun(cmd *base.Command, args []string) {
} }
base.Fatalf("go run: no suitable source files%s", hint) base.Fatalf("go run: no suitable source files%s", hint)
} }
p.exeName = src[:len(src)-len(".go")] // name temporary executable for first go file p.Internal.ExeName = src[:len(src)-len(".go")] // name temporary executable for first go file
a1 := b.action(modeBuild, modeBuild, p) a1 := b.action(modeBuild, modeBuild, p)
a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}} a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}}
b.do(a) b.do(a)
......
This diff is collapsed.
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
package main package main
import ( import (
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"flag" "flag"
"fmt" "fmt"
"os" "os"
......
...@@ -11,8 +11,8 @@ import ( ...@@ -11,8 +11,8 @@ import (
"sort" "sort"
"strings" "strings"
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
) )
var cmdTool = &base.Command{ var cmdTool = &base.Command{
...@@ -72,7 +72,7 @@ func runTool(cmd *base.Command, args []string) { ...@@ -72,7 +72,7 @@ func runTool(cmd *base.Command, args []string) {
Stdout: os.Stdout, Stdout: os.Stdout,
Stderr: os.Stderr, Stderr: os.Stderr,
// Set $GOROOT, mainly for go tool dist. // Set $GOROOT, mainly for go tool dist.
Env: mergeEnvLists([]string{"GOROOT=" + goroot}, os.Environ()), Env: mergeEnvLists([]string{"GOROOT=" + cfg.GOROOT}, os.Environ()),
} }
err := toolCmd.Run() err := toolCmd.Run()
if err != nil { if err != nil {
......
...@@ -7,8 +7,9 @@ package main ...@@ -7,8 +7,9 @@ package main
import ( import (
"path/filepath" "path/filepath"
"cmd/go/internal/cfg"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str" "cmd/go/internal/str"
) )
...@@ -38,7 +39,7 @@ See also: go fmt, go fix. ...@@ -38,7 +39,7 @@ See also: go fmt, go fix.
} }
func runVet(cmd *base.Command, args []string) { func runVet(cmd *base.Command, args []string) {
for _, p := range packages(args) { for _, p := range load.Packages(args) {
// Vet expects to be given a set of files all from the same package. // Vet expects to be given a set of files all from the same package.
// Run once for package p and once for package p_test. // Run once for package p and once for package p_test.
if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles) > 0 { if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles) > 0 {
...@@ -50,7 +51,7 @@ func runVet(cmd *base.Command, args []string) { ...@@ -50,7 +51,7 @@ func runVet(cmd *base.Command, args []string) {
} }
} }
func runVetFiles(p *Package, files []string) { func runVetFiles(p *load.Package, files []string) {
for i := range files { for i := range files {
files[i] = filepath.Join(p.Dir, files[i]) files[i] = filepath.Join(p.Dir, files[i])
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment