Source file src/cmd/go/internal/workcmd/sync.go

     1  // Copyright 2021 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  // go work sync
     6  
     7  package workcmd
     8  
     9  import (
    10  	"cmd/go/internal/base"
    11  	"cmd/go/internal/gover"
    12  	"cmd/go/internal/imports"
    13  	"cmd/go/internal/modload"
    14  	"cmd/go/internal/toolchain"
    15  	"context"
    16  
    17  	"golang.org/x/mod/module"
    18  )
    19  
    20  var cmdSync = &base.Command{
    21  	UsageLine: "go work sync",
    22  	Short:     "sync workspace build list to modules",
    23  	Long: `Sync syncs the workspace's build list back to the
    24  workspace's modules
    25  
    26  The workspace's build list is the set of versions of all the
    27  (transitive) dependency modules used to do builds in the workspace. go
    28  work sync generates that build list using the Minimal Version Selection
    29  algorithm, and then syncs those versions back to each of modules
    30  specified in the workspace (with use directives).
    31  
    32  The syncing is done by sequentially upgrading each of the dependency
    33  modules specified in a workspace module to the version in the build list
    34  if the dependency module's version is not already the same as the build
    35  list's version. Note that Minimal Version Selection guarantees that the
    36  build list's version of each module is always the same or higher than
    37  that in each workspace module.
    38  
    39  See the workspaces reference at https://go.dev/ref/mod#workspaces
    40  for more information.
    41  `,
    42  	Run: runSync,
    43  }
    44  
    45  func init() {
    46  	base.AddChdirFlag(&cmdSync.Flag)
    47  	base.AddModCommonFlags(&cmdSync.Flag)
    48  }
    49  
    50  func runSync(ctx context.Context, cmd *base.Command, args []string) {
    51  	moduleLoader := modload.NewLoader()
    52  	moduleLoader.ForceUseModules = true
    53  	moduleLoader.InitWorkfile()
    54  	if modload.WorkFilePath(moduleLoader) == "" {
    55  		base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)")
    56  	}
    57  
    58  	_, err := modload.LoadModGraph(moduleLoader, ctx, "")
    59  	if err != nil {
    60  		toolchain.SwitchOrFatal(moduleLoader, ctx, err)
    61  	}
    62  	addFor := map[module.Version][]module.Version{}
    63  
    64  	mms := moduleLoader.MainModules
    65  
    66  	opts := modload.PackageOpts{
    67  		Tags:                     imports.AnyTags(),
    68  		VendorModulesInGOROOTSrc: true,
    69  		ResolveMissingImports:    false,
    70  		LoadTests:                true,
    71  		AllowErrors:              true,
    72  		SilencePackageErrors:     true,
    73  		SilenceUnmatchedWarnings: true,
    74  	}
    75  	for _, m := range mms.Versions() {
    76  		opts.MainModule = m
    77  		_, pkgs := modload.LoadPackages(moduleLoader, ctx, opts, "all")
    78  		opts.MainModule = module.Version{} // reset
    79  
    80  		var (
    81  			addReq   []module.Version
    82  			inAddReq = map[module.Version]bool{}
    83  		)
    84  		for _, pkg := range pkgs {
    85  			if r := moduleLoader.PackageModule(pkg); r.Version != "" && !inAddReq[r] {
    86  				// r has a known version, so force that version.
    87  				addReq = append(addReq, r)
    88  				inAddReq[r] = true
    89  			}
    90  		}
    91  		gover.ModSort(addReq) // ensure determinism
    92  		addFor[m] = addReq
    93  	}
    94  
    95  	workFilePath := modload.WorkFilePath(moduleLoader)
    96  
    97  	var loaders []*modload.Loader
    98  	var goV string
    99  	for _, m := range mms.Versions() {
   100  		if mms.ModRoot(m) == "" && m.Path == "command-line-arguments" {
   101  			// This is not a real module.
   102  			// TODO(#49228): Remove this special case once the special
   103  			// command-line-arguments module is gone.
   104  			continue
   105  		}
   106  
   107  		// Use EnterModule to make a loader with a single work module.
   108  		loader := modload.NewLoader()
   109  		modload.EnterModule(loader, ctx, mms.ModRoot(m))
   110  
   111  		// Edit the build list in the same way that 'go get' would if we
   112  		// requested the relevant module versions explicitly.
   113  		// TODO(#57001): Do we need a toolchain.SwitchOrFatal here,
   114  		// and do we need to pass a toolchain.Switcher in LoadPackages?
   115  		// If so, think about saving the WriteGoMods for after the loop,
   116  		// so we don't write some go.mods with the "before" toolchain
   117  		// and others with the "after" toolchain. If nothing else, that
   118  		// discrepancy could show up in auto-recorded toolchain lines.
   119  		changed, err := modload.EditBuildList(loader, ctx, addFor[m], nil)
   120  		if err != nil {
   121  			base.Fatal(err)
   122  		}
   123  		if changed {
   124  			modload.LoadPackages(loader, ctx, modload.PackageOpts{
   125  				Tags:                     imports.AnyTags(),
   126  				Tidy:                     true,
   127  				VendorModulesInGOROOTSrc: true,
   128  				ResolveMissingImports:    false,
   129  				LoadTests:                true,
   130  				AllowErrors:              true,
   131  				SilenceMissingStdImports: true,
   132  				SilencePackageErrors:     true,
   133  			}, "all")
   134  			// Run UpdateGoModFromReqs before we run WriteGoMod so we can catch errors in it early.
   135  			if _, _, _, err := modload.UpdateGoModFromReqs(loader, ctx, modload.WriteOpts{}); err != nil {
   136  				base.Fatal(err)
   137  			}
   138  			loaders = append(loaders, loader)
   139  		}
   140  		goV = gover.Max(goV, loader.MainModules.GoVersion(loader))
   141  	}
   142  	base.ExitIfErrors()
   143  	for _, loader := range loaders {
   144  		modload.WriteGoMod(loader, ctx, modload.WriteOpts{})
   145  	}
   146  
   147  	wf, err := modload.ReadWorkFile(workFilePath)
   148  	if err != nil {
   149  		base.Fatal(err)
   150  	}
   151  	modload.UpdateWorkGoVersion(wf, goV)
   152  	modload.UpdateWorkFile(wf)
   153  	if err := modload.WriteWorkFile(workFilePath, wf); err != nil {
   154  		base.Fatal(err)
   155  	}
   156  }
   157  

View as plain text