Skip to content

Commit e007916

Browse files
committed
cmd/go/internal/load: always use DefaultExecName to determine binary name
It should produce equivalent results to split on the import path of the package rather than its directory, in GOPATH mode. That means the common code in DefaultExecName can be used. We're in the middle of Go 1.12 cycle, so now is a good time to make it happen for Go 1.13. Modify isVersionElement to accept path elements like "v2", "v3", "v10", rather than "/v2", "/v3", "/v10". Then use it in DefaultExecName instead of the ad-hoc isVersion function. There is no change in behavior. Add tests for DefaultExecName and isVersionElement. Updates #26869 Change-Id: Ic6da2c92587459aa2b327385e994b72a6e183092 Reviewed-on: https://go-review.googlesource.com/c/go/+/168678 Run-TryBot: Dmitri Shuralyov <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent db7e746 commit e007916

File tree

2 files changed

+79
-35
lines changed

2 files changed

+79
-35
lines changed

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

+11-35
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ func findVersionElement(path string) (i, j int) {
802802
j = len(path)
803803
for i = len(path) - 1; i >= 0; i-- {
804804
if path[i] == '/' {
805-
if isVersionElement(path[i:j]) {
805+
if isVersionElement(path[i+1 : j]) {
806806
return i, j
807807
}
808808
j = i
@@ -814,10 +814,10 @@ func findVersionElement(path string) (i, j int) {
814814
// isVersionElement reports whether s is a well-formed path version element:
815815
// v2, v3, v10, etc, but not v0, v05, v1.
816816
func isVersionElement(s string) bool {
817-
if len(s) < 3 || s[0] != '/' || s[1] != 'v' || s[2] == '0' || s[2] == '1' && len(s) == 3 {
817+
if len(s) < 2 || s[0] != 'v' || s[1] == '0' || s[1] == '1' && len(s) == 2 {
818818
return false
819819
}
820-
for i := 2; i < len(s); i++ {
820+
for i := 1; i < len(s); i++ {
821821
if s[i] < '0' || '9' < s[i] {
822822
return false
823823
}
@@ -1190,26 +1190,16 @@ var foldPath = make(map[string]string)
11901190
// for a package with the import path importPath.
11911191
//
11921192
// The default executable name is the last element of the import path.
1193-
// In module-aware mode, an additional rule is used. If the last element
1194-
// is a vN path element specifying the major version, then the second last
1195-
// element of the import path is used instead.
1193+
// In module-aware mode, an additional rule is used on import paths
1194+
// consisting of two or more path elements. If the last element is
1195+
// a vN path element specifying the major version, then the
1196+
// second last element of the import path is used instead.
11961197
func DefaultExecName(importPath string) string {
11971198
_, elem := pathpkg.Split(importPath)
11981199
if cfg.ModulesEnabled {
1199-
// If this is example.com/mycmd/v2, it's more useful to install it as mycmd than as v2.
1200-
// See golang.org/issue/24667.
1201-
isVersion := func(v string) bool {
1202-
if len(v) < 2 || v[0] != 'v' || v[1] < '1' || '9' < v[1] {
1203-
return false
1204-
}
1205-
for i := 2; i < len(v); i++ {
1206-
if c := v[i]; c < '0' || '9' < c {
1207-
return false
1208-
}
1209-
}
1210-
return true
1211-
}
1212-
if isVersion(elem) {
1200+
// If this is example.com/mycmd/v2, it's more useful to
1201+
// install it as mycmd than as v2. See golang.org/issue/24667.
1202+
if elem != importPath && isVersionElement(elem) {
12131203
_, elem = pathpkg.Split(pathpkg.Dir(importPath))
12141204
}
12151205
}
@@ -1256,21 +1246,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
12561246
p.Error = &PackageError{Err: e}
12571247
return
12581248
}
1259-
_, elem := filepath.Split(p.Dir)
1260-
if cfg.ModulesEnabled {
1261-
// NOTE(rsc,dmitshur): Using p.ImportPath instead of p.Dir
1262-
// makes sure we install a package in the root of a
1263-
// cached module directory as that package name
1264-
1265-
// Using p.ImportPath instead of p.Dir
1266-
// is probably correct all the time,
1267-
// even for non-module-enabled code,
1268-
// but I'm not brave enough to change the
1269-
// non-module behavior this late in the
1270-
// release cycle. Can be done for Go 1.13.
1271-
// See golang.org/issue/26869.
1272-
elem = DefaultExecName(p.ImportPath)
1273-
}
1249+
elem := DefaultExecName(p.ImportPath)
12741250
full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + "/" + elem
12751251
if cfg.BuildContext.GOOS != base.ToolGOOS || cfg.BuildContext.GOARCH != base.ToolGOARCH {
12761252
// Install cross-compiled binaries to subdirectories of bin.

src/cmd/go/internal/load/pkg_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package load
2+
3+
import (
4+
"cmd/go/internal/cfg"
5+
"testing"
6+
)
7+
8+
func TestDefaultExecName(t *testing.T) {
9+
oldModulesEnabled := cfg.ModulesEnabled
10+
defer func() { cfg.ModulesEnabled = oldModulesEnabled }()
11+
for _, tt := range []struct {
12+
in string
13+
wantMod string
14+
wantGopath string
15+
}{
16+
{"example.com/mycmd", "mycmd", "mycmd"},
17+
{"example.com/mycmd/v0", "v0", "v0"},
18+
{"example.com/mycmd/v1", "v1", "v1"},
19+
{"example.com/mycmd/v2", "mycmd", "v2"}, // Semantic import versioning, use second last element in module mode.
20+
{"example.com/mycmd/v3", "mycmd", "v3"}, // Semantic import versioning, use second last element in module mode.
21+
{"mycmd", "mycmd", "mycmd"},
22+
{"mycmd/v0", "v0", "v0"},
23+
{"mycmd/v1", "v1", "v1"},
24+
{"mycmd/v2", "mycmd", "v2"}, // Semantic import versioning, use second last element in module mode.
25+
{"v0", "v0", "v0"},
26+
{"v1", "v1", "v1"},
27+
{"v2", "v2", "v2"},
28+
} {
29+
{
30+
cfg.ModulesEnabled = true
31+
gotMod := DefaultExecName(tt.in)
32+
if gotMod != tt.wantMod {
33+
t.Errorf("DefaultExecName(%q) in module mode = %v; want %v", tt.in, gotMod, tt.wantMod)
34+
}
35+
}
36+
{
37+
cfg.ModulesEnabled = false
38+
gotGopath := DefaultExecName(tt.in)
39+
if gotGopath != tt.wantGopath {
40+
t.Errorf("DefaultExecName(%q) in gopath mode = %v; want %v", tt.in, gotGopath, tt.wantGopath)
41+
}
42+
}
43+
}
44+
}
45+
46+
func TestIsVersionElement(t *testing.T) {
47+
t.Parallel()
48+
for _, tt := range []struct {
49+
in string
50+
want bool
51+
}{
52+
{"v0", false},
53+
{"v05", false},
54+
{"v1", false},
55+
{"v2", true},
56+
{"v3", true},
57+
{"v9", true},
58+
{"v10", true},
59+
{"v11", true},
60+
{"v", false},
61+
{"vx", false},
62+
} {
63+
got := isVersionElement(tt.in)
64+
if got != tt.want {
65+
t.Errorf("isVersionElement(%q) = %v; want %v", tt.in, got, tt.want)
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)