From 6a5a45252848b1bac795fc5c3e29680350462f81 Mon Sep 17 00:00:00 2001 From: Ian Alexander Date: Thu, 9 Oct 2025 21:01:05 -0400 Subject: [PATCH] cmd/go: inject vendor dir into builder struct This change adds a new field to the Builder struct to store a function to retrieve the current vendor directory. This allows us to delay the determination of the vendor directory until later, which is currently necessary to successful interaction with the module loader state. This behavior will be changed in a future CL and the Builder field will then be removed. In addition, a new method to get the vendor dir from the module loader state is added that will return the empty string instead of panicing. This commit is part of the overall effort to eliminate global modloader state. Change-Id: Ib0165edb9502d98ddfa986acf5579c1b746a026f Reviewed-on: https://go-review.googlesource.com/c/go/+/711133 Reviewed-by: David Chase Reviewed-by: Michael Matloob LUCI-TryBot-Result: Go LUCI --- src/cmd/go/internal/bug/bug.go | 2 +- src/cmd/go/internal/envcmd/env.go | 6 ++-- src/cmd/go/internal/list/list.go | 2 +- src/cmd/go/internal/modload/init.go | 48 +++++++++++++++++++++++------ src/cmd/go/internal/run/run.go | 2 +- src/cmd/go/internal/test/test.go | 2 +- src/cmd/go/internal/tool/tool.go | 2 +- src/cmd/go/internal/vet/vet.go | 2 +- src/cmd/go/internal/work/action.go | 4 ++- src/cmd/go/internal/work/build.go | 4 +-- src/cmd/go/internal/work/exec.go | 4 +-- src/cmd/go/scriptcmds_test.go | 3 +- 12 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go index ccd8dee95e..637673f7eb 100644 --- a/src/cmd/go/internal/bug/bug.go +++ b/src/cmd/go/internal/bug/bug.go @@ -106,7 +106,7 @@ func printEnvDetails(loaderstate *modload.State, w io.Writer) { func printGoEnv(loaderstate *modload.State, w io.Writer) { env := envcmd.MkEnv() env = append(env, envcmd.ExtraEnvVars(loaderstate)...) - env = append(env, envcmd.ExtraEnvVarsCostly()...) + env = append(env, envcmd.ExtraEnvVarsCostly(loaderstate)...) envcmd.PrintEnv(w, env, false) } diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index 517722a426..aaadc2fde1 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -211,8 +211,8 @@ func ExtraEnvVars(loaderstate *modload.State) []cfg.EnvVar { // ExtraEnvVarsCostly returns environment variables that should not leak into child processes // but are costly to evaluate. -func ExtraEnvVarsCostly() []cfg.EnvVar { - b := work.NewBuilder("") +func ExtraEnvVarsCostly(loaderstate *modload.State) []cfg.EnvVar { + b := work.NewBuilder("", loaderstate.VendorDirOrEmpty) defer func() { if err := b.Close(); err != nil { base.Fatal(err) @@ -337,7 +337,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) { } if needCostly { work.BuildInit(modload.LoaderState) - env = append(env, ExtraEnvVarsCostly()...) + env = append(env, ExtraEnvVarsCostly(modload.LoaderState)...) } if len(args) > 0 { diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go index 454efd3194..200aca007d 100644 --- a/src/cmd/go/internal/list/list.go +++ b/src/cmd/go/internal/list/list.go @@ -713,7 +713,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { // Do we need to run a build to gather information? needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale") if needStale || *listExport || *listCompiled { - b := work.NewBuilder("") + b := work.NewBuilder("", modload.LoaderState.VendorDirOrEmpty) if *listE { b.AllowErrors = true } diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 12f52381b0..274589cbfd 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -183,17 +183,25 @@ func (mms *MainModuleSet) InGorootSrc(m module.Version) bool { } func (mms *MainModuleSet) mustGetSingleMainModule(loaderstate *State) module.Version { + mm, err := mms.getSingleMainModule(loaderstate) + if err != nil { + panic(err) + } + return mm +} + +func (mms *MainModuleSet) getSingleMainModule(loaderstate *State) (module.Version, error) { if mms == nil || len(mms.versions) == 0 { - panic("internal error: mustGetSingleMainModule called in context with no main modules") + return module.Version{}, errors.New("internal error: mustGetSingleMainModule called in context with no main modules") } if len(mms.versions) != 1 { if inWorkspaceMode(loaderstate) { - panic("internal error: mustGetSingleMainModule called in workspace mode") + return module.Version{}, errors.New("internal error: mustGetSingleMainModule called in workspace mode") } else { - panic("internal error: multiple main modules present outside of workspace mode") + return module.Version{}, errors.New("internal error: multiple main modules present outside of workspace mode") } } - return mms.versions[0] + return mms.versions[0], nil } func (mms *MainModuleSet) GetSingleIndexOrNil(loaderstate *State) *modFileIndex { @@ -627,18 +635,38 @@ func Enabled(loaderstate *State) bool { return loaderstate.modRoots != nil || cfg.ModulesEnabled } -func VendorDir(loaderstate *State) string { - if inWorkspaceMode(loaderstate) { - return filepath.Join(filepath.Dir(WorkFilePath(loaderstate)), "vendor") +func (s *State) vendorDir() (string, error) { + if inWorkspaceMode(s) { + return filepath.Join(filepath.Dir(WorkFilePath(s)), "vendor"), nil + } + mainModule, err := s.MainModules.getSingleMainModule(s) + if err != nil { + return "", err } // Even if -mod=vendor, we could be operating with no mod root (and thus no // vendor directory). As long as there are no dependencies that is expected // to work. See script/vendor_outside_module.txt. - modRoot := loaderstate.MainModules.ModRoot(loaderstate.MainModules.mustGetSingleMainModule(loaderstate)) + modRoot := s.MainModules.ModRoot(mainModule) if modRoot == "" { - panic("vendor directory does not exist when in single module mode outside of a module") + return "", errors.New("vendor directory does not exist when in single module mode outside of a module") + } + return filepath.Join(modRoot, "vendor"), nil +} + +func (s *State) VendorDirOrEmpty() string { + dir, err := s.vendorDir() + if err != nil { + return "" + } + return dir +} + +func VendorDir(loaderstate *State) string { + dir, err := loaderstate.vendorDir() + if err != nil { + panic(err) } - return filepath.Join(modRoot, "vendor") + return dir } func inWorkspaceMode(loaderstate *State) bool { diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go index b6d76514b0..c39fcab200 100644 --- a/src/cmd/go/internal/run/run.go +++ b/src/cmd/go/internal/run/run.go @@ -85,7 +85,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { } work.BuildInit(modload.LoaderState) - b := work.NewBuilder("") + b := work.NewBuilder("", modload.LoaderState.VendorDirOrEmpty) defer func() { if err := b.Close(); err != nil { base.Fatal(err) diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 70207cfbd1..ba1b0681c5 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -854,7 +854,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { } } - b := work.NewBuilder("") + b := work.NewBuilder("", modload.LoaderState.VendorDirOrEmpty) defer func() { if err := b.Close(); err != nil { base.Fatal(err) diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go index 75a8fab78a..7ffc50fc87 100644 --- a/src/cmd/go/internal/tool/tool.go +++ b/src/cmd/go/internal/tool/tool.go @@ -337,7 +337,7 @@ func buildAndRunModtool(loaderstate *modload.State, ctx context.Context, toolNam func buildAndRunTool(loaderstate *modload.State, ctx context.Context, tool string, args []string, runTool work.ActorFunc) { work.BuildInit(loaderstate) - b := work.NewBuilder("") + b := work.NewBuilder("", loaderstate.VendorDirOrEmpty) defer func() { if err := b.Close(); err != nil { base.Fatal(err) diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go index c5ba3ae110..0e3689f0c1 100644 --- a/src/cmd/go/internal/vet/vet.go +++ b/src/cmd/go/internal/vet/vet.go @@ -224,7 +224,7 @@ func run(ctx context.Context, cmd *base.Command, args []string) { base.Fatalf("no packages to %s", cmd.Name()) } - b := work.NewBuilder("") + b := work.NewBuilder("", modload.LoaderState.VendorDirOrEmpty) defer func() { if err := b.Close(); err != nil { base.Fatal(err) diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go index 1c6d253be2..0bff1e5e48 100644 --- a/src/cmd/go/internal/work/action.go +++ b/src/cmd/go/internal/work/action.go @@ -40,6 +40,7 @@ import ( // build packages in parallel, and the builder is shared. type Builder struct { WorkDir string // the temporary work directory (ends in filepath.Separator) + getVendorDir func() string // TODO(jitsu): remove this after we eliminate global module state actionCache map[cacheKey]*Action // a cache of already-constructed actions flagCache map[[2]string]bool // a cache of supported compiler flags gccCompilerIDCache map[string]cache.ActionID // cache for gccCompilerID @@ -275,8 +276,9 @@ const ( // and arranges for it to be removed in case of an unclean exit. // The caller must Close the builder explicitly to clean up the WorkDir // before a clean exit. -func NewBuilder(workDir string) *Builder { +func NewBuilder(workDir string, getVendorDir func() string) *Builder { b := new(Builder) + b.getVendorDir = getVendorDir b.actionCache = make(map[cacheKey]*Action) b.gccToolIDCache = make(map[string]string) diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index d5687956b4..59f1df4734 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -461,7 +461,7 @@ var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs } func runBuild(ctx context.Context, cmd *base.Command, args []string) { modload.InitWorkfile(modload.LoaderState) BuildInit(modload.LoaderState) - b := NewBuilder("") + b := NewBuilder("", modload.LoaderState.VendorDirOrEmpty) defer func() { if err := b.Close(); err != nil { base.Fatal(err) @@ -783,7 +783,7 @@ func InstallPackages(loaderstate *modload.State, ctx context.Context, patterns [ } base.ExitIfErrors() - b := NewBuilder("") + b := NewBuilder("", loaderstate.VendorDirOrEmpty) defer func() { if err := b.Close(); err != nil { base.Fatal(err) diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index eb012d2610..7e571c6efb 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -2260,7 +2260,7 @@ func (b *Builder) ccompile(loaderstate *modload.State, a *Action, outfile string } else if m.Dir == "" { // The module is in the vendor directory. Replace the entire vendor // directory path, because the module's Dir is not filled in. - from = modload.VendorDir(loaderstate) + from = b.getVendorDir() toPath = "vendor" } else { from = m.Dir @@ -3383,7 +3383,7 @@ func (b *Builder) swigDoIntSize(objdir string) (intsize string, err error) { } srcs := []string{src} - p := load.GoFilesPackage(modload.LoaderState, context.TODO(), load.PackageOpts{}, srcs) + p := load.GoFilesPackage(modload.NewState(), context.TODO(), load.PackageOpts{}, srcs) if _, _, e := BuildToolchain.gc(b, &Action{Mode: "swigDoIntSize", Package: p, Objdir: objdir}, "", nil, nil, "", false, "", srcs); e != nil { return "32", nil diff --git a/src/cmd/go/scriptcmds_test.go b/src/cmd/go/scriptcmds_test.go index ced8d880e9..8195e830ca 100644 --- a/src/cmd/go/scriptcmds_test.go +++ b/src/cmd/go/scriptcmds_test.go @@ -54,7 +54,8 @@ func scriptCC(cmdExec script.Cmd) script.Cmd { Args: "args...", }, func(s *script.State, args ...string) (script.WaitFunc, error) { - b := work.NewBuilder(s.Getwd()) + fakeVendorDirProvider := func() string { return "" } + b := work.NewBuilder(s.Getwd(), fakeVendorDirProvider) wait, err := cmdExec.Run(s, append(b.GccCmd(".", ""), args...)...) if err != nil { return wait, err -- 2.52.0