github.com/gernest/nezuko@v0.1.2/internal/modcmd/tidy.go (about)

     1  // Copyright 2018 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  // z mod tidy
     6  
     7  package modcmd
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  
    13  	"github.com/gernest/nezuko/internal/base"
    14  	"github.com/gernest/nezuko/internal/cfg"
    15  	"github.com/gernest/nezuko/internal/modfetch"
    16  	"github.com/gernest/nezuko/internal/modload"
    17  	"github.com/gernest/nezuko/internal/module"
    18  )
    19  
    20  var cmdTidy = &base.Command{
    21  	UsageLine: "z mod tidy [-v]",
    22  	Short:     "add missing and remove unused modules",
    23  	Long: `
    24  Tidy makes sure z.mod matches the source code in the module.
    25  It adds any missing modules necessary to build the current module's
    26  packages and dependencies, and it removes unused modules that
    27  don't provide any relevant packages. It also adds any missing entries
    28  to go.sum and removes any unnecessary ones.
    29  
    30  The -v flag causes tidy to print information about removed modules
    31  to standard error.
    32  	`,
    33  }
    34  
    35  func init() {
    36  	cmdTidy.Run = runTidy // break init cycle
    37  	cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
    38  }
    39  
    40  func runTidy(cmd *base.Command, args []string) {
    41  	if len(args) > 0 {
    42  		base.Fatalf("z mod tidy: no arguments allowed")
    43  	}
    44  
    45  	// LoadALL adds missing modules.
    46  	// Remove unused modules.
    47  	used := make(map[module.Version]bool)
    48  	for _, pkg := range modload.LoadALL() {
    49  		used[modload.PackageModule(pkg)] = true
    50  	}
    51  	used[modload.Target] = true // note: LoadALL initializes Target
    52  
    53  	inGoMod := make(map[string]bool)
    54  	for _, r := range modload.ModFile().Require {
    55  		inGoMod[r.Mod.Path] = true
    56  	}
    57  
    58  	var keep []module.Version
    59  	for _, m := range modload.BuildList() {
    60  		if used[m] {
    61  			keep = append(keep, m)
    62  		} else if cfg.BuildV && inGoMod[m.Path] {
    63  			fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
    64  		}
    65  	}
    66  	modload.SetBuildList(keep)
    67  	modTidyGoSum() // updates memory copy; WriteGoMod on next line flushes it out
    68  	modload.WriteGoMod()
    69  }
    70  
    71  // modTidyGoSum resets the go.sum file content
    72  // to be exactly what's needed for the current z.mod.
    73  func modTidyGoSum() {
    74  	// Assuming go.sum already has at least enough from the successful load,
    75  	// we only have to tell modfetch what needs keeping.
    76  	reqs := modload.Reqs()
    77  	keep := make(map[module.Version]bool)
    78  	replaced := make(map[module.Version]bool)
    79  	var walk func(module.Version)
    80  	walk = func(m module.Version) {
    81  		// If we build using a replacement module, keep the sum for the replacement,
    82  		// since that's the code we'll actually use during a build.
    83  		//
    84  		// TODO(golang.org/issue/29182): Perhaps we should keep both sums, and the
    85  		// sums for both sets of transitive requirements.
    86  		r := modload.Replacement(m)
    87  		if r.Path == "" {
    88  			keep[m] = true
    89  		} else {
    90  			keep[r] = true
    91  			replaced[m] = true
    92  		}
    93  		list, _ := reqs.Required(m)
    94  		for _, r := range list {
    95  			if !keep[r] && !replaced[r] {
    96  				walk(r)
    97  			}
    98  		}
    99  	}
   100  	walk(modload.Target)
   101  	modfetch.TrimGoSum(keep)
   102  }