Source file src/cmd/go/internal/clean/clean.go
1
2
3
4
5
6 package clean
7
8 import (
9 "fmt"
10 "io/ioutil"
11 "os"
12 "path/filepath"
13 "strconv"
14 "strings"
15 "time"
16
17 "cmd/go/internal/base"
18 "cmd/go/internal/cache"
19 "cmd/go/internal/cfg"
20 "cmd/go/internal/load"
21 "cmd/go/internal/lockedfile"
22 "cmd/go/internal/modfetch"
23 "cmd/go/internal/modload"
24 "cmd/go/internal/work"
25 )
26
27 var CmdClean = &base.Command{
28 UsageLine: "go clean [clean flags] [build flags] [packages]",
29 Short: "remove object files and cached files",
30 Long: `
31 Clean removes object files from package source directories.
32 The go command builds most objects in a temporary directory,
33 so go clean is mainly concerned with object files left by other
34 tools or by manual invocations of go build.
35
36 If a package argument is given or the -i or -r flag is set,
37 clean removes the following files from each of the
38 source directories corresponding to the import paths:
39
40 _obj/ old object directory, left from Makefiles
41 _test/ old test directory, left from Makefiles
42 _testmain.go old gotest file, left from Makefiles
43 test.out old test log, left from Makefiles
44 build.out old test log, left from Makefiles
45 *.[568ao] object files, left from Makefiles
46
47 DIR(.exe) from go build
48 DIR.test(.exe) from go test -c
49 MAINFILE(.exe) from go build MAINFILE.go
50 *.so from SWIG
51
52 In the list, DIR represents the final path element of the
53 directory, and MAINFILE is the base name of any Go source
54 file in the directory that is not included when building
55 the package.
56
57 The -i flag causes clean to remove the corresponding installed
58 archive or binary (what 'go install' would create).
59
60 The -n flag causes clean to print the remove commands it would execute,
61 but not run them.
62
63 The -r flag causes clean to be applied recursively to all the
64 dependencies of the packages named by the import paths.
65
66 The -x flag causes clean to print remove commands as it executes them.
67
68 The -cache flag causes clean to remove the entire go build cache.
69
70 The -testcache flag causes clean to expire all test results in the
71 go build cache.
72
73 The -modcache flag causes clean to remove the entire module
74 download cache, including unpacked source code of versioned
75 dependencies.
76
77 For more about build flags, see 'go help build'.
78
79 For more about specifying packages, see 'go help packages'.
80 `,
81 }
82
83 var (
84 cleanI bool
85 cleanR bool
86 cleanCache bool
87 cleanModcache bool
88 cleanTestcache bool
89 )
90
91 func init() {
92
93 CmdClean.Run = runClean
94
95 CmdClean.Flag.BoolVar(&cleanI, "i", false, "")
96 CmdClean.Flag.BoolVar(&cleanR, "r", false, "")
97 CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "")
98 CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "")
99 CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "")
100
101
102
103
104
105 work.AddBuildFlags(CmdClean)
106 }
107
108 func runClean(cmd *base.Command, args []string) {
109
110
111
112 cleanPkg := len(args) > 0 || cleanI || cleanR
113 if (!modload.Enabled() || modload.HasModRoot()) &&
114 !cleanCache && !cleanModcache && !cleanTestcache {
115 cleanPkg = true
116 }
117
118 if cleanPkg {
119 for _, pkg := range load.PackagesAndErrors(args) {
120 clean(pkg)
121 }
122 }
123
124 var b work.Builder
125 b.Print = fmt.Print
126
127 if cleanCache {
128 dir := cache.DefaultDir()
129 if dir != "off" {
130
131
132
133
134 subdirs, _ := filepath.Glob(filepath.Join(dir, "[0-9a-f][0-9a-f]"))
135 printedErrors := false
136 if len(subdirs) > 0 {
137 if cfg.BuildN || cfg.BuildX {
138 b.Showcmd("", "rm -r %s", strings.Join(subdirs, " "))
139 }
140 for _, d := range subdirs {
141
142
143 if err := os.RemoveAll(d); err != nil && !printedErrors {
144 printedErrors = true
145 base.Errorf("go clean -cache: %v", err)
146 }
147 }
148 }
149
150 logFile := filepath.Join(dir, "log.txt")
151 if err := os.RemoveAll(logFile); err != nil && !printedErrors {
152 printedErrors = true
153 base.Errorf("go clean -cache: %v", err)
154 }
155 }
156 }
157
158 if cleanTestcache && !cleanCache {
159
160
161
162 dir := cache.DefaultDir()
163 if dir != "off" {
164 f, err := lockedfile.Edit(filepath.Join(dir, "testexpire.txt"))
165 if err == nil {
166 now := time.Now().UnixNano()
167 buf, _ := ioutil.ReadAll(f)
168 prev, _ := strconv.ParseInt(strings.TrimSpace(string(buf)), 10, 64)
169 if now > prev {
170 if err = f.Truncate(0); err == nil {
171 if _, err = f.Seek(0, 0); err == nil {
172 _, err = fmt.Fprintf(f, "%d\n", now)
173 }
174 }
175 }
176 if closeErr := f.Close(); err == nil {
177 err = closeErr
178 }
179 }
180 if err != nil {
181 base.Errorf("go clean -testcache: %v", err)
182 }
183 }
184 }
185
186 if cleanModcache {
187 if modfetch.PkgMod == "" {
188 base.Fatalf("go clean -modcache: no module cache")
189 }
190 if cfg.BuildN || cfg.BuildX {
191 b.Showcmd("", "rm -rf %s", modfetch.PkgMod)
192 }
193 if !cfg.BuildN {
194 if err := modfetch.RemoveAll(modfetch.PkgMod); err != nil {
195 base.Errorf("go clean -modcache: %v", err)
196 }
197 }
198 }
199 }
200
201 var cleaned = map[*load.Package]bool{}
202
203
204
205 var cleanDir = map[string]bool{
206 "_test": true,
207 "_obj": true,
208 }
209
210 var cleanFile = map[string]bool{
211 "_testmain.go": true,
212 "test.out": true,
213 "build.out": true,
214 "a.out": true,
215 }
216
217 var cleanExt = map[string]bool{
218 ".5": true,
219 ".6": true,
220 ".8": true,
221 ".a": true,
222 ".o": true,
223 ".so": true,
224 }
225
226 func clean(p *load.Package) {
227 if cleaned[p] {
228 return
229 }
230 cleaned[p] = true
231
232 if p.Dir == "" {
233 base.Errorf("can't load package: %v", p.Error)
234 return
235 }
236 dirs, err := ioutil.ReadDir(p.Dir)
237 if err != nil {
238 base.Errorf("go clean %s: %v", p.Dir, err)
239 return
240 }
241
242 var b work.Builder
243 b.Print = fmt.Print
244
245 packageFile := map[string]bool{}
246 if p.Name != "main" {
247
248
249 keep := func(list []string) {
250 for _, f := range list {
251 packageFile[f] = true
252 }
253 }
254 keep(p.GoFiles)
255 keep(p.CgoFiles)
256 keep(p.TestGoFiles)
257 keep(p.XTestGoFiles)
258 }
259
260 _, elem := filepath.Split(p.Dir)
261 var allRemove []string
262
263
264 if p.Name == "main" {
265 allRemove = append(allRemove,
266 elem,
267 elem+".exe",
268 )
269 }
270
271
272 allRemove = append(allRemove,
273 elem+".test",
274 elem+".test.exe",
275 )
276
277
278
279 for _, dir := range dirs {
280 name := dir.Name()
281 if packageFile[name] {
282 continue
283 }
284 if !dir.IsDir() && strings.HasSuffix(name, ".go") {
285
286
287
288 base := name[:len(name)-len(".go")]
289 allRemove = append(allRemove, base, base+".exe")
290 }
291 }
292
293 if cfg.BuildN || cfg.BuildX {
294 b.Showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
295 }
296
297 toRemove := map[string]bool{}
298 for _, name := range allRemove {
299 toRemove[name] = true
300 }
301 for _, dir := range dirs {
302 name := dir.Name()
303 if dir.IsDir() {
304
305 if cleanDir[name] {
306 if cfg.BuildN || cfg.BuildX {
307 b.Showcmd(p.Dir, "rm -r %s", name)
308 if cfg.BuildN {
309 continue
310 }
311 }
312 if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil {
313 base.Errorf("go clean: %v", err)
314 }
315 }
316 continue
317 }
318
319 if cfg.BuildN {
320 continue
321 }
322
323 if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] {
324 removeFile(filepath.Join(p.Dir, name))
325 }
326 }
327
328 if cleanI && p.Target != "" {
329 if cfg.BuildN || cfg.BuildX {
330 b.Showcmd("", "rm -f %s", p.Target)
331 }
332 if !cfg.BuildN {
333 removeFile(p.Target)
334 }
335 }
336
337 if cleanR {
338 for _, p1 := range p.Internal.Imports {
339 clean(p1)
340 }
341 }
342 }
343
344
345
346 func removeFile(f string) {
347 err := os.Remove(f)
348 if err == nil || os.IsNotExist(err) {
349 return
350 }
351
352 if base.ToolIsWindows {
353
354 if _, err2 := os.Stat(f + "~"); err2 == nil {
355 os.Remove(f + "~")
356 }
357
358
359
360 if err2 := os.Rename(f, f+"~"); err2 == nil {
361 os.Remove(f + "~")
362 return
363 }
364 }
365 base.Errorf("go clean: %v", err)
366 }
367
View as plain text