github.com/ahmet2mir/goreleaser@v0.180.3-0.20210927151101-8e5ee5a9b8c5/internal/builders/buildtarget/targets.go (about)

     1  // Package buildtarget can generate a list of targets based on a matrix of
     2  // goos, goarch, goarm, gomips and go version.
     3  package buildtarget
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/apex/log"
    13  	"github.com/fatih/color"
    14  	"github.com/goreleaser/goreleaser/pkg/config"
    15  )
    16  
    17  type target struct {
    18  	os, arch, arm, mips string
    19  }
    20  
    21  func (t target) String() string {
    22  	if t.arm != "" {
    23  		return fmt.Sprintf("%s_%s_%s", t.os, t.arch, t.arm)
    24  	}
    25  	if t.mips != "" {
    26  		return fmt.Sprintf("%s_%s_%s", t.os, t.arch, t.mips)
    27  	}
    28  	return fmt.Sprintf("%s_%s", t.os, t.arch)
    29  }
    30  
    31  // List compiles the list of targets for the given builds.
    32  func List(build config.Build) ([]string, error) {
    33  	version, err := goVersion(build)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	return matrix(build, version)
    38  }
    39  
    40  func matrix(build config.Build, version []byte) ([]string, error) {
    41  	// nolint:prealloc
    42  	var targets []target
    43  	// nolint:prealloc
    44  	var result []string
    45  	for _, target := range allBuildTargets(build) {
    46  		if !contains(target.os, validGoos) {
    47  			return result, fmt.Errorf("invalid goos: %s", target.os)
    48  		}
    49  		if !contains(target.arch, validGoarch) {
    50  			return result, fmt.Errorf("invalid goarch: %s", target.arch)
    51  		}
    52  		if target.arm != "" && !contains(target.arm, validGoarm) {
    53  			return result, fmt.Errorf("invalid goarm: %s", target.arm)
    54  		}
    55  		if target.mips != "" && !contains(target.mips, validGomips) {
    56  			return result, fmt.Errorf("invalid gomips: %s", target.mips)
    57  		}
    58  		if target.os == "darwin" && target.arch == "arm64" && !go116re.Match(version) {
    59  			log.Warn(color.New(color.Bold, color.FgHiYellow).Sprintf(
    60  				"DEPRECATED: skipped darwin/arm64 build on Go < 1.16 for compatibility, check %s for more info.",
    61  				"https://goreleaser.com/deprecations/#builds-for-darwinarm64",
    62  			))
    63  			continue
    64  		}
    65  		if target.os == "windows" && target.arch == "arm64" && !go117re.Match(version) {
    66  			log.Warn(color.New(color.Bold, color.FgHiYellow).Sprintf(
    67  				"DEPRECATED: skipped windows/arm64 build on Go < 1.17 for compatibility, check %s for more info.",
    68  				"https://goreleaser.com/deprecations/#builds-for-windowsarm64",
    69  			))
    70  			continue
    71  		}
    72  		if !valid(target) {
    73  			log.WithField("target", target).Debug("skipped invalid build")
    74  			continue
    75  		}
    76  		if ignored(build, target) {
    77  			log.WithField("target", target).Debug("skipped ignored build")
    78  			continue
    79  		}
    80  		targets = append(targets, target)
    81  	}
    82  	for _, target := range targets {
    83  		result = append(result, target.String())
    84  	}
    85  	return result, nil
    86  }
    87  
    88  func allBuildTargets(build config.Build) (targets []target) {
    89  	for _, goos := range build.Goos {
    90  		for _, goarch := range build.Goarch {
    91  			if goarch == "arm" {
    92  				for _, goarm := range build.Goarm {
    93  					targets = append(targets, target{
    94  						os:   goos,
    95  						arch: goarch,
    96  						arm:  goarm,
    97  					})
    98  				}
    99  				continue
   100  			}
   101  			if strings.HasPrefix(goarch, "mips") {
   102  				for _, gomips := range build.Gomips {
   103  					targets = append(targets, target{
   104  						os:   goos,
   105  						arch: goarch,
   106  						mips: gomips,
   107  					})
   108  				}
   109  				continue
   110  			}
   111  			targets = append(targets, target{
   112  				os:   goos,
   113  				arch: goarch,
   114  			})
   115  		}
   116  	}
   117  	return
   118  }
   119  
   120  // TODO: this could be improved by using a map.
   121  // https://github.com/goreleaser/goreleaser/pull/522#discussion_r164245014
   122  func ignored(build config.Build, target target) bool {
   123  	for _, ig := range build.Ignore {
   124  		if ig.Goos != "" && ig.Goos != target.os {
   125  			continue
   126  		}
   127  		if ig.Goarch != "" && ig.Goarch != target.arch {
   128  			continue
   129  		}
   130  		if ig.Goarm != "" && ig.Goarm != target.arm {
   131  			continue
   132  		}
   133  		if ig.Gomips != "" && ig.Gomips != target.mips {
   134  			continue
   135  		}
   136  		return true
   137  	}
   138  	return false
   139  }
   140  
   141  var (
   142  	go116re = regexp.MustCompile(`go version go1.1[6-9]`)
   143  	go117re = regexp.MustCompile(`go version go1.1[7-9]`)
   144  )
   145  
   146  func goVersion(build config.Build) ([]byte, error) {
   147  	cmd := exec.Command(build.GoBinary, "version")
   148  	// If the build.Dir is acessible, set the cmd dir to it in case
   149  	// of reletive path to GoBinary
   150  	if _, err := os.Stat(build.Dir); err == nil {
   151  		cmd.Dir = build.Dir
   152  	}
   153  	bts, err := cmd.CombinedOutput()
   154  	if err != nil {
   155  		return nil, fmt.Errorf("unable to determine version of go binary (%s): %w", build.GoBinary, err)
   156  	}
   157  	return bts, nil
   158  }
   159  
   160  func valid(target target) bool {
   161  	return contains(target.os+target.arch, validTargets)
   162  }
   163  
   164  func contains(s string, ss []string) bool {
   165  	for _, z := range ss {
   166  		if z == s {
   167  			return true
   168  		}
   169  	}
   170  	return false
   171  }
   172  
   173  // lists from https://golang.org/doc/install/source#environment
   174  // nolint: gochecknoglobals
   175  var (
   176  	validTargets = []string{
   177  		"aixppc64",
   178  		"android386",
   179  		"androidamd64",
   180  		"androidarm",
   181  		"androidarm64",
   182  		"darwinamd64",
   183  		"darwinarm64",
   184  		"dragonflyamd64",
   185  		"freebsd386",
   186  		"freebsdamd64",
   187  		"freebsdarm",
   188  		"freebsdarm64", // not on the official list for some reason, yet its supported on go 1.14+
   189  		"illumosamd64",
   190  		"jswasm",
   191  		"linux386",
   192  		"linuxamd64",
   193  		"linuxarm",
   194  		"linuxarm64",
   195  		"linuxppc64",
   196  		"linuxppc64le",
   197  		"linuxmips",
   198  		"linuxmipsle",
   199  		"linuxmips64",
   200  		"linuxmips64le",
   201  		"linuxs390x",
   202  		"linuxriscv64",
   203  		"netbsd386",
   204  		"netbsdamd64",
   205  		"netbsdarm",
   206  		"openbsd386",
   207  		"openbsdamd64",
   208  		"openbsdarm",
   209  		"openbsdarm64",
   210  		"plan9386",
   211  		"plan9amd64",
   212  		"plan9arm",
   213  		"solarisamd64",
   214  		"windowsarm",
   215  		"windowsarm64",
   216  		"windows386",
   217  		"windowsamd64",
   218  	}
   219  
   220  	validGoos = []string{
   221  		"aix",
   222  		"android",
   223  		"darwin",
   224  		"dragonfly",
   225  		"freebsd",
   226  		"illumos",
   227  		"js",
   228  		"linux",
   229  		"netbsd",
   230  		"openbsd",
   231  		"plan9",
   232  		"solaris",
   233  		"windows",
   234  	}
   235  
   236  	validGoarch = []string{
   237  		"386",
   238  		"amd64",
   239  		"arm",
   240  		"arm64",
   241  		"mips",
   242  		"mips64",
   243  		"mips64le",
   244  		"mipsle",
   245  		"ppc64",
   246  		"ppc64le",
   247  		"s390x",
   248  		"wasm",
   249  		"riscv64",
   250  	}
   251  
   252  	validGoarm  = []string{"5", "6", "7"}
   253  	validGomips = []string{"hardfloat", "softfloat"}
   254  )