type TooNewError struct {
What string
GoVersion string
+ Toolchain string // for callers if they want to use it, but not printed
}
func (e *TooNewError) Error() string {
// LoadModGraph need only be called if LoadPackages is not,
// typically in commands that care about modules but no particular package.
func LoadModGraph(ctx context.Context, goVersion string) (*ModuleGraph, error) {
- rs := LoadModFile(ctx)
+ rs, err := loadModFile(ctx, nil)
+ if err != nil {
+ return nil, err
+ }
if goVersion != "" {
v, _ := rs.rootSelected("go")
// it for global consistency. Most callers outside of the modload package should
// use LoadModGraph instead.
func LoadModFile(ctx context.Context) *Requirements {
- return loadModFile(ctx, nil)
+ rs, err := loadModFile(ctx, nil)
+ if err != nil {
+ base.Fatal(err)
+ }
+ return rs
}
-func loadModFile(ctx context.Context, opts *PackageOpts) *Requirements {
+func loadModFile(ctx context.Context, opts *PackageOpts) (*Requirements, error) {
if requirements != nil {
- return requirements
+ return requirements, nil
}
Init()
var err error
workFile, modRoots, err = loadWorkFile(workFilePath)
if err != nil {
- base.Fatalf("reading go.work: %v", err)
+ return nil, fmt.Errorf("reading go.work: %w", err)
}
for _, modRoot := range modRoots {
sumFile := strings.TrimSuffix(modFilePath(modRoot), ".mod") + ".sum"
// with no dependencies.
requirements.initVendor(nil)
}
- return requirements
+ return requirements, nil
}
var modFiles []*modfile.File
var mainModules []module.Version
var indices []*modFileIndex
+ var errs []error
for _, modroot := range modRoots {
gomod := modFilePath(modroot)
var fixed bool
data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed))
if err != nil {
if inWorkspaceMode() {
- base.Fatalf("go: cannot load module %s listed in go.work file: %v", base.ShortPath(gomod), err)
- } else {
- base.Fatalf("go: %v", err)
+ err = fmt.Errorf("cannot load module %s listed in go.work file: %w", base.ShortPath(gomod), err)
}
+ errs = append(errs, err)
+ continue
}
modFiles = append(modFiles, f)
if pathErr, ok := err.(*module.InvalidPathError); ok {
pathErr.Kind = "module"
}
- base.Fatalf("go: %v", err)
+ errs = append(errs, err)
}
}
+ if len(errs) > 0 {
+ return nil, errors.Join(errs...)
+ }
MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFile)
setDefaultBuildMod() // possibly enable automatic vendoring
// We don't need to do anything for vendor or update the mod file so
// return early.
requirements = rs
- return rs
+ return rs, nil
}
mainModule := MainModules.mustGetSingleMainModule()
var err error
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
if err != nil {
- base.Fatal(err)
+ return nil, err
}
}
var err error
rs, err = convertPruning(ctx, rs, pruned)
if err != nil {
- base.Fatal(err)
+ return nil, err
}
}
} else {
}
requirements = rs
- return requirements
+ return requirements, nil
}
// CreateModFile initializes a new module by creating a go.mod file.
}
}
- initialRS := loadModFile(ctx, &opts)
+ initialRS, err := loadModFile(ctx, &opts)
+ if err != nil {
+ base.Fatal(err)
+ }
ld := loadFromRoots(ctx, loaderParams{
PackageOpts: opts,
// ReadModFile reads and parses the mod file at gomod. ReadModFile properly applies the
// overlay, locks the file while reading, and applies fix, if applicable.
func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfile.File, err error) {
+ gomod = base.ShortPath(gomod) // use short path in any errors
if gomodActual, ok := fsys.OverlayPath(gomod); ok {
// Don't lock go.mod if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
f, err = modfile.Parse(gomod, data, fix)
if err != nil {
// Errors returned by modfile.Parse begin with file:line.
- return nil, nil, fmt.Errorf("errors parsing go.mod:\n%s\n", err)
+ return nil, nil, fmt.Errorf("errors parsing %s:\n%w", gomod, err)
}
- if f.Go != nil && gover.Compare(f.Go.Version, gover.Local()) > 0 && cfg.CmdName != "mod edit" {
- base.Fatalf("go: %v", &gover.TooNewError{What: base.ShortPath(gomod), GoVersion: f.Go.Version})
+ if f.Go != nil && gover.Compare(f.Go.Version, gover.Local()) > 0 {
+ toolchain := ""
+ if f.Toolchain != nil {
+ toolchain = f.Toolchain.Name
+ }
+ return nil, nil, &gover.TooNewError{What: gomod, GoVersion: f.Go.Version, Toolchain: toolchain}
}
if f.Module == nil {
// No module declaration. Must add module path.
- return nil, nil, errors.New("no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
+ return nil, nil, fmt.Errorf("error reading %s: missing module declaration. To specify the module path:\n\tgo mod edit -module=example.com/mod", gomod)
}
return data, f, err
# go.mod referenced from go.work too new
cp go.work.old go.work
! go build .
-stderr '^go: go.mod requires go >= 1.99999 \(running go 1\..+\)$'
+stderr '^go: cannot load module go.mod listed in go.work file: go.mod requires go >= 1.99999 \(running go 1\..+\)$'
# go.work too new
cp go.work.new go.work
# Test that go list fails on a go.mod with no module declaration.
cd $WORK/gopath/src/mod
! go list .
-stderr '^go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod$'
+stderr '^go: error reading go.mod: missing module declaration. To specify the module path:\n\tgo mod edit -module=example.com/mod$'
# Test that go mod init in GOPATH doesn't add a module declaration
# with a path that can't possibly be a module path, because
stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2'
cd ..
! go list -m golang.org/x/text
-stderr $WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2'
+stderr '^go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2'
# A pseudo-version with fewer than 12 digits of SHA-1 prefix is invalid.
cp go.mod.orig go.mod