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  }