cmd/release: start using FreeBSD 11.2 builders for Go 1.15 RC 2+

Add the ability to have builds apply only to select Go versions
according to a module-query-like but fictional syntax. We don't
need to support all arbitrary queries right away, so start with
a smaller set of queries, and let this inform future work.

Fixes golang/go#40563.
Updates golang/go#40558.

Change-Id: I8f7afa90bfe1f91190cee34af51c012216bba455
Reviewed-on: https://go-review.googlesource.com/c/build/+/246597
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Andrew Bonventre <andybons@golang.org>
Reviewed-by: Alexander Rakoczy <alex@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
diff --git a/cmd/release/release.go b/cmd/release/release.go
index df575e0..1949a69 100644
--- a/cmd/release/release.go
+++ b/cmd/release/release.go
@@ -84,6 +84,9 @@
 		if *target != "" && b.String() != *target {
 			continue
 		}
+		if !match(b.GoQuery, *version) {
+			continue
+		}
 		matches++
 		b.logf("Start.")
 		wg.Add(1)
@@ -103,6 +106,10 @@
 }
 
 type Build struct {
+	// GoQuery is a Go version query specifying the Go versions
+	// the build applies to. Empty string means all Go versions.
+	GoQuery string
+
 	OS, Arch string
 	Source   bool
 
@@ -169,15 +176,17 @@
 		Builder: "linux-arm64-packet",
 	},
 	{
+		GoQuery: ">= go1.15rc2", // See #40563.
 		OS:      "freebsd",
 		Arch:    "386",
-		Builder: "freebsd-386-11_1",
+		Builder: "freebsd-386-11_2",
 	},
 	{
+		GoQuery: ">= go1.15rc2", // See #40563.
 		OS:      "freebsd",
 		Arch:    "amd64",
 		Race:    true,
-		Builder: "freebsd-amd64-11_1",
+		Builder: "freebsd-amd64-11_2",
 	},
 	{
 		OS:      "windows",
@@ -213,6 +222,21 @@
 		Builder:   "linux-ppc64le-buildlet",
 	},
 
+	// Older builds.
+	{
+		GoQuery: "< go1.15",
+		OS:      "freebsd",
+		Arch:    "386",
+		Builder: "freebsd-386-11_1",
+	},
+	{
+		GoQuery: "< go1.15",
+		OS:      "freebsd",
+		Arch:    "amd64",
+		Race:    true,
+		Builder: "freebsd-amd64-11_1",
+	},
+
 	// Test-only builds.
 	{
 		Builder: "linux-amd64-longtest",
@@ -933,16 +957,37 @@
 // minSupportedMacOSVersion provides the minimum supported macOS
 // version (of the form N.M) for supported Go versions.
 func minSupportedMacOSVersion(goVer string) string {
-	// TODO(amedee): Use a version package to compare versions of Go.
+	// TODO(amedee,dmitshur,golang.org/issue/40558): Use a version package to compare versions of Go.
 
 	// The minimum supported version of macOS with each version of go:
 	// go1.13 - macOS 10.11
 	// go1.14 - macOS 10.11
 	// go1.15 - macOS 10.12
 	minMacVersion := "10.12"
-	if strings.HasPrefix(goVer, "go1.13") || strings.HasPrefix(goVer, "go1.14") {
+	if match("< go1.15", goVer) {
 		minMacVersion = "10.11"
 		return minMacVersion
 	}
 	return minMacVersion
 }
+
+// match reports whether the Go version goVer matches the provided version query.
+// The empty query matches all Go versions.
+// match panics if given a query that it doesn't support.
+func match(query, goVer string) bool {
+	// TODO(golang.org/issue/40558): This should help inform the API for a Go version parser.
+	switch query {
+	case "": // A special case to make the zero Build.GoQuery value useful.
+		return true
+	case ">= go1.15rc2":
+		// By the time this code is added, Go 1.15 RC 1 has already been released and
+		// won't be modified, that's why we only care about matching RC 2 and onwards.
+		// (We could've just done ">= go1.15", but that could be misleading in future.)
+		return goVer != "go1.15rc1" && !strings.HasPrefix(goVer, "go1.15beta") &&
+			!strings.HasPrefix(goVer, "go1.14") && !strings.HasPrefix(goVer, "go1.13")
+	case "< go1.15":
+		return strings.HasPrefix(goVer, "go1.14") || strings.HasPrefix(goVer, "go1.13")
+	default:
+		panic(fmt.Errorf("match: query %q is not supported", query))
+	}
+}
diff --git a/cmd/release/release_test.go b/cmd/release/release_test.go
index 34609f0..bcacef6 100644
--- a/cmd/release/release_test.go
+++ b/cmd/release/release_test.go
@@ -19,6 +19,19 @@
 	}
 }
 
+func TestAllQueriesSupported(t *testing.T) {
+	for _, b := range builds {
+		t.Run(b.String(), func(t *testing.T) {
+			defer func() {
+				if err := recover(); err != nil {
+					t.Errorf("build %v uses an unsupported version query:\n%v", b, err)
+				}
+			}()
+			match(b.GoQuery, "go1.14.6") // Shouldn't panic for any b.GoQuery.
+		})
+	}
+}
+
 func TestTestOnlyBuildsDontSkipTests(t *testing.T) {
 	for _, b := range builds {
 		if b.TestOnly && b.SkipTests {
@@ -49,3 +62,51 @@
 		})
 	}
 }
+
+func TestFreeBSDBuilder(t *testing.T) {
+	matchBuilds := func(target, goVer string) (matched []*Build) {
+		for _, b := range builds {
+			if b.String() != target || !match(b.GoQuery, goVer) {
+				continue
+			}
+			matched = append(matched, b)
+		}
+		return matched
+	}
+
+	testCases := []struct {
+		goVer       string
+		target      string
+		wantBuilder string
+	}{
+		// Go 1.14.x and 1.13.x still use the FreeBSD 11.1 builder.
+		{"go1.13.55", "freebsd-amd64", "freebsd-amd64-11_1"},
+		{"go1.13.55", "freebsd-386", "freebsd-386-11_1"},
+		{"go1.14.55", "freebsd-amd64", "freebsd-amd64-11_1"},
+		{"go1.14.55", "freebsd-386", "freebsd-386-11_1"},
+
+		// Go 1.15 RC 2+ starts to use the the FreeBSD 11.2 builder.
+		{"go1.15rc2", "freebsd-amd64", "freebsd-amd64-11_2"},
+		{"go1.15rc2", "freebsd-386", "freebsd-386-11_2"},
+		{"go1.15", "freebsd-amd64", "freebsd-amd64-11_2"},
+		{"go1.15", "freebsd-386", "freebsd-386-11_2"},
+		{"go1.15.1", "freebsd-amd64", "freebsd-amd64-11_2"},
+		{"go1.15.1", "freebsd-386", "freebsd-386-11_2"},
+
+		// May change further during the 1.16 dev cycle,
+		// but expect same builder as 1.15 for now.
+		{"go1.16", "freebsd-amd64", "freebsd-amd64-11_2"},
+		{"go1.16", "freebsd-386", "freebsd-386-11_2"},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.goVer, func(t *testing.T) {
+			builds := matchBuilds(tc.target, tc.goVer)
+			if len(builds) != 1 {
+				t.Fatalf("got %d matching builds; want 1", len(builds))
+			}
+			if got, want := builds[0].Builder, tc.wantBuilder; got != want {
+				t.Errorf("got %s; want %s", got, want)
+			}
+		})
+	}
+}