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