1
0
mirror of https://github.com/golang/go synced 2024-11-07 01:46:15 -07:00

cmd/go: remove deleted subdirectories in 'go work use'

Also remove absolute names (relative to PWD) when updating relative
directories, and relative names when updating absolute directories.

Fixes #50959

Change-Id: If129019cad7146e82face7f23427b28240d29cfc
Reviewed-on: https://go-review.googlesource.com/c/go/+/383837
Trust: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Bryan C. Mills 2022-02-02 13:56:22 -05:00 committed by Bryan Mills
parent ef06a5f44a
commit 9e0de1fe7b
4 changed files with 144 additions and 48 deletions

View File

@ -301,7 +301,7 @@ func InitWorkfile() {
} }
} }
// WorkFilePath returns the path of the go.work file, or "" if not in // WorkFilePath returns the absolute path of the go.work file, or "" if not in
// workspace mode. WorkFilePath must be called after InitWorkfile. // workspace mode. WorkFilePath must be called after InitWorkfile.
func WorkFilePath() string { func WorkFilePath() string {
return workFilePath return workFilePath

View File

@ -10,7 +10,10 @@ import (
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/fsys" "cmd/go/internal/fsys"
"cmd/go/internal/modload" "cmd/go/internal/modload"
"cmd/go/internal/str"
"context" "context"
"errors"
"fmt"
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
@ -56,44 +59,34 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
if err != nil { if err != nil {
base.Fatalf("go: %v", err) base.Fatalf("go: %v", err)
} }
workDir := filepath.Dir(gowork) // Absolute, since gowork itself is absolute.
haveDirs := make(map[string][]string) // absolute → original(s) haveDirs := make(map[string][]string) // absolute → original(s)
for _, use := range workFile.Use { for _, use := range workFile.Use {
var absDir string var abs string
if filepath.IsAbs(use.Path) { if filepath.IsAbs(use.Path) {
absDir = filepath.Clean(use.Path) abs = filepath.Clean(use.Path)
} else { } else {
absDir = filepath.Join(filepath.Dir(gowork), use.Path) abs = filepath.Join(workDir, use.Path)
} }
haveDirs[absDir] = append(haveDirs[absDir], use.Path) haveDirs[abs] = append(haveDirs[abs], use.Path)
} }
addDirs := make(map[string]bool) // keepDirs maps each absolute path to keep to the literal string to use for
removeDirs := make(map[string]bool) // that path (either an absolute or a relative path), or the empty string if
// all entries for the absolute path should be removed.
keepDirs := make(map[string]string)
// lookDir updates the entry in keepDirs for the directory dir,
// which is either absolute or relative to the current working directory
// (not necessarily the directory containing the workfile).
lookDir := func(dir string) { lookDir := func(dir string) {
// If the path is absolute, try to keep it absolute. If it's relative, absDir, dir := pathRel(workDir, dir)
// make it relative to the go.work file rather than the working directory.
absDir := dir
if !filepath.IsAbs(dir) {
absDir = filepath.Join(base.Cwd(), dir)
rel, err := filepath.Rel(filepath.Dir(gowork), absDir)
if err == nil {
// Normalize relative paths to use slashes, so that checked-in go.work
// files with relative paths within the repo are platform-independent.
dir = filepath.ToSlash(rel)
} else {
// The path can't be made relative to the go.work file,
// so it must be kept absolute instead.
dir = absDir
}
}
fi, err := os.Stat(filepath.Join(absDir, "go.mod")) fi, err := os.Stat(filepath.Join(absDir, "go.mod"))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
for _, origDir := range haveDirs[absDir] { keepDirs[absDir] = ""
removeDirs[origDir] = true
}
return return
} }
base.Errorf("go: %v", err) base.Errorf("go: %v", err)
@ -103,31 +96,96 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod")) base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod"))
} }
if len(haveDirs[absDir]) == 0 { if dup := keepDirs[absDir]; dup != "" && dup != dir {
addDirs[dir] = true base.Errorf(`go: already added "%s" as "%s"`, dir, dup)
} }
keepDirs[absDir] = dir
} }
for _, useDir := range args { for _, useDir := range args {
if *useR { if !*useR {
fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error { lookDir(useDir)
if !info.IsDir() {
return nil
}
lookDir(path)
return nil
})
continue continue
} }
lookDir(useDir)
// Add or remove entries for any subdirectories that still exist.
err := fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() {
if info.Mode()&fs.ModeSymlink != 0 {
if target, err := fsys.Stat(path); err == nil && target.IsDir() {
fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
}
}
return nil
}
lookDir(path)
return nil
})
if err != nil && !errors.Is(err, os.ErrNotExist) {
base.Errorf("go: %v", err)
}
// Remove entries for subdirectories that no longer exist.
// Because they don't exist, they will be skipped by Walk.
absArg, _ := pathRel(workDir, useDir)
for absDir, _ := range haveDirs {
if str.HasFilePathPrefix(absDir, absArg) {
if _, ok := keepDirs[absDir]; !ok {
keepDirs[absDir] = "" // Mark for deletion.
}
}
}
} }
for dir := range removeDirs { base.ExitIfErrors()
workFile.DropUse(dir)
} for absDir, keepDir := range keepDirs {
for dir := range addDirs { nKept := 0
workFile.AddUse(dir, "") for _, dir := range haveDirs[absDir] {
if dir == keepDir { // (note that dir is always non-empty)
nKept++
} else {
workFile.DropUse(dir)
}
}
if keepDir != "" && nKept != 1 {
// If we kept more than one copy, delete them all.
// We'll recreate a unique copy with AddUse.
if nKept > 1 {
workFile.DropUse(keepDir)
}
workFile.AddUse(keepDir, "")
}
} }
modload.UpdateWorkFile(workFile) modload.UpdateWorkFile(workFile)
modload.WriteWorkFile(gowork, workFile) modload.WriteWorkFile(gowork, workFile)
} }
// pathRel returns the absolute and canonical forms of dir for use in a
// go.work file located in directory workDir.
//
// If dir is relative, it is intepreted relative to base.Cwd()
// and its canonical form is relative to workDir if possible.
// If dir is absolute or cannot be made relative to workDir,
// its canonical form is absolute.
//
// Canonical absolute paths are clean.
// Canonical relative paths are clean and slash-separated.
func pathRel(workDir, dir string) (abs, canonical string) {
if filepath.IsAbs(dir) {
abs = filepath.Clean(dir)
return abs, abs
}
abs = filepath.Join(base.Cwd(), dir)
rel, err := filepath.Rel(workDir, abs)
if err != nil {
// The path can't be made relative to the go.work file,
// so it must be kept absolute instead.
return abs, abs
}
// Normalize relative paths to use slashes, so that checked-in go.work
// files with relative paths within the repo are platform-independent.
return abs, filepath.ToSlash(rel)
}

