diff --git a/buildtags/syntax.go b/buildtags/syntax.go new file mode 100644 index 0000000..63995ae --- /dev/null +++ b/buildtags/syntax.go @@ -0,0 +1,45 @@ +package buildtags + +import ( + "bufio" + "bytes" + "fmt" + "go/format" + "strings" +) + +// PlusBuildSyntaxSupported reports whether the current Go version supports the +// "// +build" constraint syntax. +func PlusBuildSyntaxSupported() bool { return plusbuild } + +// GoBuildSyntaxSupported reports whether the current Go version supports the +// "//go:build" constraint syntax. +func GoBuildSyntaxSupported() bool { return gobuild } + +// Format constraints according to the syntax supported by the current Go version. +func Format(t ConstraintsConvertable) (string, error) { + // Print build tags to minimal Go source that can be passed to go/format. + src := t.ToConstraints().GoString() + "\npackage stub" + + // Format them. + formatted, err := format.Source([]byte(src)) + if err != nil { + return "", fmt.Errorf("format build constraints: %w", err) + } + + // Extract the comment lines. + buf := bytes.NewReader(formatted) + scanner := bufio.NewScanner(buf) + output := "" + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "//") { + output += line + "\n" + } + } + if err := scanner.Err(); err != nil { + return "", fmt.Errorf("parse formatted build constraints: %w", err) + } + + return output, nil +} diff --git a/buildtags/syntax_go116.go b/buildtags/syntax_go116.go new file mode 100644 index 0000000..07c5068 --- /dev/null +++ b/buildtags/syntax_go116.go @@ -0,0 +1,9 @@ +//go:build !go1.17 +// +build !go1.17 + +package buildtags + +const ( + plusbuild = true + gobuild = false +) diff --git a/buildtags/syntax_go117.go b/buildtags/syntax_go117.go new file mode 100644 index 0000000..a6dca4e --- /dev/null +++ b/buildtags/syntax_go117.go @@ -0,0 +1,9 @@ +//go:build go1.17 && !go1.18 +// +build go1.17,!go1.18 + +package buildtags + +const ( + plusbuild = true + gobuild = true +) diff --git a/buildtags/syntax_go118.go b/buildtags/syntax_go118.go new file mode 100644 index 0000000..7e70c72 --- /dev/null +++ b/buildtags/syntax_go118.go @@ -0,0 +1,9 @@ +//go:build go1.18 +// +build go1.18 + +package buildtags + +const ( + plusbuild = false + gobuild = true +) diff --git a/buildtags/syntax_test.go b/buildtags/syntax_test.go new file mode 100644 index 0000000..d9aac8c --- /dev/null +++ b/buildtags/syntax_test.go @@ -0,0 +1,71 @@ +package buildtags + +import ( + "bytes" + "fmt" + "testing" +) + +func TestFormat(t *testing.T) { + cases := []struct { + PlusBuild []string + GoBuild string + }{ + { + PlusBuild: []string{"amd64"}, + GoBuild: "amd64", + }, + { + PlusBuild: []string{ + "linux darwin", + "amd64 arm64 mips64x ppc64x", + }, + GoBuild: "(linux || darwin) && (amd64 || arm64 || mips64x || ppc64x)", + }, + { + PlusBuild: []string{"!linux,!darwin !amd64,!arm64,!mips64x,!ppc64x"}, + GoBuild: "(!linux && !darwin) || (!amd64 && !arm64 && !mips64x && !ppc64x)", + }, + { + PlusBuild: []string{ + "linux,386 darwin,!cgo", + "!noasm", + }, + GoBuild: "((linux && 386) || (darwin && !cgo)) && !noasm", + }, + } + + for _, c := range cases { + // Parse constraints. + var cs Constraints + for _, expr := range c.PlusBuild { + constraint, err := ParseConstraint(expr) + if err != nil { + t.Fatal(err) + } + cs = append(cs, constraint) + } + + // Build expected output. + var buf bytes.Buffer + if GoBuildSyntaxSupported() { + fmt.Fprintf(&buf, "//go:build %s\n", c.GoBuild) + } + if PlusBuildSyntaxSupported() { + for _, expr := range c.PlusBuild { + fmt.Fprintf(&buf, "// +build %s\n", expr) + } + } + expect := buf.String() + + // Format and check. + got, err := Format(cs) + if err != nil { + t.Fatal(err) + } + + if got != expect { + t.Errorf("got=\n%s\nexpect=\n%s\n", got, expect) + } + } +}