cmd/go/internal/fsys: minor cleanup

Rename canonicalize to abs.
Rename IsDirWithGoFiles to IsGoDir.
Remove Init argument.
Split OverlayPath into Actual and Renamed.
Clean up doc comments.
Other minor cleanups.

Preparation for larger changes.

Change-Id: Ida022588149a1618a63acc91e3800b09df873b6e
Reviewed-on: https://go-review.googlesource.com/c/go/+/628697
TryBot-Bypass: Russ Cox <rsc@golang.org>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Russ Cox 2024-11-15 12:54:37 -05:00 committed by Gopher Robot
parent 0b4cde34ac
commit f174c31f3e
13 changed files with 193 additions and 202 deletions

View File

@ -306,7 +306,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
if err := fsys.Init(base.Cwd()); err != nil {
if err := fsys.Init(); err != nil {
base.Fatal(err)
}

View File

@ -2,8 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fsys is an abstraction for reading files that
// allows for virtual overlays on top of the files on disk.
// Package fsys implements a virtual file system that the go command
// uses to read source file trees. The virtual file system redirects some
// OS file paths to other OS file paths, according to an overlay file.
// Editors can use this overlay support to invoke the go command on
// temporary files that have been edited but not yet saved into their
// final locations.
package fsys
import (
@ -14,10 +18,12 @@ import (
"io"
"io/fs"
"log"
"maps"
"os"
pathpkg "path"
"path/filepath"
"runtime/debug"
"slices"
"sort"
"strings"
"sync"
@ -70,16 +76,15 @@ func init() {
}
}
// OverlayFile is the path to a text file in the OverlayJSON format.
// It is the value of the -overlay flag.
// OverlayFile is the -overlay flag value.
// It names a file containing the JSON for an overlayJSON struct.
var OverlayFile string
// OverlayJSON is the format overlay files are expected to be in.
// The Replace map maps from overlaid paths to replacement paths:
// the Go command will forward all reads trying to open
// each overlaid path to its replacement path, or consider the overlaid
// path not to exist if the replacement path is empty.
type OverlayJSON struct {
// overlayJSON is the format for the -overlay file.
type overlayJSON struct {
// Replace maps file names observed by Go tools
// to the actual files that should be used when those are read.
// If the actual name is "", the file should appear to be deleted.
Replace map[string]string
}
@ -98,15 +103,23 @@ func (n *node) isDeleted() bool {
// TODO(matloob): encapsulate these in an io/fs-like interface
var overlay map[string]*node // path -> file or directory node
var cwd string // copy of base.Cwd() to avoid dependency
// canonicalize a path for looking it up in the overlay.
// Important: filepath.Join(cwd, path) doesn't always produce
// the correct absolute path if path is relative, because on
// Windows producing the correct absolute path requires making
// a syscall. So this should only be used when looking up paths
// in the overlay, or canonicalizing the paths in the overlay.
func canonicalize(path string) string {
// cwd returns the current directory, caching it on first use.
var cwd = sync.OnceValue(cwdOnce)
func cwdOnce() string {
wd, err := os.Getwd()
if err != nil {
// Note: cannot import base, so using log.Fatal.
log.Fatalf("cannot determine current directory: %v", err)
}
return wd
}
// abs returns the absolute form of path, for looking up in the overlay map.
// For the most part, this is filepath.Abs and filepath.Clean,
// except that Windows requires special handling, as always.
func abs(path string) string {
if path == "" {
return ""
}
@ -114,28 +127,24 @@ func canonicalize(path string) string {
return filepath.Clean(path)
}
if v := filepath.VolumeName(cwd); v != "" && path[0] == filepath.Separator {
// On Windows filepath.Join(cwd, path) doesn't always work. In general
// filepath.Abs needs to make a syscall on Windows. Elsewhere in cmd/go
// use filepath.Join(cwd, path), but cmd/go specifically supports Windows
// paths that start with "\" which implies the path is relative to the
// volume of the working directory. See golang.org/issue/8130.
return filepath.Join(v, path)
dir := cwd()
if vol := filepath.VolumeName(dir); vol != "" && (path[0] == '\\' || path[0] == '/') {
// path is volume-relative, like `\Temp`.
// Connect to volume name to make absolute path.
// See go.dev/issue/8130.
return filepath.Join(vol, path)
}
// Make the path absolute.
return filepath.Join(cwd, path)
return filepath.Join(dir, path)
}
// Init initializes the overlay, if one is being used.
func Init(wd string) error {
func Init() error {
if overlay != nil {
// already initialized
return nil
}
cwd = wd
if OverlayFile == "" {
return nil
}
@ -143,46 +152,39 @@ func Init(wd string) error {
Trace("ReadFile", OverlayFile)
b, err := os.ReadFile(OverlayFile)
if err != nil {
return fmt.Errorf("reading overlay file: %v", err)
return fmt.Errorf("reading overlay: %v", err)
}
var overlayJSON OverlayJSON
if err := json.Unmarshal(b, &overlayJSON); err != nil {
return fmt.Errorf("parsing overlay JSON: %v", err)
}
return initFromJSON(overlayJSON)
return initFromJSON(b)
}
func initFromJSON(overlayJSON OverlayJSON) error {
func initFromJSON(js []byte) error {
var ojs overlayJSON
if err := json.Unmarshal(js, &ojs); err != nil {
return err
}
// Canonicalize the paths in the overlay map.
// Use reverseCanonicalized to check for collisions:
// no two 'from' paths should canonicalize to the same path.
// no two 'from' paths should abs to the same path.
overlay = make(map[string]*node)
reverseCanonicalized := make(map[string]string) // inverse of canonicalize operation, to check for duplicates
reverseCanonicalized := make(map[string]string) // inverse of abs operation, to check for duplicates
// Build a table of file and directory nodes from the replacement map.
// Remove any potential non-determinism from iterating over map by sorting it.
replaceFrom := make([]string, 0, len(overlayJSON.Replace))
for k := range overlayJSON.Replace {
replaceFrom = append(replaceFrom, k)
}
sort.Strings(replaceFrom)
for _, from := range replaceFrom {
to := overlayJSON.Replace[from]
for _, from := range slices.Sorted(maps.Keys(ojs.Replace)) {
to := ojs.Replace[from]
// Canonicalize paths and check for a collision.
if from == "" {
return fmt.Errorf("empty string key in overlay file Replace map")
}
cfrom := canonicalize(from)
cfrom := abs(from)
if to != "" {
// Don't canonicalize "", meaning to delete a file, because then it will turn into ".".
to = canonicalize(to)
// Don't abs "", meaning to delete a file, because then it will turn into ".".
to = abs(to)
}
if otherFrom, seen := reverseCanonicalized[cfrom]; seen {
return fmt.Errorf(
"paths %q and %q both canonicalize to %q in overlay file Replace map", otherFrom, from, cfrom)
"paths %q and %q both abs to %q in overlay file Replace map", otherFrom, from, cfrom)
}
reverseCanonicalized[cfrom] = from
from = cfrom
@ -247,7 +249,7 @@ func initFromJSON(overlayJSON OverlayJSON) error {
// overlay.
func IsDir(path string) (bool, error) {
Trace("IsDir", path)
path = canonicalize(path)
path = abs(path)
if _, ok := parentIsOverlayFile(path); ok {
return false, nil
@ -290,8 +292,8 @@ func parentIsOverlayFile(name string) (string, bool) {
return "", false
}
// errNotDir is used to communicate from ReadDir to IsDirWithGoFiles
// that the argument is not a directory, so that IsDirWithGoFiles doesn't
// errNotDir is used to communicate from ReadDir to IsGoDir
// that the argument is not a directory, so that IsGoDir doesn't
// return an error.
var errNotDir = errors.New("not a directory")
@ -299,49 +301,46 @@ func nonFileInOverlayError(overlayPath string) error {
return fmt.Errorf("replacement path %q is a directory, not a file", overlayPath)
}
// readDir reads a dir on disk, returning an error that is errNotDir if the dir is not a directory.
// Unfortunately, the error returned by os.ReadDir if dir is not a directory
// can vary depending on the OS (Linux, Mac, Windows return ENOTDIR; BSD returns EINVAL).
func readDir(dir string) ([]fs.FileInfo, error) {
entries, err := os.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return nil, err
// osReadDir is like os.ReadDir but returns []fs.FileInfo and corrects the error to be errNotDir
// if the problem is that name exists but is not a directory.
func osReadDir(name string) ([]fs.FileInfo, error) {
dirs, err := os.ReadDir(name)
if err != nil && !os.IsNotExist(err) {
if info, err := os.Stat(name); err == nil && !info.IsDir() {
return nil, &fs.PathError{Op: "ReadDir", Path: name, Err: errNotDir}
}
if dirfi, staterr := os.Stat(dir); staterr == nil && !dirfi.IsDir() {
return nil, &fs.PathError{Op: "ReadDir", Path: dir, Err: errNotDir}
}
return nil, err
}
fis := make([]fs.FileInfo, 0, len(entries))
for _, entry := range entries {
info, err := entry.Info()
// Convert dirs to infos, even if there is an error,
// so that we preserve any partial read from os.ReadDir.
infos := make([]fs.FileInfo, 0, len(dirs))
for _, dir := range dirs {
info, err := dir.Info()
if err != nil {
continue
}
fis = append(fis, info)
infos = append(infos, info)
}
return fis, nil
return infos, err
}
// ReadDir provides a slice of fs.FileInfo entries corresponding
// to the overlaid files in the directory.
// ReadDir reads the named directory in the virtual file system.
func ReadDir(dir string) ([]fs.FileInfo, error) {
Trace("ReadDir", dir)
dir = canonicalize(dir)
dir = abs(dir)
if _, ok := parentIsOverlayFile(dir); ok {
return nil, &fs.PathError{Op: "ReadDir", Path: dir, Err: errNotDir}
}
dirNode := overlay[dir]
if dirNode == nil {
return readDir(dir)
return osReadDir(dir)
}
if dirNode.isDeleted() {
return nil, &fs.PathError{Op: "ReadDir", Path: dir, Err: fs.ErrNotExist}
}
diskfis, err := readDir(dir)
diskfis, err := osReadDir(dir)
if err != nil && !os.IsNotExist(err) && !errors.Is(err, errNotDir) {
return nil, err
}
@ -383,28 +382,31 @@ func ReadDir(dir string) ([]fs.FileInfo, error) {
return sortedFiles, nil
}
// OverlayPath returns the path to the overlaid contents of the
// file, the empty string if the overlay deletes the file, or path
// itself if the file is not in the overlay, the file is a directory
// in the overlay, or there is no overlay.
// It returns true if the path is overlaid with a regular file
// or deleted, and false otherwise.
func OverlayPath(path string) (string, bool) {
if p, ok := overlay[canonicalize(path)]; ok && !p.isDir() {
return p.actualFilePath, ok
// Actual returns the actual file system path for the named file.
// It returns the empty string if name has been deleted in the virtual file system.
func Actual(name string) string {
if p, ok := overlay[abs(name)]; ok && !p.isDir() {
return p.actualFilePath
}
return path, false
return name
}
// Open opens the file at or overlaid on the given path.
func Open(path string) (*os.File, error) {
Trace("Open", path)
return openFile(path, os.O_RDONLY, 0)
// Replaced reports whether the named file has been modified
// in the virtual file system compared to the OS file system.
func Replaced(name string) bool {
p, ok := overlay[abs(name)]
return ok && !p.isDir()
}
// Open opens the named file in the virtual file system.
// It must be an ordinary file, not a directory.
func Open(name string) (*os.File, error) {
Trace("Open", name)
return openFile(name, os.O_RDONLY, 0)
}
func openFile(path string, flag int, perm os.FileMode) (*os.File, error) {
cpath := canonicalize(path)
cpath := abs(path)
if node, ok := overlay[cpath]; ok {
// Opening a file in the overlay.
if node.isDir() {
@ -429,9 +431,10 @@ func openFile(path string, flag int, perm os.FileMode) (*os.File, error) {
return os.OpenFile(cpath, flag, perm)
}
// ReadFile reads the file at or overlaid on the given path.
func ReadFile(path string) ([]byte, error) {
f, err := Open(path)
// ReadFile reads the named file from the virtual file system
// and returns the contents.
func ReadFile(name string) ([]byte, error) {
f, err := Open(name)
if err != nil {
return nil, err
}
@ -440,11 +443,11 @@ func ReadFile(path string) ([]byte, error) {
return io.ReadAll(f)
}
// IsDirWithGoFiles reports whether dir is a directory containing Go files
// either on disk or in the overlay.
func IsDirWithGoFiles(dir string) (bool, error) {
Trace("IsDirWithGoFiles", dir)
fis, err := ReadDir(dir)
// IsGoDir reports whether the named directory in the virtual file system
// is a directory containing one or more Go source files.
func IsGoDir(name string) (bool, error) {
Trace("IsGoDir", name)
fis, err := ReadDir(name)
if os.IsNotExist(err) || errors.Is(err, errNotDir) {
return false, nil
}
@ -454,15 +457,7 @@ func IsDirWithGoFiles(dir string) (bool, error) {
var firstErr error
for _, fi := range fis {
if fi.IsDir() {
continue
}
// TODO(matloob): this enforces that the "from" in the map
// has a .go suffix, but the actual destination file
// doesn't need to have a .go suffix. Is this okay with the
// compiler?
if !strings.HasSuffix(fi.Name(), ".go") {
if fi.IsDir() || !strings.HasSuffix(fi.Name(), ".go") {
continue
}
if fi.Mode().IsRegular() {
@ -472,8 +467,7 @@ func IsDirWithGoFiles(dir string) (bool, error) {
// fi is the result of an Lstat, so it doesn't follow symlinks.
// But it's okay if the file is a symlink pointing to a regular
// file, so use os.Stat to follow symlinks and check that.
actualFilePath, _ := OverlayPath(filepath.Join(dir, fi.Name()))
fi, err := os.Stat(actualFilePath)
fi, err := os.Stat(Actual(filepath.Join(name, fi.Name())))
if err == nil && fi.Mode().IsRegular() {
return true, nil
}
@ -486,24 +480,26 @@ func IsDirWithGoFiles(dir string) (bool, error) {
return false, firstErr
}
// Lstat implements a version of os.Lstat that operates on the overlay filesystem.
func Lstat(path string) (fs.FileInfo, error) {
Trace("Lstat", path)
return overlayStat(path, os.Lstat, "lstat")
// Lstat returns a FileInfo describing the named file in the virtual file system.
// It does not follow symbolic links
func Lstat(name string) (fs.FileInfo, error) {
Trace("Lstat", name)
return overlayStat("lstat", name, os.Lstat)
}
// Stat implements a version of os.Stat that operates on the overlay filesystem.
func Stat(path string) (fs.FileInfo, error) {
Trace("Stat", path)
return overlayStat(path, os.Stat, "stat")
// Stat returns a FileInfo describing the named file in the virtual file system.
// It follows symbolic links.
func Stat(name string) (fs.FileInfo, error) {
Trace("Stat", name)
return overlayStat("stat", name, os.Stat)
}
// overlayStat implements lstat or Stat (depending on whether os.Lstat or os.Stat is passed in).
func overlayStat(path string, osStat func(string) (fs.FileInfo, error), opName string) (fs.FileInfo, error) {
cpath := canonicalize(path)
func overlayStat(op, path string, osStat func(string) (fs.FileInfo, error)) (fs.FileInfo, error) {
cpath := abs(path)
if _, ok := parentIsOverlayFile(filepath.Dir(cpath)); ok {
return nil, &fs.PathError{Op: opName, Path: cpath, Err: fs.ErrNotExist}
return nil, &fs.PathError{Op: op, Path: path, Err: fs.ErrNotExist}
}
node, ok := overlay[cpath]
@ -514,7 +510,7 @@ func overlayStat(path string, osStat func(string) (fs.FileInfo, error), opName s
switch {
case node.isDeleted():
return nil, &fs.PathError{Op: opName, Path: cpath, Err: fs.ErrNotExist}
return nil, &fs.PathError{Op: op, Path: path, Err: fs.ErrNotExist}
case node.isDir():
return fakeDir(filepath.Base(path)), nil
default:
@ -528,7 +524,7 @@ func overlayStat(path string, osStat func(string) (fs.FileInfo, error), opName s
return nil, err
}
if fi.IsDir() {
return nil, &fs.PathError{Op: opName, Path: cpath, Err: nonFileInOverlayError(node.actualFilePath)}
return nil, &fs.PathError{Op: op, Path: path, Err: nonFileInOverlayError(node.actualFilePath)}
}
return fakeFile{name: filepath.Base(path), real: fi}, nil
}

View File

@ -5,7 +5,6 @@
package fsys
import (
"encoding/json"
"errors"
"internal/testenv"
"internal/txtar"
@ -14,23 +13,26 @@ import (
"os"
"path/filepath"
"reflect"
"sync"
"testing"
)
func resetForTesting() {
cwd = sync.OnceValue(cwdOnce)
overlay = nil
}
// initOverlay resets the overlay state to reflect the config.
// config should be a text archive string. The comment is the overlay config
// json, and the files, in the archive are laid out in a temp directory
// that cwd is set to.
func initOverlay(t *testing.T, config string) {
t.Helper()
t.Chdir(t.TempDir())
resetForTesting()
t.Cleanup(resetForTesting)
// Create a temporary directory and chdir to it.
cwd = filepath.Join(t.TempDir(), "root")
if err := os.Mkdir(cwd, 0777); err != nil {
t.Fatal(err)
}
t.Chdir(cwd)
cwd := cwd()
a := txtar.Parse([]byte(config))
for _, f := range a.Files {
name := filepath.Join(cwd, f.Name)
@ -42,15 +44,9 @@ func initOverlay(t *testing.T, config string) {
}
}
var overlayJSON OverlayJSON
if err := json.Unmarshal(a.Comment, &overlayJSON); err != nil {
t.Fatal("parsing overlay JSON:", err)
}
if err := initFromJSON(overlayJSON); err != nil {
if err := initFromJSON(a.Comment); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { overlay = nil })
}
func TestIsDir(t *testing.T) {
@ -80,6 +76,7 @@ x
six
`)
cwd := cwd()
testCases := []struct {
path string
want, wantErr bool
@ -414,7 +411,7 @@ func TestGlob(t *testing.T) {
}
}
func TestOverlayPath(t *testing.T) {
func TestActual(t *testing.T) {
initOverlay(t, `
{
"Replace": {
@ -438,16 +435,17 @@ file 2
99999999
`)
cwd := cwd()
testCases := []struct {
path string
wantPath string
wantOK bool
}{
{"subdir1/file1.txt", "subdir1/file1.txt", false},
// OverlayPath returns false for directories
// Actual returns false for directories
{"subdir2", "subdir2", false},
{"subdir2/file2.txt", filepath.Join(cwd, "overlayfiles/subdir2_file2.txt"), true},
// OverlayPath doesn't stat a file to see if it exists, so it happily returns
// Actual doesn't stat a file to see if it exists, so it happily returns
// the 'to' path and true even if the 'to' path doesn't exist on disk.
{"subdir3/doesntexist", filepath.Join(cwd, "this_file_doesnt_exist_anywhere"), true},
// Like the subdir2/file2.txt case above, but subdir4 exists on disk, but subdir2 does not.
@ -457,10 +455,14 @@ file 2
}
for _, tc := range testCases {
gotPath, gotOK := OverlayPath(tc.path)
if gotPath != tc.wantPath || gotOK != tc.wantOK {
t.Errorf("OverlayPath(%q): got %v, %v; want %v, %v",
tc.path, gotPath, gotOK, tc.wantPath, tc.wantOK)
path := Actual(tc.path)
ok := Replaced(tc.path)
if path != tc.wantPath {
t.Errorf("Actual(%q) = %q, want %q", tc.path, path, tc.wantPath)
}
if ok != tc.wantOK {
t.Errorf("Replaced(%q) = %v, want %v", tc.path, ok, tc.wantOK)
}
}
}
@ -546,7 +548,7 @@ this can exist because the parent directory is deleted
}
}
func TestIsDirWithGoFiles(t *testing.T) {
func TestIsGoDir(t *testing.T) {
initOverlay(t, `
{
"Replace": {
@ -585,18 +587,18 @@ contents don't matter for this test
}
for _, tc := range testCases {
got, gotErr := IsDirWithGoFiles(tc.dir)
got, gotErr := IsGoDir(tc.dir)
if tc.wantErr {
if gotErr == nil {
t.Errorf("IsDirWithGoFiles(%q): got %v, %v; want non-nil error", tc.dir, got, gotErr)
t.Errorf("IsGoDir(%q): got %v, %v; want non-nil error", tc.dir, got, gotErr)
}
continue
}
if gotErr != nil {
t.Errorf("IsDirWithGoFiles(%q): got %v, %v; want nil error", tc.dir, got, gotErr)
t.Errorf("IsGoDir(%q): got %v, %v; want nil error", tc.dir, got, gotErr)
}
if got != tc.want {
t.Errorf("IsDirWithGoFiles(%q) = %v; want %v", tc.dir, got, tc.want)
t.Errorf("IsGoDir(%q) = %v; want %v", tc.dir, got, tc.want)
}
}
}

View File

@ -481,11 +481,11 @@ func readGoSumFile(dst map[module.Version][]string, file string) (bool, error) {
data []byte
err error
)
if actualSumFile, ok := fsys.OverlayPath(file); ok {
if fsys.Replaced(file) {
// Don't lock go.sum if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(actualSumFile)
data, err = os.ReadFile(fsys.Actual(file))
} else {
data, err = lockedfile.Read(file)
}
@ -861,7 +861,7 @@ Outer:
if readonly {
return ErrGoSumDirty
}
if _, ok := fsys.OverlayPath(GoSumFile); ok {
if fsys.Replaced(GoSumFile) {
base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
}

View File

@ -671,7 +671,7 @@ func IsStandardPackage(goroot_, compiler, path string) bool {
modroot = filepath.Join(modroot, "cmd")
}
if pkg, err := GetPackage(modroot, filepath.Join(modroot, reldir)); err == nil {
hasGo, err := pkg.IsDirWithGoFiles()
hasGo, err := pkg.IsGoDir()
return err == nil && hasGo
} else if errors.Is(err, ErrNotIndexed) {
// Fall back because package isn't indexable. (Probably because
@ -681,8 +681,8 @@ func IsStandardPackage(goroot_, compiler, path string) bool {
return false
}
// IsDirWithGoFiles is the equivalent of fsys.IsDirWithGoFiles using the information in the index.
func (rp *IndexPackage) IsDirWithGoFiles() (_ bool, err error) {
// IsGoDir is the equivalent of fsys.IsGoDir using the information in the index.
func (rp *IndexPackage) IsGoDir() (_ bool, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("error reading module index: %v", e)

View File

@ -719,13 +719,13 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
haveGoFiles, err = haveGoFilesCache.Do(dir, func() (bool, error) {
// modindex.GetPackage will return ErrNotIndexed for any directories which
// are reached through a symlink, so that they will be handled by
// fsys.IsDirWithGoFiles below.
// fsys.IsGoDir below.
if ip, err := modindex.GetPackage(mdir, dir); err == nil {
return ip.IsDirWithGoFiles()
return ip.IsGoDir()
} else if !errors.Is(err, modindex.ErrNotIndexed) {
return false, err
}
return fsys.IsDirWithGoFiles(dir)
return fsys.IsGoDir(dir)
})
return dir, haveGoFiles, err

View File

@ -355,7 +355,7 @@ func BinDir() string {
// for example 'go mod tidy', that don't operate in workspace mode.
func InitWorkfile() {
// Initialize fsys early because we need overlay to read go.work file.
if err := fsys.Init(base.Cwd()); err != nil {
if err := fsys.Init(); err != nil {
base.Fatal(err)
}
workFilePath = FindGoWork(base.Cwd())
@ -434,7 +434,7 @@ func Init() {
return
}
if err := fsys.Init(base.Cwd()); err != nil {
if err := fsys.Init(); err != nil {
base.Fatal(err)
}
@ -1938,7 +1938,7 @@ func commitRequirements(ctx context.Context, opts WriteOpts) (err error) {
mainModule := MainModules.mustGetSingleMainModule()
modFilePath := modFilePath(MainModules.ModRoot(mainModule))
if _, ok := fsys.OverlayPath(modFilePath); ok {
if fsys.Replaced(modFilePath) {
if dirty {
return errors.New("updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
}

View File

@ -34,13 +34,13 @@ func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfil
// so a more convenient path is displayed in the errors. ShortPath isn't used
// because it's meant only to be used in errors, not to open files.
gomod = base.ShortPathConservative(gomod)
if gomodActual, ok := fsys.OverlayPath(gomod); ok {
if fsys.Replaced(gomod) {
// Don't lock go.mod if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(gomodActual)
data, err = os.ReadFile(fsys.Actual(gomod))
} else {
data, err = lockedfile.Read(gomodActual)
data, err = lockedfile.Read(gomod)
}
if err != nil {
return nil, nil, err
@ -749,13 +749,13 @@ func rawGoModData(m module.Version) (name string, data []byte, err error) {
}
}
name = filepath.Join(dir, "go.mod")
if gomodActual, ok := fsys.OverlayPath(name); ok {
if fsys.Replaced(name) {
// Don't lock go.mod if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(gomodActual)
data, err = os.ReadFile(fsys.Actual(name))
} else {
data, err = lockedfile.Read(gomodActual)
data, err = lockedfile.Read(name)
}
if err != nil {
return "", nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(name), err))

View File

@ -398,8 +398,7 @@ func (b *Builder) buildID(file string) string {
// fileHash returns the content hash of the named file.
func (b *Builder) fileHash(file string) string {
file, _ = fsys.OverlayPath(file)
sum, err := cache.FileHash(file)
sum, err := cache.FileHash(fsys.Actual(file))
if err != nil {
return ""
}

View File

@ -617,7 +617,7 @@ func (b *Builder) build(ctx context.Context, a *Action) (err error) {
OverlayLoop:
for _, fs := range nonGoFileLists {
for _, f := range fs {
if _, ok := fsys.OverlayPath(mkAbs(p.Dir, f)); ok {
if fsys.Replaced(mkAbs(p.Dir, f)) {
a.nonGoOverlay = make(map[string]string)
break OverlayLoop
}
@ -627,9 +627,8 @@ OverlayLoop:
for _, fs := range nonGoFileLists {
for i := range fs {
from := mkAbs(p.Dir, fs[i])
opath, _ := fsys.OverlayPath(from)
dst := objdir + filepath.Base(fs[i])
if err := sh.CopyFile(dst, opath, 0666, false); err != nil {
if err := sh.CopyFile(dst, fsys.Actual(from), 0666, false); err != nil {
return err
}
a.nonGoOverlay[from] = dst
@ -2840,9 +2839,10 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
var trimpath []string
for i := range cgofiles {
path := mkAbs(p.Dir, cgofiles[i])
if opath, ok := fsys.OverlayPath(path); ok {
cgofiles[i] = opath
trimpath = append(trimpath, opath+"=>"+path)
if fsys.Replaced(path) {
actual := fsys.Actual(path)
cgofiles[i] = actual
trimpath = append(trimpath, actual+"=>"+path)
}
}
if len(trimpath) > 0 {

View File

@ -159,10 +159,10 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
for _, f := range gofiles {
f := mkAbs(p.Dir, f)
// Handle overlays. Convert path names using OverlayPath
// Handle overlays. Convert path names using fsys.Actual
// so these paths can be handed directly to tools.
// Deleted files won't show up in when scanning directories earlier,
// so OverlayPath will never return "" (meaning a deleted file) here.
// so Actual will never return "" (meaning a deleted file) here.
// TODO(#39958): Handle cases where the package directory
// doesn't exist on disk (this can happen when all the package's
// files are in an overlay): the code expects the package directory
@ -171,9 +171,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
// gofiles, cgofiles, cfiles, sfiles, and cxxfiles variables are
// created in (*Builder).build. Doing that requires rewriting the
// code that uses those values to expect absolute paths.
f, _ = fsys.OverlayPath(f)
args = append(args, f)
args = append(args, fsys.Actual(f))
}
output, err = sh.runOut(base.Cwd(), nil, args...)
@ -286,12 +284,12 @@ func (a *Action) trimpath() string {
base := filepath.Base(path)
isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s")
isCgo := cgoFiles[filename] || !isGo
overlayPath, isOverlay := fsys.OverlayPath(path)
if isCgo && isOverlay {
hasCgoOverlay = true
}
if !isCgo && isOverlay {
rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";"
if fsys.Replaced(path) {
if isCgo {
hasCgoOverlay = true
} else {
rewrite += fsys.Actual(path) + "=>" + filepath.Join(rewriteDir, base) + ";"
}
} else if isCgo {
// Generate rewrites for non-Go files copied to files in objdir.
if filepath.Dir(path) == a.Package.Dir {
@ -395,10 +393,9 @@ func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error)
var ofiles []string
for _, sfile := range sfiles {
overlayPath, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
ofiles = append(ofiles, ofile)
args1 := append(args, "-o", ofile, overlayPath)
args1 := append(args, "-o", ofile, fsys.Actual(mkAbs(p.Dir, sfile)))
if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, args1...); err != nil {
return nil, err
}
@ -416,8 +413,7 @@ func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, erro
if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
continue
}
op, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
args = append(args, op)
args = append(args, fsys.Actual(mkAbs(p.Dir, sfile)))
}
// Supply an empty go_asm.h as if the compiler had been run.

View File

@ -107,8 +107,7 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg,
if fsys.OverlayFile != "" {
for _, name := range gofiles {
absPath := mkAbs(p.Dir, name)
overlayPath, ok := fsys.OverlayPath(absPath)
if !ok {
if !fsys.Replaced(absPath) {
continue
}
toPath := absPath
@ -117,7 +116,7 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg,
if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd()) {
toPath = "." + toPath[len(base.Cwd()):]
}
args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath)
args = append(args, "-ffile-prefix-map="+fsys.Actual(absPath)+"="+toPath)
}
}
}
@ -127,8 +126,7 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg,
f := mkAbs(p.Dir, f)
// Overlay files if necessary.
// See comment on gctoolchain.gc about overlay TODOs
f, _ = fsys.OverlayPath(f)
args = append(args, f)
args = append(args, fsys.Actual(f))
}
output, err = sh.runOut(p.Dir, nil, args)
@ -200,7 +198,7 @@ func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]strin
base := filepath.Base(sfile)
ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
ofiles = append(ofiles, ofile)
sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile))
sfile = fsys.Actual(mkAbs(p.Dir, sfile))
defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)

View File

@ -36,7 +36,7 @@ func BuildInit() {
modload.Init()
instrumentInit()
buildModeInit()
if err := fsys.Init(base.Cwd()); err != nil {
if err := fsys.Init(); err != nil {
base.Fatal(err)
}