}
var (
- GOROOT string
- GOROOTbin string
- GOROOTpkg string
- GOROOTsrc string
+ GOROOT string
+
+ // Either empty or produced by filepath.Join(GOROOT, …).
+ GOROOTbin string
+ GOROOTpkg string
+ GOROOTsrc string
+
GOROOT_FINAL string
- GOBIN = Getenv("GOBIN")
- GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod"))
+
+ GOBIN = Getenv("GOBIN")
+ GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod"))
// Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(buildcfg.GOARM))
modroot := modload.PackageModRoot(ctx, r.path)
if modroot == "" && str.HasPathPrefix(r.dir, cfg.GOROOTsrc) {
modroot = cfg.GOROOTsrc
- if str.HasPathPrefix(r.dir, cfg.GOROOTsrc+string(filepath.Separator)+"cmd") {
- modroot += string(filepath.Separator) + "cmd"
+ gorootSrcCmd := filepath.Join(cfg.GOROOTsrc, "cmd")
+ if str.HasPathPrefix(r.dir, gorootSrcCmd) {
+ modroot = gorootSrcCmd
}
}
if modroot != "" {
return
}
elem := p.DefaultExecName() + cfg.ExeSuffix
- full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + string(filepath.Separator) + elem
+ full := filepath.Join(cfg.BuildContext.GOOS+"_"+cfg.BuildContext.GOARCH, elem)
if cfg.BuildContext.GOOS != runtime.GOOS || cfg.BuildContext.GOARCH != runtime.GOARCH {
// Install cross-compiled binaries to subdirectories of bin.
elem = full
}
// Glob to find matches.
- match, err := fsys.Glob(str.QuoteGlob(pkgdir) + string(filepath.Separator) + filepath.FromSlash(glob))
+ match, err := fsys.Glob(str.QuoteGlob(str.WithFilePathSeparator(pkgdir)) + filepath.FromSlash(glob))
if err != nil {
return nil, nil, err
}
import (
"bytes"
"cmd/go/internal/fsys"
+ "cmd/go/internal/str"
"errors"
"fmt"
"go/ast"
// hasSubdir reports if dir is within root by performing lexical analysis only.
func hasSubdir(root, dir string) (rel string, ok bool) {
- const sep = string(filepath.Separator)
- root = filepath.Clean(root)
- if !strings.HasSuffix(root, sep) {
- root += sep
- }
+ root = str.WithFilePathSeparator(filepath.Clean(root))
dir = filepath.Clean(dir)
if !strings.HasPrefix(dir, root) {
return "", false
pkgNotFoundLongestPrefix := ""
for _, mainModule := range MainModules.Versions() {
modRoot := MainModules.ModRoot(mainModule)
- if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") {
- suffix := filepath.ToSlash(absDir[len(modRoot):])
- if pkg, found := strings.CutPrefix(suffix, "/vendor/"); found {
+ if modRoot != "" && str.HasFilePathPrefix(absDir, modRoot) && !strings.Contains(absDir[len(modRoot):], "@") {
+ suffix := filepath.ToSlash(str.TrimFilePathPrefix(absDir, modRoot))
+ if pkg, found := strings.CutPrefix(suffix, "vendor/"); found {
if cfg.BuildMod != "vendor" {
return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir)
}
mainModulePrefix := MainModules.PathPrefix(mainModule)
if mainModulePrefix == "" {
- pkg := strings.TrimPrefix(suffix, "/")
+ pkg := suffix
if pkg == "builtin" {
// "builtin" is a pseudo-package with a real source file.
// It's not included in "std", so it shouldn't resolve from "."
return pkg, nil
}
- pkg := mainModulePrefix + suffix
+ pkg := pathpkg.Join(mainModulePrefix, suffix)
if _, ok, err := dirInModule(pkg, mainModulePrefix, modRoot, true); err != nil {
return "", err
} else if !ok {
if dir == modRoot {
return mms.PathPrefix(v), v
}
- if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
+ if str.HasFilePathPrefix(dir, modRoot) {
pathPrefix := MainModules.PathPrefix(v)
if pathPrefix > longestPrefix {
longestPrefix = pathPrefix
longestPrefixVersion = v
- suffix := filepath.ToSlash(dir[len(modRoot):])
- if strings.HasPrefix(suffix, "/vendor/") {
- longestPrefixPath = strings.TrimPrefix(suffix, "/vendor/")
+ suffix := filepath.ToSlash(str.TrimFilePathPrefix(dir, modRoot))
+ if strings.HasPrefix(suffix, "vendor/") {
+ longestPrefixPath = strings.TrimPrefix(suffix, "vendor/")
continue
}
- longestPrefixPath = mms.PathPrefix(v) + suffix
+ longestPrefixPath = pathpkg.Join(mms.PathPrefix(v), suffix)
}
}
}
defer span.Done()
root = filepath.Clean(root)
- err := fsys.Walk(root, func(path string, fi fs.FileInfo, err error) error {
+ err := fsys.Walk(root, func(pkgDir string, fi fs.FileInfo, err error) error {
if err != nil {
m.AddError(err)
return nil
elem := ""
// Don't use GOROOT/src but do walk down into it.
- if path == root {
+ if pkgDir == root {
if importPathRoot == "" {
return nil
}
} else {
// Avoid .foo, _foo, and testdata subdirectory trees.
- _, elem = filepath.Split(path)
+ _, elem = filepath.Split(pkgDir)
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
want = false
}
}
- name := importPathRoot + filepath.ToSlash(path[len(root):])
- if importPathRoot == "" {
- name = name[1:] // cut leading slash
- }
+ rel := strings.TrimPrefix(filepath.ToSlash(pkgDir[len(root):]), "/")
+ name := path.Join(importPathRoot, rel)
+
if !treeCanMatch(name) {
want = false
}
if !fi.IsDir() {
if fi.Mode()&fs.ModeSymlink != 0 && want && strings.Contains(m.Pattern(), "...") {
- if target, err := fsys.Stat(path); err == nil && target.IsDir() {
- fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+ if target, err := fsys.Stat(pkgDir); err == nil && target.IsDir() {
+ fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", pkgDir)
}
}
return nil
return filepath.SkipDir
}
// Stop at module boundaries.
- if (prune&pruneGoMod != 0) && path != root {
- if fi, err := os.Stat(filepath.Join(path, "go.mod")); err == nil && !fi.IsDir() {
+ if (prune&pruneGoMod != 0) && pkgDir != root {
+ if fi, err := os.Stat(filepath.Join(pkgDir, "go.mod")); err == nil && !fi.IsDir() {
return filepath.SkipDir
}
}
have[name] = true
if isMatch(name) {
q.Add(func() {
- if _, _, err := scanDir(root, path, tags); err != imports.ErrNoGo {
+ if _, _, err := scanDir(root, pkgDir, tags); err != imports.ErrNoGo {
addPkg(name)
}
})
return nil, ErrUsage
}
- // Use the script's PATH to look up the command if it contains a separator
+ // Use the script's PATH to look up the command (if it does not contain a separator)
// instead of the test process's PATH (see lookPath).
// Don't use filepath.Clean, since that changes "./foo" to "foo".
name := filepath.FromSlash(args[0])
pathEnv, _ := s.LookupEnv(pathEnvName())
for _, dir := range strings.Split(pathEnv, string(filepath.ListSeparator)) {
+ if dir == "" {
+ continue
+ }
+
+ // Determine whether dir needs a trailing path separator.
+ // Note: we avoid filepath.Join in this function because it cleans the
+ // result: we want to preserve the exact dir prefix from the environment.
+ sep := string(filepath.Separator)
+ if os.IsPathSeparator(dir[len(dir)-1]) {
+ sep = ""
+ }
+
if searchExt {
ents, err := os.ReadDir(dir)
if err != nil {
for _, ent := range ents {
for _, ext := range pathExt {
if !ent.IsDir() && strEqual(ent.Name(), command+ext) {
- return dir + string(filepath.Separator) + ent.Name(), nil
+ return dir + sep + ent.Name(), nil
}
}
}
} else {
- path := dir + string(filepath.Separator) + command
+ path := dir + sep + command
if fi, err := os.Stat(path); err == nil && isExecutable(fi) {
return path, nil
}
// originally created.
func (s *State) ExtractFiles(ar *txtar.Archive) error {
wd := s.workdir
+
// Add trailing separator to terminate wd.
// This prevents extracting to outside paths which prefix wd,
// e.g. extracting to /home/foobar when wd is /home/foo
- if !strings.HasSuffix(wd, string(filepath.Separator)) {
+ if wd == "" {
+ panic("s.workdir is unexpectedly empty")
+ }
+ if !os.IsPathSeparator(wd[len(wd)-1]) {
wd += string(filepath.Separator)
}
if (m.pattern == "std" || m.pattern == "cmd") && src != cfg.GOROOTsrc {
continue
}
- src = filepath.Clean(src) + string(filepath.Separator)
+ src = str.WithFilePathSeparator(filepath.Clean(src))
root := src
if m.pattern == "cmd" {
root += "cmd" + string(filepath.Separator)
return trimmed
}
+// WithFilePathSeparator returns s with a trailing path separator, or the empty
+// string if s is empty.
+func WithFilePathSeparator(s string) string {
+ if s == "" || os.IsPathSeparator(s[len(s)-1]) {
+ return s
+ }
+ return s + string(filepath.Separator)
+}
+
// QuoteGlob returns s with all Glob metacharacters quoted.
// We don't try to handle backslash here, as that can appear in a
// file path on Windows.
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/robustio"
+ "cmd/go/internal/str"
"cmd/go/internal/trace"
"cmd/internal/buildid"
)
// be called during action graph execution.
func (b *Builder) NewObjdir() string {
b.objdirSeq++
- return filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)) + string(filepath.Separator)
+ return str.WithFilePathSeparator(filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)))
}
// readpkglist returns the list of packages that were built into the shared library
//
// It is acceptable to edit the archive afterward to remove or shorten files.
// See mod/README for more information.
-//
package main
import (
if err != nil {
return err
}
- a.Files = append(a.Files, txtar.File{Name: strings.TrimPrefix(path, dir+string(filepath.Separator)), Data: data})
+ a.Files = append(a.Files, txtar.File{Name: str.TrimFilePathPrefix(path, dir), Data: data})
}
return nil
})
// go run savedir.go /path/to/dir >saved.txt
//
// Typically the tree is later extracted during a test with tg.extract("testdata/saved.txt").
-//
package main
import (
log.Printf("%s: ignoring invalid UTF-8 data", path)
return nil
}
- a.Files = append(a.Files, txtar.File{Name: strings.TrimPrefix(path, dir+string(filepath.Separator)), Data: data})
+ a.Files = append(a.Files, txtar.File{Name: str.TrimFilePathPrefix(path, dir), Data: data})
return nil
})