Skip to content

Commit 0613c74

Browse files
author
Jay Conrod
committed
cmd/go: move 'go install cmd@version' code into internal/load
'go run cmd@version' will use the same code. This changes error messages a bit. For #42088 Change-Id: Iaed3997a3d27f9fc0e868013ab765f1fb638a0b5 Reviewed-on: https://go-review.googlesource.com/c/go/+/310410 Trust: Jay Conrod <jayconrod@google.com> Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
1 parent dc76c47 commit 0613c74

File tree

3 files changed

+210
-147
lines changed

3 files changed

+210
-147
lines changed

src/cmd/go/internal/load/pkg.go

+187
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"go/build"
1515
"go/scanner"
1616
"go/token"
17+
"internal/goroot"
1718
"io/fs"
1819
"os"
1920
"path"
@@ -30,6 +31,7 @@ import (
3031
"cmd/go/internal/cfg"
3132
"cmd/go/internal/fsys"
3233
"cmd/go/internal/imports"
34+
"cmd/go/internal/modfetch"
3335
"cmd/go/internal/modinfo"
3436
"cmd/go/internal/modload"
3537
"cmd/go/internal/par"
@@ -38,6 +40,7 @@ import (
3840
"cmd/go/internal/trace"
3941
"cmd/internal/sys"
4042

43+
"golang.org/x/mod/modfile"
4144
"golang.org/x/mod/module"
4245
)
4346

@@ -2390,6 +2393,13 @@ type PackageOpts struct {
23902393
// of those packages could be missing, and resolving those missing dependencies
23912394
// could change the selected versions of modules that provide other packages.
23922395
ModResolveTests bool
2396+
2397+
// MainOnly is true if the caller only wants to load main packages.
2398+
// For a literal argument matching a non-main package, a stub may be returned
2399+
// with an error. For a non-literal argument (with "..."), non-main packages
2400+
// are not be matched, and their dependencies may not be loaded. A warning
2401+
// may be printed for non-literal arguments that match no main packages.
2402+
MainOnly bool
23932403
}
23942404

23952405
// PackagesAndErrors returns the packages named by the command line arguments
@@ -2480,6 +2490,10 @@ func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string)
24802490
}
24812491
}
24822492

2493+
if opts.MainOnly {
2494+
pkgs = mainPackagesOnly(pkgs, patterns)
2495+
}
2496+
24832497
// Now that CmdlinePkg is set correctly,
24842498
// compute the effective flags for all loaded packages
24852499
// (not just the ones matching the patterns but also
@@ -2528,6 +2542,51 @@ func CheckPackageErrors(pkgs []*Package) {
25282542
base.ExitIfErrors()
25292543
}
25302544

