github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/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  }