Skip to content

Commit 6c53f99

Browse files
authored
ruleguard: fix go env parsing for Windows (#289)
Windows uses a different output format for `go env`. ``` // Windows set GOPATH=some path // Linux & Darwin GOPATH="some path" ```
1 parent 2f53913 commit 6c53f99

File tree

3 files changed

+153
-24
lines changed

3 files changed

+153
-24
lines changed

internal/goenv/goenv.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package goenv
2+
3+
import (
4+
"errors"
5+
"os/exec"
6+
"runtime"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
func Read() (map[string]string, error) {
12+
out, err := exec.Command("go", "env").CombinedOutput()
13+
if err != nil {
14+
return nil, err
15+
}
16+
return parseGoEnv(out, runtime.GOOS)
17+
}
18+
19+
func parseGoEnv(data []byte, goos string) (map[string]string, error) {
20+
vars := make(map[string]string)
21+
22+
lines := strings.Split(strings.ReplaceAll(string(data), "\r\n", "\n"), "\n")
23+
24+
if goos == "windows" {
25+
// Line format is: `set $name=$value`
26+
for _, l := range lines {
27+
l = strings.TrimPrefix(l, "set ")
28+
parts := strings.Split(l, "=")
29+
if len(parts) != 2 {
30+
continue
31+
}
32+
vars[parts[0]] = parts[1]
33+
}
34+
} else {
35+
// Line format is: `$name="$value"`
36+
for _, l := range lines {
37+
parts := strings.Split(strings.TrimSpace(l), "=")
38+
if len(parts) != 2 {
39+
continue
40+
}
41+
val, err := strconv.Unquote(parts[1])
42+
if err != nil {
43+
continue
44+
}
45+
vars[parts[0]] = val
46+
}
47+
}
48+
49+
if len(vars) == 0 {
50+
return nil, errors.New("empty env set")
51+
}
52+
53+
return vars, nil
54+
}

internal/goenv/goenv_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package goenv
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestParse(t *testing.T) {
9+
tests := []struct {
10+
goos string
11+
lines []string
12+
goroot string
13+
gopath string
14+
}{
15+
{
16+
goos: "windows",
17+
lines: []string{
18+
"set GOROOT=C:\\Program Files\\Go\r\n",
19+
"set GOPATH=C:\\Users\\me\\go\r\n",
20+
},
21+
goroot: "C:\\Program Files\\Go",
22+
gopath: "C:\\Users\\me\\go",
23+
},
24+
25+
// Don't do trim on Windows.
26+
{
27+
goos: "windows",
28+
lines: []string{
29+
"set GOROOT=C:\\Program Files\\Go \r\n",
30+
"set GOPATH=C:\\Users\\me\\go \r\n",
31+
},
32+
goroot: "C:\\Program Files\\Go ",
33+
gopath: "C:\\Users\\me\\go ",
34+
},
35+
36+
{
37+
goos: "linux",
38+
lines: []string{
39+
"GOROOT=\"/usr/local/go\"\n",
40+
"GOPATH=\"/home/me/go\"\n",
41+
},
42+
goroot: "/usr/local/go",
43+
gopath: "/home/me/go",
44+
},
45+
46+
// Trim lines on Linux.
47+
{
48+
goos: "linux",
49+
lines: []string{
50+
" GOROOT=\"/usr/local/go\" \n",
51+
"GOPATH=\"/home/me/go\" \n",
52+
},
53+
goroot: "/usr/local/go",
54+
gopath: "/home/me/go",
55+
},
56+
57+
// Quotes preserve the whitespace.
58+
{
59+
goos: "linux",
60+
lines: []string{
61+
" GOROOT=\"/usr/local/go \" \n",
62+
"GOPATH=\"/home/me/go \" \n",
63+
},
64+
goroot: "/usr/local/go ",
65+
gopath: "/home/me/go ",
66+
},
67+
}
68+
69+
for i, test := range tests {
70+
data := []byte(strings.Join(test.lines, ""))
71+
vars, err := parseGoEnv(data, test.goos)
72+
if err != nil {
73+
t.Fatalf("test %d failed: %v", i, err)
74+
}
75+
if vars["GOROOT"] != test.goroot {
76+
t.Errorf("test %d GOROOT mismatch: have %q, want %q", i, vars["GOROOT"], test.goroot)
77+
continue
78+
}
79+
if vars["GOPATH"] != test.gopath {
80+
t.Errorf("test %d GOPATH mismatch: have %q, want %q", i, vars["GOPATH"], test.gopath)
81+
continue
82+
}
83+
}
84+
}

ruleguard/engine.go

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package ruleguard
22

33
import (
4-
"bytes"
54
"errors"
65
"fmt"
76
"go/ast"
@@ -10,12 +9,12 @@ import (
109
"go/types"
1110
"io"
1211
"io/ioutil"
13-
"os/exec"
12+
"os"
1413
"sort"
15-
"strconv"
1614
"strings"
1715
"sync"
1816

17+
"github.com/quasilyte/go-ruleguard/internal/goenv"
1918
"github.com/quasilyte/go-ruleguard/internal/stdinfo"
2019
"github.com/quasilyte/go-ruleguard/ruleguard/ir"
2120
"github.com/quasilyte/go-ruleguard/ruleguard/quasigo"
@@ -239,35 +238,27 @@ func (state *engineState) findTypeNoCache(importer *goImporter, currentPkg *type
239238
}
240239

241240
func inferBuildContext() *build.Context {
242-
goEnv := func() map[string]string {
243-
out, err := exec.Command("go", "env").CombinedOutput()
244-
if err != nil {
245-
return nil
246-
}
247-
vars := make(map[string]string)
248-
for _, l := range bytes.Split(out, []byte("\n")) {
249-
parts := strings.Split(strings.TrimSpace(string(l)), "=")
250-
if len(parts) != 2 {
251-
continue
252-
}
253-
val, err := strconv.Unquote(parts[1])
254-
if err != nil {
255-
continue
256-
}
257-
vars[parts[0]] = val
258-
}
259-
return vars
260-
}
261-
262241
// Inherit most fields from the build.Default.
263242
ctx := build.Default
264243

265-
env := goEnv()
244+
env, err := goenv.Read()
245+
if err != nil {
246+
return &ctx
247+
}
266248

267249
ctx.GOROOT = env["GOROOT"]
268250
ctx.GOPATH = env["GOPATH"]
269251
ctx.GOARCH = env["GOARCH"]
270252
ctx.GOOS = env["GOOS"]
271253

254+
switch os.Getenv("CGO_ENABLED") {
255+
case "0":
256+
ctx.CgoEnabled = false
257+
case "1":
258+
ctx.CgoEnabled = true
259+
default:
260+
ctx.CgoEnabled = env["CGO_ENABLED"] == "1"
261+
}
262+
272263
return &ctx
273264
}

0 commit comments

Comments
 (0)