github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/clean/clean.go (about) 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 "github.com/gagliardetto/golang-go/cmd/go/not-internal/base" 18 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cache" 19 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg" 20 "github.com/gagliardetto/golang-go/cmd/go/not-internal/load" 21 "github.com/gagliardetto/golang-go/cmd/go/not-internal/lockedfile" 22 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modfetch" 23 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modload" 24 "github.com/gagliardetto/golang-go/cmd/go/not-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 if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) { 182 base.Errorf("go clean -testcache: %v", err) 183 } 184 } 185 } 186 } 187 188 if cleanModcache { 189 if modfetch.PkgMod == "" { 190 base.Fatalf("go clean -modcache: no module cache") 191 } 192 if cfg.BuildN || cfg.BuildX { 193 b.Showcmd("", "rm -rf %s", modfetch.PkgMod) 194 } 195 if !cfg.BuildN { 196 if err := modfetch.RemoveAll(modfetch.PkgMod); err != nil { 197 base.Errorf("go clean -modcache: %v", err) 198 } 199 } 200 } 201 } 202 203 var cleaned = map[*load.Package]bool{} 204 205 // TODO: These are dregs left by Makefile-based builds. 206 // Eventually, can stop deleting these. 207 var cleanDir = map[string]bool{ 208 "_test": true, 209 "_obj": true, 210 } 211 212 var cleanFile = map[string]bool{ 213 "_testmain.go": true, 214 "test.out": true, 215 "build.out": true, 216 "a.out": true, 217 } 218 219 var cleanExt = map[string]bool{ 220 ".5": true, 221 ".6": true, 222 ".8": true, 223 ".a": true, 224 ".o": true, 225 ".so": true, 226 } 227 228 func clean(p *load.Package) { 229 if cleaned[p] { 230 return 231 } 232 cleaned[p] = true 233 234 if p.Dir == "" { 235 base.Errorf("can't load package: %v", p.Error) 236 return 237 } 238 dirs, err := ioutil.ReadDir(p.Dir) 239 if err != nil { 240 base.Errorf("go clean %s: %v", p.Dir, err) 241 return 242 } 243 244 var b work.Builder 245 b.Print = fmt.Print 246 247 packageFile := map[string]bool{} 248 if p.Name != "main" { 249 // Record which files are not in package main. 250 // The others are. 251 keep := func(list []string) { 252 for _, f := range list { 253 packageFile[f] = true 254 } 255 } 256 keep(p.GoFiles) 257 keep(p.CgoFiles) 258 keep(p.TestGoFiles) 259 keep(p.XTestGoFiles) 260 } 261 262 _, elem := filepath.Split(p.Dir) 263 var allRemove []string 264 265 // Remove dir-named executable only if this is package main. 266 if p.Name == "main" { 267 allRemove = append(allRemove, 268 elem, 269 elem+".exe", 270 ) 271 } 272 273 // Remove package test executables. 274 allRemove = append(allRemove, 275 elem+".test", 276 elem+".test.exe", 277 ) 278 279 // Remove a potential executable for each .go file in the directory that 280 // is not part of the directory's package. 281 for _, dir := range dirs { 282 name := dir.Name() 283 if packageFile[name] { 284 continue 285 } 286 if !dir.IsDir() && strings.HasSuffix(name, ".go") { 287 // TODO(adg,rsc): check that this .go file is actually 288 // in "package main", and therefore capable of building 289 // to an executable file. 290 base := name[:len(name)-len(".go")] 291 allRemove = append(allRemove, base, base+".exe") 292 } 293 } 294 295 if cfg.BuildN || cfg.BuildX { 296 b.Showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " ")) 297 } 298 299 toRemove := map[string]bool{} 300 for _, name := range allRemove { 301 toRemove[name] = true 302 } 303 for _, dir := range dirs { 304 name := dir.Name() 305 if dir.IsDir() { 306 // TODO: Remove once Makefiles are forgotten. 307 if cleanDir[name] { 308 if cfg.BuildN || cfg.BuildX { 309 b.Showcmd(p.Dir, "rm -r %s", name) 310 if cfg.BuildN { 311 continue 312 } 313 } 314 if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { 315 base.Errorf("go clean: %v", err) 316 } 317 } 318 continue 319 } 320 321 if cfg.BuildN { 322 continue 323 } 324 325 if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] { 326 removeFile(filepath.Join(p.Dir, name)) 327 } 328 } 329 330 if cleanI && p.Target != "" { 331 if cfg.BuildN || cfg.BuildX { 332 b.Showcmd("", "rm -f %s", p.Target) 333 } 334 if !cfg.BuildN { 335 removeFile(p.Target) 336 } 337 } 338 339 if cleanR { 340 for _, p1 := range p.Internal.Imports { 341 clean(p1) 342 } 343 } 344 } 345 346 // removeFile tries to remove file f, if error other than file doesn't exist 347 // occurs, it will report the error. 348 func removeFile(f string) { 349 err := os.Remove(f) 350 if err == nil || os.IsNotExist(err) { 351 return 352 } 353 // Windows does not allow deletion of a binary file while it is executing. 354 if base.ToolIsWindows { 355 // Remove lingering ~ file from last attempt. 356 if _, err2 := os.Stat(f + "~"); err2 == nil { 357 os.Remove(f + "~") 358 } 359 // Try to move it out of the way. If the move fails, 360 // which is likely, we'll try again the 361 // next time we do an install of this binary. 362 if err2 := os.Rename(f, f+"~"); err2 == nil { 363 os.Remove(f + "~") 364 return 365 } 366 } 367 base.Errorf("go clean: %v", err) 368 }