github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/go/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 "strings" 14 "time" 15 16 "cmd/go/internal/base" 17 "cmd/go/internal/cache" 18 "cmd/go/internal/cfg" 19 "cmd/go/internal/load" 20 "cmd/go/internal/modfetch" 21 "cmd/go/internal/modload" 22 "cmd/go/internal/work" 23 ) 24 25 var CmdClean = &base.Command{ 26 UsageLine: "go clean [clean flags] [build flags] [packages]", 27 Short: "remove object files and cached files", 28 Long: ` 29 Clean removes object files from package source directories. 30 The go command builds most objects in a temporary directory, 31 so go clean is mainly concerned with object files left by other 32 tools or by manual invocations of go build. 33 34 Specifically, clean removes the following files from each of the 35 source directories corresponding to the import paths: 36 37 _obj/ old object directory, left from Makefiles 38 _test/ old test directory, left from Makefiles 39 _testmain.go old gotest file, left from Makefiles 40 test.out old test log, left from Makefiles 41 build.out old test log, left from Makefiles 42 *.[568ao] object files, left from Makefiles 43 44 DIR(.exe) from go build 45 DIR.test(.exe) from go test -c 46 MAINFILE(.exe) from go build MAINFILE.go 47 *.so from SWIG 48 49 In the list, DIR represents the final path element of the 50 directory, and MAINFILE is the base name of any Go source 51 file in the directory that is not included when building 52 the package. 53 54 The -i flag causes clean to remove the corresponding installed 55 archive or binary (what 'go install' would create). 56 57 The -n flag causes clean to print the remove commands it would execute, 58 but not run them. 59 60 The -r flag causes clean to be applied recursively to all the 61 dependencies of the packages named by the import paths. 62 63 The -x flag causes clean to print remove commands as it executes them. 64 65 The -cache flag causes clean to remove the entire go build cache. 66 67 The -testcache flag causes clean to expire all test results in the 68 go build cache. 69 70 The -modcache flag causes clean to remove the entire module 71 download cache, including unpacked source code of versioned 72 dependencies. 73 74 For more about build flags, see 'go help build'. 75 76 For more about specifying packages, see 'go help packages'. 77 `, 78 } 79 80 var ( 81 cleanI bool // clean -i flag 82 cleanR bool // clean -r flag 83 cleanCache bool // clean -cache flag 84 cleanModcache bool // clean -modcache flag 85 cleanTestcache bool // clean -testcache flag 86 ) 87 88 func init() { 89 // break init cycle 90 CmdClean.Run = runClean 91 92 CmdClean.Flag.BoolVar(&cleanI, "i", false, "") 93 CmdClean.Flag.BoolVar(&cleanR, "r", false, "") 94 CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "") 95 CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "") 96 CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "") 97 98 // -n and -x are important enough to be 99 // mentioned explicitly in the docs but they 100 // are part of the build flags. 101 102 work.AddBuildFlags(CmdClean) 103 } 104 105 func runClean(cmd *base.Command, args []string) { 106 if len(args) == 0 && modload.Failed() { 107 // Don't try to clean current directory, 108 // which will cause modload to base.Fatalf. 109 } else { 110 for _, pkg := range load.PackagesAndErrors(args) { 111 clean(pkg) 112 } 113 } 114 115 var b work.Builder 116 b.Print = fmt.Print 117 118 if cleanCache { 119 dir := cache.DefaultDir() 120 if dir != "off" { 121 // Remove the cache subdirectories but not the top cache directory. 122 // The top cache directory may have been created with special permissions 123 // and not something that we want to remove. Also, we'd like to preserve 124 // the access log for future analysis, even if the cache is cleared. 125 subdirs, _ := filepath.Glob(filepath.Join(dir, "[0-9a-f][0-9a-f]")) 126 if len(subdirs) > 0 { 127 if cfg.BuildN || cfg.BuildX { 128 b.Showcmd("", "rm -r %s", strings.Join(subdirs, " ")) 129 } 130 printedErrors := false 131 for _, d := range subdirs { 132 // Only print the first error - there may be many. 133 // This also mimics what os.RemoveAll(dir) would do. 134 if err := os.RemoveAll(d); err != nil && !printedErrors { 135 printedErrors = true 136 base.Errorf("go clean -cache: %v", err) 137 } 138 } 139 } 140 } 141 } 142 143 if cleanTestcache && !cleanCache { 144 // Instead of walking through the entire cache looking for test results, 145 // we write a file to the cache indicating that all test results from before 146 // right now are to be ignored. 147 dir := cache.DefaultDir() 148 if dir != "off" { 149 err := ioutil.WriteFile(filepath.Join(dir, "testexpire.txt"), []byte(fmt.Sprintf("%d\n", time.Now().UnixNano())), 0666) 150 if err != nil { 151 base.Errorf("go clean -testcache: %v", err) 152 } 153 } 154 } 155 156 if cleanModcache { 157 if modfetch.PkgMod == "" { 158 base.Fatalf("go clean -modcache: no module cache") 159 } 160 if cfg.BuildN || cfg.BuildX { 161 b.Showcmd("", "rm -rf %s", modfetch.PkgMod) 162 } 163 if !cfg.BuildN { 164 if err := removeAll(modfetch.PkgMod); err != nil { 165 base.Errorf("go clean -modcache: %v", err) 166 } 167 } 168 } 169 } 170 171 func removeAll(dir string) error { 172 // Module cache has 0555 directories; make them writable in order to remove content. 173 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 174 if err != nil { 175 return nil // ignore errors walking in file system 176 } 177 if info.IsDir() { 178 os.Chmod(path, 0777) 179 } 180 return nil 181 }) 182 return os.RemoveAll(dir) 183 } 184 185 var cleaned = map[*load.Package]bool{} 186 187 // TODO: These are dregs left by Makefile-based builds. 188 // Eventually, can stop deleting these. 189 var cleanDir = map[string]bool{ 190 "_test": true, 191 "_obj": true, 192 } 193 194 var cleanFile = map[string]bool{ 195 "_testmain.go": true, 196 "test.out": true, 197 "build.out": true, 198 "a.out": true, 199 } 200 201 var cleanExt = map[string]bool{ 202 ".5": true, 203 ".6": true, 204 ".8": true, 205 ".a": true, 206 ".o": true, 207 ".so": true, 208 } 209 210 func clean(p *load.Package) { 211 if cleaned[p] { 212 return 213 } 214 cleaned[p] = true 215 216 if p.Dir == "" { 217 base.Errorf("can't load package: %v", p.Error) 218 return 219 } 220 dirs, err := ioutil.ReadDir(p.Dir) 221 if err != nil { 222 base.Errorf("go clean %s: %v", p.Dir, err) 223 return 224 } 225 226 var b work.Builder 227 b.Print = fmt.Print 228 229 packageFile := map[string]bool{} 230 if p.Name != "main" { 231 // Record which files are not in package main. 232 // The others are. 233 keep := func(list []string) { 234 for _, f := range list { 235 packageFile[f] = true 236 } 237 } 238 keep(p.GoFiles) 239 keep(p.CgoFiles) 240 keep(p.TestGoFiles) 241 keep(p.XTestGoFiles) 242 } 243 244 _, elem := filepath.Split(p.Dir) 245 var allRemove []string 246 247 // Remove dir-named executable only if this is package main. 248 if p.Name == "main" { 249 allRemove = append(allRemove, 250 elem, 251 elem+".exe", 252 ) 253 } 254 255 // Remove package test executables. 256 allRemove = append(allRemove, 257 elem+".test", 258 elem+".test.exe", 259 ) 260 261 // Remove a potential executable for each .go file in the directory that 262 // is not part of the directory's package. 263 for _, dir := range dirs { 264 name := dir.Name() 265 if packageFile[name] { 266 continue 267 } 268 if !dir.IsDir() && strings.HasSuffix(name, ".go") { 269 // TODO(adg,rsc): check that this .go file is actually 270 // in "package main", and therefore capable of building 271 // to an executable file. 272 base := name[:len(name)-len(".go")] 273 allRemove = append(allRemove, base, base+".exe") 274 } 275 } 276 277 if cfg.BuildN || cfg.BuildX { 278 b.Showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " ")) 279 } 280 281 toRemove := map[string]bool{} 282 for _, name := range allRemove { 283 toRemove[name] = true 284 } 285 for _, dir := range dirs { 286 name := dir.Name() 287 if dir.IsDir() { 288 // TODO: Remove once Makefiles are forgotten. 289 if cleanDir[name] { 290 if cfg.BuildN || cfg.BuildX { 291 b.Showcmd(p.Dir, "rm -r %s", name) 292 if cfg.BuildN { 293 continue 294 } 295 } 296 if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { 297 base.Errorf("go clean: %v", err) 298 } 299 } 300 continue 301 } 302 303 if cfg.BuildN { 304 continue 305 } 306 307 if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] { 308 removeFile(filepath.Join(p.Dir, name)) 309 } 310 } 311 312 if cleanI && p.Target != "" { 313 if cfg.BuildN || cfg.BuildX { 314 b.Showcmd("", "rm -f %s", p.Target) 315 } 316 if !cfg.BuildN { 317 removeFile(p.Target) 318 } 319 } 320 321 if cleanR { 322 for _, p1 := range p.Internal.Imports { 323 clean(p1) 324 } 325 } 326 } 327 328 // removeFile tries to remove file f, if error other than file doesn't exist 329 // occurs, it will report the error. 330 func removeFile(f string) { 331 err := os.Remove(f) 332 if err == nil || os.IsNotExist(err) { 333 return 334 } 335 // Windows does not allow deletion of a binary file while it is executing. 336 if base.ToolIsWindows { 337 // Remove lingering ~ file from last attempt. 338 if _, err2 := os.Stat(f + "~"); err2 == nil { 339 os.Remove(f + "~") 340 } 341 // Try to move it out of the way. If the move fails, 342 // which is likely, we'll try again the 343 // next time we do an install of this binary. 344 if err2 := os.Rename(f, f+"~"); err2 == nil { 345 os.Remove(f + "~") 346 return 347 } 348 } 349 base.Errorf("go clean: %v", err) 350 }