Skip to content

Commit 9e0de1f

Browse files
author
Bryan C. Mills
committed
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 <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Reviewed-by: Michael Matloob <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent ef06a5f commit 9e0de1f

File tree

4 files changed

+143
-47
lines changed

4 files changed

+143
-47
lines changed

src/cmd/go/internal/modload/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ func InitWorkfile() {
301301
}
302302
}
303303

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

src/cmd/go/internal/workcmd/use.go

Lines changed: 98 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import (
1010
"cmd/go/internal/base"
1111
"cmd/go/internal/fsys"
1212
"cmd/go/internal/modload"
13+
"cmd/go/internal/str"
1314
"context"
15+
"errors"
16+
"fmt"
1417
"io/fs"
1518
"os"
1619
"path/filepath"
@@ -56,44 +59,34 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
5659
if err != nil {
5760
base.Fatalf("go: %v", err)
5861
}
62+
workDir := filepath.Dir(gowork) // Absolute, since gowork itself is absolute.
5963

6064
haveDirs := make(map[string][]string) // absolute → original(s)
6165
for _, use := range workFile.Use {
62-
var absDir string
66+
var abs string
6367
if filepath.IsAbs(use.Path) {
64-
absDir = filepath.Clean(use.Path)
68+
abs = filepath.Clean(use.Path)
6569
} else {
66-
absDir = filepath.Join(filepath.Dir(gowork), use.Path)
70+
abs = filepath.Join(workDir, use.Path)
6771
}
68-
haveDirs[absDir] = append(haveDirs[absDir], use.Path)
72+
haveDirs[abs] = append(haveDirs[abs], use.Path)
6973
}
7074

71-
addDirs := make(map[string]bool)
72-
removeDirs := make(map[string]bool)
75+
// keepDirs maps each absolute path to keep to the literal string to use for
76+
// that path (either an absolute or a relative path), or the empty string if
77+
// all entries for the absolute path should be removed.
78+
keepDirs := make(map[string]string)
79+
80+
// lookDir updates the entry in keepDirs for the directory dir,
81+
// which is either absolute or relative to the current working directory
82+
// (not necessarily the directory containing the workfile).
7383
lookDir := func(dir string) {
74-
// If the path is absolute, try to keep it absolute. If it's relative,
75-
// make it relative to the go.work file rather than the working directory.
76-
absDir := dir
77-
if !filepath.IsAbs(dir) {
78-
absDir = filepath.Join(base.Cwd(), dir)
79-
rel, err := filepath.Rel(filepath.Dir(gowork), absDir)
80-
if err == nil {
81-
// Normalize relative paths to use slashes, so that checked-in go.work
82-
// files with relative paths within the repo are platform-independent.
83-
dir = filepath.ToSlash(rel)
84-
} else {
85-
// The path can't be made relative to the go.work file,
86-
// so it must be kept absolute instead.
87-
dir = absDir
88-
}
89-
}
84+
absDir, dir := pathRel(workDir, dir)
9085

9186
fi, err := os.Stat(filepath.Join(absDir, "go.mod"))
9287
if err != nil {
9388
if os.IsNotExist(err) {
94-
for _, origDir := range haveDirs[absDir] {
95-
removeDirs[origDir] = true
96-
}
89+
keepDirs[absDir] = ""
9790
return
9891
}
9992
base.Errorf("go: %v", err)
@@ -103,31 +96,96 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
10396
base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod"))
10497
}
10598

106-
if len(haveDirs[absDir]) == 0 {
107-
addDirs[dir] = true
99+
if dup := keepDirs[absDir]; dup != "" && dup != dir {
100+
base.Errorf(`go: already added "%s" as "%s"`, dir, dup)
108101
}
102+
keepDirs[absDir] = dir
109103
}
110104

111105
for _, useDir := range args {
112-
if *useR {
113-
fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
114-
if !info.IsDir() {
115-
return nil
106+
if !*useR {
107+
lookDir(useDir)
108+
continue
109+
}
110+
111+
// Add or remove entries for any subdirectories that still exist.
112+
err := fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
113+
if !info.IsDir() {
114+
if info.Mode()&fs.ModeSymlink != 0 {
115+
if target, err := fsys.Stat(path); err == nil && target.IsDir() {
116+
fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
117+
}
116118
}
117-
lookDir(path)
118119
return nil
119-
})
120-
continue
120+
}
121+
lookDir(path)
122+
return nil
123+
})
124+
if err != nil && !errors.Is(err, os.ErrNotExist) {
125+
base.Errorf("go: %v", err)
121126
}
122-
lookDir(useDir)
123-
}
124127

