keep build flags when calling 'go list'

Otherwise any build flags like -tags won't be used, and we might easily
end up with errors or incorrect packages.

The common case with -tags is covered by one of the integration test
scripts. On top of that, we add a table-driven unit test to cover all
edge cases, since there are many we can do quickly in a unit test.

Fixes #82.
pull/91/head
Daniel Martí 5 years ago
parent fc4eb4f940
commit d0e01478f0

@ -102,11 +102,9 @@ var (
seed []byte seed []byte
) )
func saveListedPackages(w io.Writer, test bool, patterns ...string) error { func saveListedPackages(w io.Writer, flags, patterns []string) error {
args := []string{"list", "-json", "-deps", "-export"} args := []string{"list", "-json", "-deps", "-export"}
if test { args = append(args, flags...)
args = append(args, "-test")
}
args = append(args, patterns...) args = append(args, patterns...)
cmd := exec.Command("go", args...) cmd := exec.Command("go", args...)
@ -295,9 +293,14 @@ func mainErr(args []string) error {
return err return err
} }
defer os.Remove(f.Name()) defer os.Remove(f.Name())
// TODO: Pass along flags that 'go list' understands too, such
// as -mod or -modfile. // Note that we also need to pass build flags to 'go list', such
if err := saveListedPackages(f, cmd == "test", args...); err != nil { // as -tags.
listFlags := filterBuildFlags(flags)
if cmd == "test" {
listFlags = append(listFlags, "-test")
}
if err := saveListedPackages(f, listFlags, args); err != nil {
return err return err
} }
os.Setenv("GARBLE_LISTPKGS", f.Name()) os.Setenv("GARBLE_LISTPKGS", f.Name())
@ -959,6 +962,35 @@ func splitFlagsFromArgs(all []string) (flags, args []string) {
return all, nil return all, nil
} }
// buildFlags is obtained from 'go help build' as of Go 1.15.
var buildFlags = map[string]bool{
"-a": true,
"-n": true,
"-p": true,
"-race": true,
"-msan": true,
"-v": true,
"-work": true,
"-x": true,
"-asmflags": true,
"-buildmode": true,
"-compiler": true,
"-gccgoflags": true,
"-gcflags": true,
"-installsuffix": true,
"-ldflags": true,
"-linkshared": true,
"-mod": true,
"-modcacherw": true,
"-modfile": true,
"-pkgdir": true,
"-tags": true,
"-trimpath": true,
"-toolexec": true,
}
// booleanFlags is obtained from 'go help build' and 'go help testflag' as of Go
// 1.15.
var booleanFlags = map[string]bool{ var booleanFlags = map[string]bool{
// Shared build flags. // Shared build flags.
"-a": true, "-a": true,
@ -981,6 +1013,30 @@ var booleanFlags = map[string]bool{
"-benchmem": true, "-benchmem": true,
} }
func filterBuildFlags(flags []string) (filtered []string) {
for i := 0; i < len(flags); i++ {
arg := flags[i]
name := arg
if i := strings.IndexByte(arg, '='); i > 0 {
name = arg[:i]
}
buildFlag := buildFlags[name]
if buildFlag {
filtered = append(filtered, arg)
}
if booleanFlags[arg] || strings.Contains(arg, "=") {
// Either "-bool" or "-name=value".
continue
}
// "-name value", so the next arg is part of this flag.
if i++; i < len(flags) {
filtered = append(filtered, flags[i])
}
}
return filtered
}
// splitFlagsFromFiles splits args into a list of flag and file arguments. Since // splitFlagsFromFiles splits args into a list of flag and file arguments. Since
// we can't rely on "--" being present, and we don't parse all flags upfront, we // we can't rely on "--" being present, and we don't parse all flags upfront, we
// rely on finding the first argument that doesn't begin with "-" and that has // rely on finding the first argument that doesn't begin with "-" and that has

@ -354,6 +354,11 @@ func TestSplitFlagsFromArgs(t *testing.T) {
[]string{"-race", "pkg"}, []string{"-race", "pkg"},
[2][]string{{"-race"}, {"pkg"}}, [2][]string{{"-race"}, {"pkg"}},
}, },
{
"ExplicitBoolFlag",
[]string{"-race=true", "pkg"},
[2][]string{{"-race=true"}, {"pkg"}},
},
} }
for _, test := range tests { for _, test := range tests {
test := test test := test
@ -369,6 +374,38 @@ func TestSplitFlagsFromArgs(t *testing.T) {
} }
} }
func TestFilterBuildFlags(t *testing.T) {
t.Parallel()
tests := []struct {
name string
flags []string
want []string
}{
{"Empty", []string{}, nil},
{
"NoBuild",
[]string{"-short", "-json"},
nil,
},
{
"Mixed",
[]string{"-short", "-tags", "foo", "-mod=readonly", "-json"},
[]string{"-tags", "foo", "-mod=readonly"},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
got := filterBuildFlags(test.flags)
if diff := cmp.Diff(test.want, got); diff != "" {
t.Fatalf("filterBuildFlags(%q) mismatch (-want +got):\n%s", test.flags, diff)
}
})
}
}
func TestFlagValue(t *testing.T) { func TestFlagValue(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {

@ -1,4 +1,4 @@
garble build garble build -tags buildtag
exec ./main exec ./main
cmp stdout main.stdout cmp stdout main.stdout
@ -9,10 +9,10 @@ cmp stdout main.stdout
# Also check that the binary is reproducible when many imports are involved. # Also check that the binary is reproducible when many imports are involved.
cp main$exe main_old$exe cp main$exe main_old$exe
rm main$exe rm main$exe
garble build garble build -tags buildtag
bincmp main$exe main_old$exe bincmp main$exe main_old$exe
go build go build -tags buildtag
exec ./main exec ./main
cmp stdout main.stdout cmp stdout main.stdout
@ -55,6 +55,20 @@ func main() {
linkedPrintln(nil) linkedPrintln(nil)
fmt.Println(quote.Go()) fmt.Println(quote.Go())
} }
-- notag_fail.go --
// +build !buildtag
package main
var foo int = "should be omitted by -tags"
-- withtag_success.go --
// +build buildtag
package main
import "fmt"
func init() { fmt.Println("buildtag init func") }
-- imported/imported.go -- -- imported/imported.go --
package imported package imported
@ -92,6 +106,7 @@ var _ = reflect.TypeOf(ReflectValueOfVar)
type ImportedType int type ImportedType int
-- main.stdout -- -- main.stdout --
buildtag init func
imported var value imported var value
imported const value imported const value
x x

Loading…
Cancel
Save