2545+
// mainPackagesOnly filters out non-main packages matched only by arguments
2546+
// containing "..." and returns the remaining main packages.
2547+
//
2548+
// mainPackagesOnly sets a package's error if it is named by a literal argument.
2549+
//
2550+
// mainPackagesOnly prints warnings for non-literal arguments that only match
2551+
// non-main packages.
2552+
func mainPackagesOnly(pkgs []*Package, patterns []string) []*Package {
2553+
matchers := make([]func(string) bool, len(patterns))
2554+
for i, p := range patterns {
2555+
if strings.Contains(p, "...") {
2556+
matchers[i] = search.MatchPattern(p)
2557+
}
2558+
}
2559+
2560+
mainPkgs := make([]*Package, 0, len(pkgs))
2561+
mainCount := make([]int, len(patterns))
2562+
nonMainCount := make([]int, len(patterns))
2563+
for _, pkg := range pkgs {
2564+
if pkg.Name == "main" {
2565+
mainPkgs = append(mainPkgs, pkg)
2566+
for i := range patterns {
2567+
if matchers[i] != nil && matchers[i](pkg.ImportPath) {
2568+
mainCount[i]++
2569+
}
2570+
}
2571+
} else {
2572+
for i := range patterns {
2573+
if matchers[i] == nil && patterns[i] == pkg.ImportPath && pkg.Error == nil {
2574+
pkg.Error = &PackageError{Err: ImportErrorf(pkg.ImportPath, "package %s is not a main package", pkg.ImportPath)}
2575+
} else if matchers[i] != nil && matchers[i](pkg.ImportPath) {
2576+
nonMainCount[i]++
2577+
}
2578+
}
2579+
}
2580+
}
2581+
for i, p := range patterns {
2582+
if matchers[i] != nil && mainCount[i] == 0 && nonMainCount[i] > 0 {
2583+
fmt.Fprintf(os.Stderr, "go: warning: %q matched no main packages\n", p)
2584+
}
2585+
}
2586+
2587+
return mainPkgs
2588+
}
2589+
25312590
func setToolFlags(pkgs ...*Package) {
25322591
for _, p := range PackageList(pkgs) {
25332592
p.Internal.Asmflags = BuildAsmflags.For(p)
@@ -2624,3 +2683,131 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa
26242683

26252684
return pkg
26262685
}
2686+
2687+
// PackagesAndErrorsOutsideModule is like PackagesAndErrors but runs in
2688+
// module-aware mode and ignores the go.mod file in the current directory or any
2689+
// parent directory, if there is one. This is used in the implementation of 'go
2690+
// install pkg@version' and other commands that support similar forms.
2691+
//
2692+
// modload.ForceUseModules must be true, and modload.RootMode must be NoRoot
2693+
// before calling this function.
2694+
//
2695+
// PackagesAndErrorsOutsideModule imposes several constraints to avoid
2696+
// ambiguity. All arguments must have the same version suffix (not just a suffix
2697+
// that resolves to the same version). They must refer to packages in the same
2698+
// module, which must not be std or cmd. That module is not considered the main
2699+
// module, but its go.mod file (if it has one) must not contain directives that
2700+
// would cause it to be interpreted differently if it were the main module
2701+
// (replace, exclude).
2702+
func PackagesAndErrorsOutsideModule(ctx context.Context, opts PackageOpts, args []string) ([]*Package, error) {
2703+
if !modload.ForceUseModules {
2704+
panic("modload.ForceUseModules must be true")
2705+
}
2706+
if modload.RootMode != modload.NoRoot {
2707+
panic("modload.RootMode must be NoRoot")
2708+
}
2709+
2710+
// Check that the arguments satisfy syntactic constraints.
2711+
var version string
2712+
for _, arg := range args {
2713+
if i := strings.Index(arg, "@"); i >= 0 {
2714+
version = arg[i+1:]
2715+
if version == "" {
2716+
return nil, fmt.Errorf("%s: version must not be empty", arg)
2717+
}
2718+
break
2719+
}
2720+
}
2721+
patterns := make([]string, len(args))
2722+
for i, arg := range args {
2723+
if !strings.HasSuffix(arg, "@"+version) {
2724+
return nil, fmt.Errorf("%s: all arguments must have the same version (@%s)", arg, version)
2725+
}
2726+
p := arg[:len(arg)-len(version)-1]
2727+
switch {
2728+
case build.IsLocalImport(p):
2729+
return nil, fmt.Errorf("%s: argument must be a package path, not a relative path", arg)
2730+
case filepath.IsAbs(p):
2731+
return nil, fmt.Errorf("%s: argument must be a package path, not an absolute path", arg)
2732+
case search.IsMetaPackage(p):
2733+
return nil, fmt.Errorf("%s: argument must be a package path, not a meta-package", arg)
2734+
case path.Clean(p) != p:
2735+
return nil, fmt.Errorf("%s: argument must be a clean package path", arg)
2736+
case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p):
2737+
return nil, fmt.Errorf("%s: argument must not be a package in the standard library", arg)
2738+
default:
2739+
patterns[i] = p
2740+
}
2741+
}
2742+
2743+
// Query the module providing the first argument, load its go.mod file, and
2744+
// check that it doesn't contain directives that would cause it to be
2745+
// interpreted differently if it were the main module.
2746+
//
2747+
// If multiple modules match the first argument, accept the longest match
2748+
// (first result). It's possible this module won't provide packages named by
2749+
// later arguments, and other modules would. Let's not try to be too
2750+
// magical though.
2751+
allowed := modload.CheckAllowed
2752+
if modload.IsRevisionQuery(version) {
2753+
// Don't check for retractions if a specific revision is requested.
2754+
allowed = nil
2755+
}
2756+
noneSelected := func(path string) (version string) { return "none" }
2757+
qrs, err := modload.QueryPackages(ctx, patterns[0], version, noneSelected, allowed)
2758+
if err != nil {
2759+
return nil, fmt.Errorf("%s: %w", args[0], err)
2760+
}
2761+
rootMod := qrs[0].Mod
2762+
data, err := modfetch.GoMod(rootMod.Path, rootMod.Version)
2763+
if err != nil {
2764+
return nil, fmt.Errorf("%s: %w", args[0], err)
2765+
}
2766+
f, err := modfile.Parse("go.mod", data, nil)
2767+
if err != nil {
2768+
return nil, fmt.Errorf("%s (in %s): %w", args[0], rootMod, err)
2769+
}
2770+
directiveFmt := "%s (in %s):\n" +
2771+
"\tThe go.mod file for the module providing named packages contains one or\n" +
2772+
"\tmore %s directives. It must not contain directives that would cause\n" +
2773+
"\tit to be interpreted differently than if it were the main module."
2774+
if len(f.Replace) > 0 {
2775+
return nil, fmt.Errorf(directiveFmt, args[0], rootMod, "replace")
2776+
}
2777+
if len(f.Exclude) > 0 {
2778+
return nil, fmt.Errorf(directiveFmt, args[0], rootMod, "exclude")
2779+
}
2780+
2781+
// Since we are in NoRoot mode, the build list initially contains only
2782+
// the dummy command-line-arguments module. Add a requirement on the
2783+
// module that provides the packages named on the command line.
2784+
if _, err := modload.EditBuildList(ctx, nil, []module.Version{rootMod}); err != nil {
2785+
return nil, fmt.Errorf("%s: %w", args[0], err)
2786+
}
2787+
2788+
// Load packages for all arguments.
2789+
pkgs := PackagesAndErrors(ctx, opts, patterns)
2790+
2791+
// Check that named packages are all provided by the same module.
2792+
for _, pkg := range pkgs {
2793+
var pkgErr error
2794+
if pkg.Module == nil {
2795+
// Packages in std, cmd, and their vendored dependencies
2796+
// don't have this field set.
2797+
pkgErr = fmt.Errorf("package %s not provided by module %s", pkg.ImportPath, rootMod)
2798+
} else if pkg.Module.Path != rootMod.Path || pkg.Module.Version != rootMod.Version {
2799+
pkgErr = fmt.Errorf("package %s provided by module %s@%s\n\tAll packages must be provided by the same module (%s).", pkg.ImportPath, pkg.Module.Path, pkg.Module.Version, rootMod)
2800+
}
2801+
if pkgErr != nil && pkg.Error == nil {
2802+
pkg.Error = &PackageError{Err: pkgErr}
2803+
}
2804+
}
2805+
2806+
matchers := make([]func(string) bool, len(patterns))
2807+
for i, p := range patterns {
2808+
if strings.Contains(p, "...") {
2809+
matchers[i] = search.MatchPattern(p)
2810+
}
2811+
}
2812+
return pkgs, nil
2813+
}

0 commit comments

Comments
 (0)