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] = &copy
   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  }