1
2
3
4
5
6
7 package workcmd
8
9 import (
10 "cmd/go/internal/base"
11 "cmd/go/internal/fsys"
12 "cmd/go/internal/modload"
13 "cmd/go/internal/str"
14 "context"
15 "fmt"
16 "io/fs"
17 "os"
18 "path/filepath"
19 )
20
21 var cmdUse = &base.Command{
22 UsageLine: "go work use [-r] moddirs",
23 Short: "add modules to workspace file",
24 Long: `Use provides a command-line interface for adding
25 directories, optionally recursively, to a go.work file.
26
27 A use directive will be added to the go.work file for each argument
28 directory listed on the command line go.work file, if it exists on disk,
29 or removed from the go.work file if it does not exist on disk.
30
31 The -r flag searches recursively for modules in the argument
32 directories, and the use command operates as if each of the directories
33 were specified as arguments: namely, use directives will be added for
34 directories that exist, and removed for directories that do not exist.
35
36 See the workspaces reference at https://go.dev/ref/mod#workspaces
37 for more information.
38 `,
39 }
40
41 var useR = cmdUse.Flag.Bool("r", false, "")
42
43 func init() {
44 cmdUse.Run = runUse
45
46 base.AddModCommonFlags(&cmdUse.Flag)
47 }
48
49 func runUse(ctx context.Context, cmd *base.Command, args []string) {
50 modload.ForceUseModules = true
51
52 var gowork string
53 modload.InitWorkfile()
54 gowork = modload.WorkFilePath()
55
56 if gowork == "" {
57 base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)")
58 }
59 workFile, err := modload.ReadWorkFile(gowork)
60 if err != nil {
61 base.Fatalf("go: %v", err)
62 }
63 workDir := filepath.Dir(gowork)
64
65 haveDirs := make(map[string][]string)
66 for _, use := range workFile.Use {
67 var abs string
68 if filepath.IsAbs(use.Path) {
69 abs = filepath.Clean(use.Path)
70 } else {
71 abs = filepath.Join(workDir, use.Path)
72 }
73 haveDirs[abs] = append(haveDirs[abs], use.Path)
74 }
75
76
77
78
79 keepDirs := make(map[string]string)
80
81
82
83
84 lookDir := func(dir string) {
85 absDir, dir := pathRel(workDir, dir)
86
87 fi, err := fsys.Stat(filepath.Join(absDir, "go.mod"))
88 if err != nil {
89 if os.IsNotExist(err) {
90 keepDirs[absDir] = ""
91 } else {
92 base.Errorf("go: %v", err)
93 }
94 return
95 }
96
97 if !fi.Mode().IsRegular() {
98 base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod"))
99 }
100
101 if dup := keepDirs[absDir]; dup != "" && dup != dir {
102 base.Errorf(`go: already added "%s" as "%s"`, dir, dup)
103 }
104 keepDirs[absDir] = dir
105 }
106
107 if len(args) == 0 {
108 base.Fatalf("go: 'go work use' requires one or more directory arguments")
109 }
110 for _, useDir := range args {
111 absArg, _ := pathRel(workDir, useDir)
112
113 info, err := fsys.Stat(absArg)
114 if err != nil {
115
116 if os.IsNotExist(err) {
117 base.Errorf("go: directory %v does not exist", absArg)
118 } else {
119 base.Errorf("go: %v", err)
120 }
121 continue
122 } else if !info.IsDir() {
123 base.Errorf("go: %s is not a directory", absArg)
124 continue
125 }
126
127 if !*useR {
128 lookDir(useDir)
129 continue
130 }
131
132
133 fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
134 if err != nil {
135 return err
136 }
137
138 if !info.IsDir() {
139 if info.Mode()&fs.ModeSymlink != 0 {
140 if target, err := fsys.Stat(path); err == nil && target.IsDir() {
141 fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
142 }
143 }
144 return nil
145 }
146 lookDir(path)
147 return nil
148 })
149
150
151
152 for absDir, _ := range haveDirs {
153 if str.HasFilePathPrefix(absDir, absArg) {
154 if _, ok := keepDirs[absDir]; !ok {
155 keepDirs[absDir] = ""
156 }
157 }
158 }
159 }
160
161 base.ExitIfErrors()
162
163 for absDir, keepDir := range keepDirs {
164 nKept := 0
165 for _, dir := range haveDirs[absDir] {
166 if dir == keepDir {
167 nKept++
168 } else {
169 workFile.DropUse(dir)
170 }
171 }
172 if keepDir != "" && nKept != 1 {
173
174
175 if nKept > 1 {
176 workFile.DropUse(keepDir)
177 }
178 workFile.AddUse(keepDir, "")
179 }
180 }
181 modload.UpdateWorkFile(workFile)
182 modload.WriteWorkFile(gowork, workFile)
183 }
184
185
186
187
188
189
190
191
192
193
194
195 func pathRel(workDir, dir string) (abs, canonical string) {
196 if filepath.IsAbs(dir) {
197 abs = filepath.Clean(dir)
198 return abs, abs
199 }
200
201 abs = filepath.Join(base.Cwd(), dir)
202 rel, err := filepath.Rel(workDir, abs)
203 if err != nil {
204
205
206 return abs, abs
207 }
208
209
210
211 return abs, modload.ToDirectoryPath(rel)
212 }
213
View as plain text