@@ -14,6 +14,7 @@ import (
14
14
"go/build"
15
15
"go/scanner"
16
16
"go/token"
17
+ "internal/goroot"
17
18
"io/fs"
18
19
"os"
19
20
"path"
@@ -30,6 +31,7 @@ import (
30
31
"cmd/go/internal/cfg"
31
32
"cmd/go/internal/fsys"
32
33
"cmd/go/internal/imports"
34
+ "cmd/go/internal/modfetch"
33
35
"cmd/go/internal/modinfo"
34
36
"cmd/go/internal/modload"
35
37
"cmd/go/internal/par"
@@ -38,6 +40,7 @@ import (
38
40
"cmd/go/internal/trace"
39
41
"cmd/internal/sys"
40
42
43
+ "golang.org/x/mod/modfile"
41
44
"golang.org/x/mod/module"
42
45
)
43
46
@@ -2390,6 +2393,13 @@ type PackageOpts struct {
2390
2393
// of those packages could be missing, and resolving those missing dependencies
2391
2394
// could change the selected versions of modules that provide other packages.
2392
2395
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
2393
2403
}
2394
2404
2395
2405
// PackagesAndErrors returns the packages named by the command line arguments
@@ -2480,6 +2490,10 @@ func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string)
2480
2490
}
2481
2491
}
2482
2492
2493
+ if opts .MainOnly {
2494
+ pkgs = mainPackagesOnly (pkgs , patterns )
2495
+ }
2496
+
2483
2497
// Now that CmdlinePkg is set correctly,
2484
2498
// compute the effective flags for all loaded packages
2485
2499
// (not just the ones matching the patterns but also
@@ -2528,6 +2542,51 @@ func CheckPackageErrors(pkgs []*Package) {
2528
2542
base .ExitIfErrors ()
2529
2543
}
2530
2544
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
+
2531
2590
func setToolFlags (pkgs ... * Package ) {
2532
2591
for _ , p := range PackageList (pkgs ) {
2533
2592
p .Internal .Asmflags = BuildAsmflags .For (p )
@@ -2624,3 +2683,131 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa
2624
2683
2625
2684
return pkg
2626
2685
}
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
+ "\t The go.mod file for the module providing named packages contains one or\n " +
2772
+ "\t more %s directives. It must not contain directives that would cause\n " +
2773
+ "\t it 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 \t All 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