125-
for dir := range removeDirs {
126-
workFile.DropUse(dir)
128+
// Remove entries for subdirectories that no longer exist.
129+
// Because they don't exist, they will be skipped by Walk.
130+
absArg, _ := pathRel(workDir, useDir)
131+
for absDir, _ := range haveDirs {
132+
if str.HasFilePathPrefix(absDir, absArg) {
133+
if _, ok := keepDirs[absDir]; !ok {
134+
keepDirs[absDir] = "" // Mark for deletion.
135+
}
136+
}
137+
}
127138
}
128-
for dir := range addDirs {
129-
workFile.AddUse(dir, "")
139+
140+
base.ExitIfErrors()
141+
142+
for absDir, keepDir := range keepDirs {
143+
nKept := 0
144+
for _, dir := range haveDirs[absDir] {
145+
if dir == keepDir { // (note that dir is always non-empty)
146+
nKept++
147+
} else {
148+
workFile.DropUse(dir)
149+
}
150+
}
151+
if keepDir != "" && nKept != 1 {
152+
// If we kept more than one copy, delete them all.
153+
// We'll recreate a unique copy with AddUse.
154+
if nKept > 1 {
155+
workFile.DropUse(keepDir)
156+
}
157+
workFile.AddUse(keepDir, "")
158+
}
130159
}
131160
modload.UpdateWorkFile(workFile)
132161
modload.WriteWorkFile(gowork, workFile)
133162
}
163+
164+
// pathRel returns the absolute and canonical forms of dir for use in a
165+
// go.work file located in directory workDir.
166+
//
167+
// If dir is relative, it is intepreted relative to base.Cwd()
168+
// and its canonical form is relative to workDir if possible.
169+
// If dir is absolute or cannot be made relative to workDir,
170+
// its canonical form is absolute.
171+
//
172+
// Canonical absolute paths are clean.
173+
// Canonical relative paths are clean and slash-separated.
174+
func pathRel(workDir, dir string) (abs, canonical string) {
175+
if filepath.IsAbs(dir) {
176+
abs = filepath.Clean(dir)
177+
return abs, abs
178+
}
179+
180+
abs = filepath.Join(base.Cwd(), dir)
181+
rel, err := filepath.Rel(workDir, abs)
182+
if err != nil {
183+
// The path can't be made relative to the go.work file,
184+
// so it must be kept absolute instead.
185+
return abs, abs
186+
}
187+
188+
// Normalize relative paths to use slashes, so that checked-in go.work
189+
// files with relative paths within the repo are platform-independent.
190+
return abs, filepath.ToSlash(rel)
191+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
go work use -r .
2+
cmp go.work go.work.want
3+
4+
-- go.work --
5+
go 1.18
6+
7+
use (
8+
.
9+
sub
10+
sub/dir/deleted
11+
)
12+
-- go.work.want --
13+
go 1.18
14+
15+
use sub/dir
16+
-- sub/README.txt --
17+
A go.mod file has been deleted from this directory.
18+
In addition, the entire subdirectory sub/dir/deleted
19+
has been deleted, along with sub/dir/deleted/go.mod.
20+
-- sub/dir/go.mod --
21+
module example/sub/dir
22+
go 1.18
Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cp go.work go.work.orig
22

3-
# 'go work use .' should add an entry for the current directory.
3+
# If the current directory contains a go.mod file,
4+
# 'go work use .' should add an entry for it.
45
cd bar/baz
56
go work use .
67
cmp ../../go.work ../../go.work.rel
@@ -11,9 +12,28 @@ mv go.mod go.mod.bak
1112
go work use .
1213
cmp ../../go.work ../../go.work.orig
1314

15+
# If the path is absolute, it should remain absolute.
1416
mv go.mod.bak go.mod
1517
go work use $PWD
16-
cmpenv ../../go.work ../../go.work.abs
18+
grep -count=1 '^use ' ../../go.work
19+
grep '^use ["]?'$PWD'["]?$' ../../go.work
20+
21+
# An absolute path should replace an entry for the corresponding relative path
22+
# and vice-versa.
23+
go work use .
24+
cmp ../../go.work ../../go.work.rel
25+
go work use $PWD
26+
grep -count=1 '^use ' ../../go.work
27+
grep '^use ["]?'$PWD'["]?$' ../../go.work
28+
29+
# If both the absolute and relative paths are named, 'go work use' should error
30+
# out: we don't know which one to use, and shouldn't add both because the
31+
# resulting workspace would contain a duplicate module.
32+
cp ../../go.work.orig ../../go.work
33+
! go work use $PWD .
34+
stderr '^go: already added "bar/baz" as "'$PWD'"$'
35+
cmp ../../go.work ../../go.work.orig
36+
1737

1838
-- go.mod --
1939
module example
@@ -24,10 +44,6 @@ go 1.18
2444
go 1.18
2545

2646
use bar/baz
27-
-- go.work.abs --
28-
go 1.18
29-
30-
use $PWD
3147
-- bar/baz/go.mod --
3248
module example/bar/baz
3349
go 1.18

0 commit comments

Comments
 (0)