golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/releasetargets/releasetargets.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package releasetargets 6 7 import ( 8 "embed" 9 "fmt" 10 "io/fs" 11 "sort" 12 "strings" 13 "sync" 14 15 "golang.org/x/build/maintner/maintnerd/maintapi/version" 16 ) 17 18 type Target struct { 19 Name string 20 GOOS, GOARCH string 21 SecondClass bool // A port that is not a first class port. See go.dev/wiki/PortingPolicy#first-class-ports. 22 ExtraEnv []string // Extra environment variables set during toolchain build. 23 24 // For Darwin targets, the minimum targeted version, e.g. 10.13 or 13. 25 MinMacOSVersion string 26 } 27 28 // ReleaseTargets maps a target name (usually but not always $GOOS-$GOARCH) 29 // to its target. 30 type ReleaseTargets map[string]*Target 31 32 type OSArch struct { 33 OS, Arch string 34 } 35 36 func (o OSArch) String() string { 37 if o.OS == "linux" && o.Arch == "arm" { 38 return "linux-armv6l" 39 } 40 return o.OS + "-" + o.Arch 41 } 42 43 func (rt ReleaseTargets) FirstClassPorts() map[OSArch]bool { 44 result := map[OSArch]bool{} 45 for _, target := range rt { 46 if !target.SecondClass { 47 result[OSArch{target.GOOS, target.GOARCH}] = true 48 } 49 } 50 return result 51 } 52 53 // allReleases contains all the targets for all releases we're currently 54 // supporting. To reduce duplication, targets from earlier versions are 55 // propagated forward unless overridden. To stop configuring a target in a 56 // later release, set it to nil explicitly. 57 // GOOS and GOARCH will be set automatically from the target name, but can be 58 // overridden if necessary. Name will also be set and should not be overridden. 59 var allReleases = map[int]ReleaseTargets{ 60 21: { 61 "darwin-amd64": &Target{ 62 MinMacOSVersion: "10.15", // go.dev/issue/57125 63 }, 64 "darwin-arm64": &Target{ 65 MinMacOSVersion: "11", // Big Sur was the first release with M1 support. 66 }, 67 "linux-386": &Target{}, 68 "linux-armv6l": &Target{ 69 GOARCH: "arm", 70 ExtraEnv: []string{"GOARM=6"}, 71 }, 72 "linux-amd64": &Target{}, 73 "linux-arm64": &Target{}, 74 "windows-386": &Target{}, 75 "windows-amd64": &Target{}, 76 "windows-arm": &Target{ 77 SecondClass: true, 78 }, 79 "windows-arm64": &Target{ 80 SecondClass: true, 81 }, 82 }, 83 23: { 84 "darwin-amd64": &Target{ 85 MinMacOSVersion: "11", // go.dev/issue/64207 86 }, 87 }, 88 } 89 90 //go:generate ./genlatestports.bash 91 //go:embed allports/*.txt 92 var allPortsFS embed.FS 93 var allPorts = map[int][]OSArch{} 94 95 func init() { 96 for _, targets := range allReleases { 97 for name, target := range targets { 98 if target == nil { 99 continue 100 } 101 if target.Name != "" { 102 panic(fmt.Sprintf("target.Name in %q should be left inferred", name)) 103 } 104 target.Name = name 105 parts := strings.SplitN(name, "-", 2) 106 if target.GOOS == "" { 107 target.GOOS = parts[0] 108 } 109 if target.GOARCH == "" { 110 target.GOARCH = parts[1] 111 } 112 113 if (target.MinMacOSVersion != "") != (target.GOOS == "darwin") { 114 panic("must set MinMacOSVersion in target " + target.Name) 115 } 116 } 117 } 118 files, err := allPortsFS.ReadDir("allports") 119 if err != nil { 120 panic(err) 121 } 122 for _, f := range files { 123 major := 0 124 if n, err := fmt.Sscanf(f.Name(), "go1.%d.txt", &major); err != nil || n == 0 { 125 panic("failed to parse filename " + f.Name()) 126 } 127 body, err := fs.ReadFile(allPortsFS, "allports/"+f.Name()) 128 if err != nil { 129 panic(err) 130 } 131 for _, line := range strings.Split(strings.TrimSpace(string(body)), "\n") { 132 os, arch, _ := strings.Cut(line, "/") 133 allPorts[major] = append(allPorts[major], OSArch{os, arch}) 134 } 135 } 136 } 137 138 func sortedReleases() []int { 139 var releases []int 140 for rel := range allReleases { 141 releases = append(releases, rel) 142 } 143 sort.Ints(releases) 144 return releases 145 } 146 147 var unbuildableOSs = map[string]bool{ 148 "android": true, 149 "ios": true, 150 "js": true, 151 "wasip1": true, 152 } 153 154 // TargetsForGo1Point returns the ReleaseTargets that apply to the given 155 // version. 156 func TargetsForGo1Point(x int) ReleaseTargets { 157 targets := ReleaseTargets{} 158 var ports []OSArch 159 for _, release := range sortedReleases() { 160 if release > x { 161 break 162 } 163 for osarch, target := range allReleases[release] { 164 if target == nil { 165 delete(targets, osarch) 166 } else { 167 copy := *target 168 targets[osarch] = © 169 } 170 } 171 if p, ok := allPorts[release]; ok { 172 ports = p 173 } 174 } 175 for _, osarch := range ports { 176 _, unbuildable := unbuildableOSs[osarch.OS] 177 _, exists := targets[osarch.String()] 178 if unbuildable || exists { 179 continue 180 } 181 targets[osarch.String()] = &Target{ 182 Name: osarch.String(), 183 GOOS: osarch.OS, 184 GOARCH: osarch.Arch, 185 SecondClass: true, 186 } 187 } 188 return targets 189 } 190 191 // TargetsForVersion returns the ReleaseTargets for a given Go version string, 192 // e.g. go1.18.1. 193 func TargetsForVersion(versionStr string) (ReleaseTargets, bool) { 194 x, ok := version.Go1PointX(versionStr) 195 if !ok { 196 return nil, false 197 } 198 return TargetsForGo1Point(x), true 199 } 200 201 var latestFCPs map[OSArch]bool 202 var latestFCPsOnce sync.Once 203 204 // LatestFirstClassPorts returns the first class ports in the upcoming release. 205 func LatestFirstClassPorts() map[OSArch]bool { 206 latestFCPsOnce.Do(func() { 207 rels := sortedReleases() 208 latest := rels[len(rels)-1] 209 latestFCPs = TargetsForGo1Point(latest).FirstClassPorts() 210 }) 211 return latestFCPs 212 } 213 214 // IsFirstClass reports whether the given port is first class in the upcoming 215 // release. 216 func IsFirstClass(os, arch string) bool { 217 return LatestFirstClassPorts()[OSArch{os, arch}] 218 }