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