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 <drchase@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Ian Alexander 2025-10-09 21:01:05 -04:00
parent dfac972233
commit 6a5a452528
12 changed files with 56 additions and 25 deletions

View file

@ -106,7 +106,7 @@ func printEnvDetails(loaderstate *modload.State, w io.Writer) {
func printGoEnv(loaderstate *modload.State, w io.Writer) { func printGoEnv(loaderstate *modload.State, w io.Writer) {
env := envcmd.MkEnv() env := envcmd.MkEnv()
env = append(env, envcmd.ExtraEnvVars(loaderstate)...) env = append(env, envcmd.ExtraEnvVars(loaderstate)...)
env = append(env, envcmd.ExtraEnvVarsCostly()...) env = append(env, envcmd.ExtraEnvVarsCostly(loaderstate)...)
envcmd.PrintEnv(w, env, false) envcmd.PrintEnv(w, env, false)
} }

View file

@ -211,8 +211,8 @@ func ExtraEnvVars(loaderstate *modload.State) []cfg.EnvVar {
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes // ExtraEnvVarsCostly returns environment variables that should not leak into child processes
// but are costly to evaluate. // but are costly to evaluate.
func ExtraEnvVarsCostly() []cfg.EnvVar { func ExtraEnvVarsCostly(loaderstate *modload.State) []cfg.EnvVar {
b := work.NewBuilder("") b := work.NewBuilder("", loaderstate.VendorDirOrEmpty)
defer func() { defer func() {
if err := b.Close(); err != nil { if err := b.Close(); err != nil {
base.Fatal(err) base.Fatal(err)
@ -337,7 +337,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
} }
if needCostly { if needCostly {
work.BuildInit(modload.LoaderState) work.BuildInit(modload.LoaderState)
env = append(env, ExtraEnvVarsCostly()...) env = append(env, ExtraEnvVarsCostly(modload.LoaderState)...)
} }
if len(args) > 0 { if len(args) > 0 {

View file

@ -713,7 +713,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
// Do we need to run a build to gather information? // Do we need to run a build to gather information?
needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale") needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale")
if needStale || *listExport || *listCompiled { if needStale || *listExport || *listCompiled {
b := work.NewBuilder("") b := work.NewBuilder("", modload.LoaderState.VendorDirOrEmpty)
if *listE { if *listE {
b.AllowErrors = true b.AllowErrors = true
} }

View file

@ -183,17 +183,25 @@ func (mms *MainModuleSet) InGorootSrc(m module.Version) bool {
} }
func (mms *MainModuleSet) mustGetSingleMainModule(loaderstate *State) module.Version { 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 { 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 len(mms.versions) != 1 {
if inWorkspaceMode(loaderstate) { if inWorkspaceMode(loaderstate) {
panic("internal error: mustGetSingleMainModule called in workspace mode") return module.Version{}, errors.New("internal error: mustGetSingleMainModule called in workspace mode")
} else { } 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 { func (mms *MainModuleSet) GetSingleIndexOrNil(loaderstate *State) *modFileIndex {
@ -627,18 +635,38 @@ func Enabled(loaderstate *State) bool {
return loaderstate.modRoots != nil || cfg.ModulesEnabled return loaderstate.modRoots != nil || cfg.ModulesEnabled
} }
func VendorDir(loaderstate *State) string { func (s *State) vendorDir() (string, error) {
if inWorkspaceMode(loaderstate) { if inWorkspaceMode(s) {
return filepath.Join(filepath.Dir(WorkFilePath(loaderstate)), "vendor") 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 // 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 // vendor directory). As long as there are no dependencies that is expected
// to work. See script/vendor_outside_module.txt. // to work. See script/vendor_outside_module.txt.
modRoot := loaderstate.MainModules.ModRoot(loaderstate.MainModules.mustGetSingleMainModule(loaderstate)) modRoot := s.MainModules.ModRoot(mainModule)
if modRoot == "" { 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") 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 dir
} }
func inWorkspaceMode(loaderstate *State) bool { func inWorkspaceMode(loaderstate *State) bool {

View file

@ -85,7 +85,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
} }
work.BuildInit(modload.LoaderState) work.BuildInit(modload.LoaderState)
b := work.NewBuilder("") b := work.NewBuilder("", modload.LoaderState.VendorDirOrEmpty)
defer func() { defer func() {
if err := b.Close(); err != nil { if err := b.Close(); err != nil {
base.Fatal(err) base.Fatal(err)

View file

@ -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() { defer func() {
if err := b.Close(); err != nil { if err := b.Close(); err != nil {
base.Fatal(err) base.Fatal(err)

View file

@ -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) { func buildAndRunTool(loaderstate *modload.State, ctx context.Context, tool string, args []string, runTool work.ActorFunc) {
work.BuildInit(loaderstate) work.BuildInit(loaderstate)
b := work.NewBuilder("") b := work.NewBuilder("", loaderstate.VendorDirOrEmpty)
defer func() { defer func() {
if err := b.Close(); err != nil { if err := b.Close(); err != nil {
base.Fatal(err) base.Fatal(err)

View file

@ -224,7 +224,7 @@ func run(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("no packages to %s", cmd.Name()) base.Fatalf("no packages to %s", cmd.Name())
} }
b := work.NewBuilder("") b := work.NewBuilder("", modload.LoaderState.VendorDirOrEmpty)
defer func() { defer func() {
if err := b.Close(); err != nil { if err := b.Close(); err != nil {
base.Fatal(err) base.Fatal(err)

View file

@ -40,6 +40,7 @@ import (
// build packages in parallel, and the builder is shared. // build packages in parallel, and the builder is shared.
type Builder struct { type Builder struct {
WorkDir string // the temporary work directory (ends in filepath.Separator) 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 actionCache map[cacheKey]*Action // a cache of already-constructed actions
flagCache map[[2]string]bool // a cache of supported compiler flags flagCache map[[2]string]bool // a cache of supported compiler flags
gccCompilerIDCache map[string]cache.ActionID // cache for gccCompilerID 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. // 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 // The caller must Close the builder explicitly to clean up the WorkDir
// before a clean exit. // before a clean exit.
func NewBuilder(workDir string) *Builder { func NewBuilder(workDir string, getVendorDir func() string) *Builder {
b := new(Builder) b := new(Builder)
b.getVendorDir = getVendorDir
b.actionCache = make(map[cacheKey]*Action) b.actionCache = make(map[cacheKey]*Action)
b.gccToolIDCache = make(map[string]string) b.gccToolIDCache = make(map[string]string)

View file

@ -461,7 +461,7 @@ var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs }
func runBuild(ctx context.Context, cmd *base.Command, args []string) { func runBuild(ctx context.Context, cmd *base.Command, args []string) {
modload.InitWorkfile(modload.LoaderState) modload.InitWorkfile(modload.LoaderState)
BuildInit(modload.LoaderState) BuildInit(modload.LoaderState)
b := NewBuilder("") b := NewBuilder("", modload.LoaderState.VendorDirOrEmpty)
defer func() { defer func() {
if err := b.Close(); err != nil { if err := b.Close(); err != nil {
base.Fatal(err) base.Fatal(err)
@ -783,7 +783,7 @@ func InstallPackages(loaderstate *modload.State, ctx context.Context, patterns [
} }
base.ExitIfErrors() base.ExitIfErrors()
b := NewBuilder("") b := NewBuilder("", loaderstate.VendorDirOrEmpty)
defer func() { defer func() {
if err := b.Close(); err != nil { if err := b.Close(); err != nil {
base.Fatal(err) base.Fatal(err)

View file

@ -2260,7 +2260,7 @@ func (b *Builder) ccompile(loaderstate *modload.State, a *Action, outfile string
} else if m.Dir == "" { } else if m.Dir == "" {
// The module is in the vendor directory. Replace the entire vendor // The module is in the vendor directory. Replace the entire vendor
// directory path, because the module's Dir is not filled in. // directory path, because the module's Dir is not filled in.
from = modload.VendorDir(loaderstate) from = b.getVendorDir()
toPath = "vendor" toPath = "vendor"
} else { } else {
from = m.Dir from = m.Dir
@ -3383,7 +3383,7 @@ func (b *Builder) swigDoIntSize(objdir string) (intsize string, err error) {
} }
srcs := []string{src} 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 { if _, _, e := BuildToolchain.gc(b, &Action{Mode: "swigDoIntSize", Package: p, Objdir: objdir}, "", nil, nil, "", false, "", srcs); e != nil {
return "32", nil return "32", nil

View file

@ -54,7 +54,8 @@ func scriptCC(cmdExec script.Cmd) script.Cmd {
Args: "args...", Args: "args...",
}, },
func(s *script.State, args ...string) (script.WaitFunc, error) { 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...)...) wait, err := cmdExec.Run(s, append(b.GccCmd(".", ""), args...)...)
if err != nil { if err != nil {
return wait, err return wait, err