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