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