View File

@ -0,0 +1,22 @@
go work use -r .
cmp go.work go.work.want
-- go.work --
go 1.18
use (
.
sub
sub/dir/deleted
)
-- go.work.want --
go 1.18
use sub/dir
-- sub/README.txt --
A go.mod file has been deleted from this directory.
In addition, the entire subdirectory sub/dir/deleted
has been deleted, along with sub/dir/deleted/go.mod.
-- sub/dir/go.mod --
module example/sub/dir
go 1.18

View File

@ -1,6 +1,7 @@
cp go.work go.work.orig cp go.work go.work.orig
# 'go work use .' should add an entry for the current directory. # If the current directory contains a go.mod file,
# 'go work use .' should add an entry for it.
cd bar/baz cd bar/baz
go work use . go work use .
cmp ../../go.work ../../go.work.rel cmp ../../go.work ../../go.work.rel
@ -11,9 +12,28 @@ mv go.mod go.mod.bak
go work use . go work use .
cmp ../../go.work ../../go.work.orig cmp ../../go.work ../../go.work.orig
# If the path is absolute, it should remain absolute.
mv go.mod.bak go.mod mv go.mod.bak go.mod
go work use $PWD go work use $PWD
cmpenv ../../go.work ../../go.work.abs grep -count=1 '^use ' ../../go.work
grep '^use ["]?'$PWD'["]?$' ../../go.work
# An absolute path should replace an entry for the corresponding relative path
# and vice-versa.
go work use .
cmp ../../go.work ../../go.work.rel
go work use $PWD
grep -count=1 '^use ' ../../go.work
grep '^use ["]?'$PWD'["]?$' ../../go.work
# If both the absolute and relative paths are named, 'go work use' should error
# out: we don't know which one to use, and shouldn't add both because the
# resulting workspace would contain a duplicate module.
cp ../../go.work.orig ../../go.work
! go work use $PWD .
stderr '^go: already added "bar/baz" as "'$PWD'"$'
cmp ../../go.work ../../go.work.orig
-- go.mod -- -- go.mod --
module example module example
@ -24,10 +44,6 @@ go 1.18
go 1.18 go 1.18
use bar/baz use bar/baz
-- go.work.abs --
go 1.18
use $PWD
-- bar/baz/go.mod -- -- bar/baz/go.mod --
module example/bar/baz module example/bar/baz
go 1.18 go 1.18