]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/cmd/go/internal/clean/clean.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / cmd / go / internal / clean / clean.go
1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package clean implements the ``go clean'' command.
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 // clean -i flag
85 cleanR bool // clean -r flag
86 cleanCache bool // clean -cache flag
87 cleanModcache bool // clean -modcache flag
88 cleanTestcache bool // clean -testcache flag
89 )
90
91 func init() {
92 // break init cycle
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 // -n and -x are important enough to be
102 // mentioned explicitly in the docs but they
103 // are part of the build flags.
104
105 work.AddBuildFlags(CmdClean, work.DefaultBuildFlags)
106 }
107
108 func runClean(cmd *base.Command, args []string) {
109 // golang.org/issue/29925: only load packages before cleaning if
110 // either the flags and arguments explicitly imply a package,
111 // or no other target (such as a cache) was requested to be cleaned.
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 // Remove the cache subdirectories but not the top cache directory.
131 // The top cache directory may have been created with special permissions
132 // and not something that we want to remove. Also, we'd like to preserve
133 // the access log for future analysis, even if the cache is cleared.
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 // Only print the first error - there may be many.
142 // This also mimics what os.RemoveAll(dir) would do.
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 // Instead of walking through the entire cache looking for test results,
160 // we write a file to the cache indicating that all test results from before
161 // right now are to be ignored.
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 // TODO: These are dregs left by Makefile-based builds.
204 // Eventually, can stop deleting these.
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 // Record which files are not in package main.
248 // The others are.
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 // Remove dir-named executable only if this is package main.
264 if p.Name == "main" {
265 allRemove = append(allRemove,
266 elem,
267 elem+".exe",
268 )
269 }
270
271 // Remove package test executables.
272 allRemove = append(allRemove,
273 elem+".test",
274 elem+".test.exe",
275 )
276
277 // Remove a potential executable for each .go file in the directory that
278 // is not part of the directory's package.
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 // TODO(adg,rsc): check that this .go file is actually
286 // in "package main", and therefore capable of building
287 // to an executable file.
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 // TODO: Remove once Makefiles are forgotten.
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 // removeFile tries to remove file f, if error other than file doesn't exist
345 // occurs, it will report the error.
346 func removeFile(f string) {
347 err := os.Remove(f)
348 if err == nil || os.IsNotExist(err) {
349 return
350 }
351 // Windows does not allow deletion of a binary file while it is executing.
352 if base.ToolIsWindows {
353 // Remove lingering ~ file from last attempt.
354 if _, err2 := os.Stat(f + "~"); err2 == nil {
355 os.Remove(f + "~")
356 }
357 // Try to move it out of the way. If the move fails,
358 // which is likely, we'll try again the
359 // next time we do an install of this binary.
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 }