@@ -587,6 +587,8 @@ var (
587
587
testCacheExpire time.Time // ignore cached test results before this time
588
588
589
589
testBlockProfile , testCPUProfile , testMemProfile , testMutexProfile , testTrace string // profiling flag that limits test to one package
590
+
591
+ testODir = false
590
592
)
591
593
592
594
// testProfile returns the name of an arbitrary single-package profiling flag
@@ -694,12 +696,6 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
694
696
base .Fatalf ("no packages to test" )
695
697
}
696
698
697
- if testC && len (pkgs ) != 1 {
698
- base .Fatalf ("cannot use -c flag with multiple packages" )
699
- }
700
- if testO != "" && len (pkgs ) != 1 {
701
- base .Fatalf ("cannot use -o flag with multiple packages" )
702
- }
703
699
if testFuzz != "" {
704
700
if ! platform .FuzzSupported (cfg .Goos , cfg .Goarch ) {
705
701
base .Fatalf ("-fuzz flag is not supported on %s/%s" , cfg .Goos , cfg .Goarch )
@@ -749,6 +745,42 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
749
745
if testProfile () != "" && len (pkgs ) != 1 {
750
746
base .Fatalf ("cannot use %s flag with multiple packages" , testProfile ())
751
747
}
748
+
749
+ if testO != "" {
750
+ if strings .HasSuffix (testO , "/" ) || strings .HasSuffix (testO , string (os .PathSeparator )) {
751
+ testODir = true
752
+ } else if fi , err := os .Stat (testO ); err == nil && fi .IsDir () {
753
+ testODir = true
754
+ }
755
+ }
756
+
757
+ if len (pkgs ) > 1 && (testC || testO != "" ) && ! base .IsNull (testO ) {
758
+ if testO != "" && ! testODir {
759
+ base .Fatalf ("with multiple packages, -o must refer to a directory or %s" , os .DevNull )
760
+ }
761
+
762
+ pkgsForBinary := map [string ][]* load.Package {}
763
+
764
+ for _ , p := range pkgs {
765
+ testBinary := testBinaryName (p )
766
+ pkgsForBinary [testBinary ] = append (pkgsForBinary [testBinary ], p )
767
+ }
768
+
769
+ for testBinary , pkgs := range pkgsForBinary {
770
+ if len (pkgs ) > 1 {
771
+ var buf strings.Builder
772
+ for _ , pkg := range pkgs {
773
+ buf .WriteString (pkg .ImportPath )
774
+ buf .WriteString ("\n " )
775
+ }
776
+
777
+ base .Errorf ("cannot write test binary %s for multiple packages:\n %s" , testBinary , buf .String ())
778
+ }
779
+ }
780
+
781
+ base .ExitIfErrors ()
782
+ }
783
+
752
784
initCoverProfile ()
753
785
defer closeCoverProfile ()
754
786
@@ -978,17 +1010,7 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts,
978
1010
buildTest .Deps = append (buildTest .Deps , buildP )
979
1011
}
980
1012
981
- // Use last element of import path, not package name.
982
- // They differ when package name is "main".
983
- // But if the import path is "command-line-arguments",
984
- // like it is during 'go run', use the package name.
985
- var elem string
986
- if p .ImportPath == "command-line-arguments" {
987
- elem = p .Name
988
- } else {
989
- elem = p .DefaultExecName ()
990
- }
991
- testBinary := elem + ".test"
1013
+ testBinary := testBinaryName (p )
992
1014
993
1015
testDir := b .NewObjdir ()
994
1016
if err := b .Mkdir (testDir ); err != nil {
@@ -1048,14 +1070,25 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts,
1048
1070
// -c or profiling flag: create action to copy binary to ./test.out.
1049
1071
target := filepath .Join (base .Cwd (), testBinary + cfg .ExeSuffix )
1050
1072
isNull := false
1073
+
1051
1074
if testO != "" {
1052
1075
target = testO
1053
- if base .IsNull (target ) {
1054
- isNull = true
1055
- } else if ! filepath .IsAbs (target ) {
1056
- target = filepath .Join (base .Cwd (), target )
1076
+
1077
+ if testODir {
1078
+ if filepath .IsAbs (target ) {
1079
+ target = filepath .Join (target , testBinary + cfg .ExeSuffix )
1080
+ } else {
1081
+ target = filepath .Join (base .Cwd (), target , testBinary + cfg .ExeSuffix )
1082
+ }
1083
+ } else {
1084
+ if base .IsNull (target ) {
1085
+ isNull = true
1086
+ } else if ! filepath .IsAbs (target ) {
1087
+ target = filepath .Join (base .Cwd (), target )
1088
+ }
1057
1089
}
1058
1090
}
1091
+
1059
1092
if isNull {
1060
1093
runAction = buildAction
1061
1094
} else {
@@ -1862,3 +1895,19 @@ func printExitStatus(b *work.Builder, ctx context.Context, a *work.Action) error
1862
1895
}
1863
1896
return nil
1864
1897
}
1898
+
1899
+ // testBinaryName can be used to create name for test binary executable.
1900
+ // Use last element of import path, not package name.
1901
+ // They differ when package name is "main".
1902
+ // But if the import path is "command-line-arguments",
1903
+ // like it is during 'go run', use the package name.
1904
+ func testBinaryName (p * load.Package ) string {
1905
+ var elem string
1906
+ if p .ImportPath == "command-line-arguments" {
1907
+ elem = p .Name
1908
+ } else {
1909
+ elem = p .DefaultExecName ()
1910
+ }
1911
+
1912
+ return elem + ".test"
1913
+ }
0 commit comments