273 lines
6.2 KiB
Go
273 lines
6.2 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"flag"
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// PlatformFlag is a flag.Value (and flag.Getter) implementation that
|
||
|
// is used to track the os/arch flags on the command-line.
|
||
|
type PlatformFlag struct {
|
||
|
OS []string
|
||
|
Arch []string
|
||
|
OSArch []Platform
|
||
|
}
|
||
|
|
||
|
// Platforms returns the list of platforms that were set by this flag.
|
||
|
// The default set of platforms must be passed in.
|
||
|
func (p *PlatformFlag) Platforms(supported []Platform) []Platform {
|
||
|
// NOTE: Reading this method alone is a bit hard to understand. It
|
||
|
// is much easier to understand this method if you pair this with the
|
||
|
// table of test cases it has.
|
||
|
|
||
|
// Build a list of OS and archs NOT to build
|
||
|
ignoreArch := make(map[string]struct{})
|
||
|
includeArch := make(map[string]struct{})
|
||
|
ignoreOS := make(map[string]struct{})
|
||
|
includeOS := make(map[string]struct{})
|
||
|
ignoreOSArch := make(map[string]Platform)
|
||
|
includeOSArch := make(map[string]Platform)
|
||
|
for _, v := range p.Arch {
|
||
|
if v[0] == '!' {
|
||
|
ignoreArch[v[1:]] = struct{}{}
|
||
|
} else {
|
||
|
includeArch[v] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
for _, v := range p.OS {
|
||
|
if v[0] == '!' {
|
||
|
ignoreOS[v[1:]] = struct{}{}
|
||
|
} else {
|
||
|
includeOS[v] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
for _, v := range p.OSArch {
|
||
|
if v.OS[0] == '!' {
|
||
|
v = Platform{
|
||
|
OS: v.OS[1:],
|
||
|
Arch: v.Arch,
|
||
|
}
|
||
|
|
||
|
ignoreOSArch[v.String()] = v
|
||
|
} else {
|
||
|
includeOSArch[v.String()] = v
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We're building a list of new platforms, so build the list
|
||
|
// based only on the configured OS/arch pairs.
|
||
|
var prefilter []Platform = nil
|
||
|
if len(includeOSArch) > 0 {
|
||
|
prefilter = make([]Platform, 0, len(p.Arch)*len(p.OS)+len(includeOSArch))
|
||
|
for _, v := range includeOSArch {
|
||
|
prefilter = append(prefilter, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if len(includeOS) > 0 && len(includeArch) > 0 {
|
||
|
// Build up the list of prefiltered by what is specified
|
||
|
if prefilter == nil {
|
||
|
prefilter = make([]Platform, 0, len(p.Arch)*len(p.OS))
|
||
|
}
|
||
|
|
||
|
for _, os := range p.OS {
|
||
|
if _, ok := includeOS[os]; !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
for _, arch := range p.Arch {
|
||
|
if _, ok := includeArch[arch]; !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
prefilter = append(prefilter, Platform{
|
||
|
OS: os,
|
||
|
Arch: arch,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
} else if len(includeOS) > 0 {
|
||
|
// Build up the list of prefiltered by what is specified
|
||
|
if prefilter == nil {
|
||
|
prefilter = make([]Platform, 0, len(p.Arch)*len(p.OS))
|
||
|
}
|
||
|
|
||
|
for _, os := range p.OS {
|
||
|
for _, platform := range supported {
|
||
|
if platform.OS == os {
|
||
|
prefilter = append(prefilter, platform)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if prefilter != nil {
|
||
|
// Remove any that aren't supported
|
||
|
result := make([]Platform, 0, len(prefilter))
|
||
|
for _, pending := range prefilter {
|
||
|
found := false
|
||
|
for _, platform := range supported {
|
||
|
if pending.String() == platform.String() {
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if found {
|
||
|
add := pending
|
||
|
add.Default = false
|
||
|
result = append(result, add)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
prefilter = result
|
||
|
}
|
||
|
|
||
|
if prefilter == nil {
|
||
|
prefilter = make([]Platform, 0, len(supported))
|
||
|
for _, v := range supported {
|
||
|
if v.Default {
|
||
|
add := v
|
||
|
add.Default = false
|
||
|
prefilter = append(prefilter, add)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Go through each default platform and filter out the bad ones
|
||
|
result := make([]Platform, 0, len(prefilter))
|
||
|
for _, platform := range prefilter {
|
||
|
if len(ignoreOSArch) > 0 {
|
||
|
if _, ok := ignoreOSArch[platform.String()]; ok {
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We only want to check the components (OS and Arch) if we didn't
|
||
|
// specifically ask to include it via the osarch.
|
||
|
checkComponents := true
|
||
|
if len(includeOSArch) > 0 {
|
||
|
if _, ok := includeOSArch[platform.String()]; ok {
|
||
|
checkComponents = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if checkComponents {
|
||
|
if len(ignoreArch) > 0 {
|
||
|
if _, ok := ignoreArch[platform.Arch]; ok {
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
if len(ignoreOS) > 0 {
|
||
|
if _, ok := ignoreOS[platform.OS]; ok {
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
if len(includeArch) > 0 {
|
||
|
if _, ok := includeArch[platform.Arch]; !ok {
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
if len(includeOS) > 0 {
|
||
|
if _, ok := includeOS[platform.OS]; !ok {
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
result = append(result, platform)
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// ArchFlagValue returns a flag.Value that can be used with the flag
|
||
|
// package to collect the arches for the flag.
|
||
|
func (p *PlatformFlag) ArchFlagValue() flag.Value {
|
||
|
return (*appendStringValue)(&p.Arch)
|
||
|
}
|
||
|
|
||
|
// OSFlagValue returns a flag.Value that can be used with the flag
|
||
|
// package to collect the operating systems for the flag.
|
||
|
func (p *PlatformFlag) OSFlagValue() flag.Value {
|
||
|
return (*appendStringValue)(&p.OS)
|
||
|
}
|
||
|
|
||
|
// OSArchFlagValue returns a flag.Value that can be used with the flag
|
||
|
// package to collect complete os and arch pairs for the flag.
|
||
|
func (p *PlatformFlag) OSArchFlagValue() flag.Value {
|
||
|
return (*appendPlatformValue)(&p.OSArch)
|
||
|
}
|
||
|
|
||
|
// appendPlatformValue is a flag.Value that appends a full platform (os/arch)
|
||
|
// to a list where the values from space-separated lines. This is used to
|
||
|
// satisfy the -osarch flag.
|
||
|
type appendPlatformValue []Platform
|
||
|
|
||
|
func (s *appendPlatformValue) String() string {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
func (s *appendPlatformValue) Set(value string) error {
|
||
|
if value == "" {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
for _, v := range strings.Split(value, " ") {
|
||
|
parts := strings.Split(v, "/")
|
||
|
if len(parts) != 2 {
|
||
|
return fmt.Errorf(
|
||
|
"Invalid platform syntax: %s should be os/arch", v)
|
||
|
}
|
||
|
|
||
|
platform := Platform{
|
||
|
OS: strings.ToLower(parts[0]),
|
||
|
Arch: strings.ToLower(parts[1]),
|
||
|
}
|
||
|
|
||
|
s.appendIfMissing(&platform)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *appendPlatformValue) appendIfMissing(value *Platform) {
|
||
|
for _, existing := range *s {
|
||
|
if existing == *value {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*s = append(*s, *value)
|
||
|
}
|
||
|
|
||
|
// appendStringValue is a flag.Value that appends values to the list,
|
||
|
// where the values come from space-separated lines. This is used to
|
||
|
// satisfy the -os="windows linux" flag to become []string{"windows", "linux"}
|
||
|
type appendStringValue []string
|
||
|
|
||
|
func (s *appendStringValue) String() string {
|
||
|
return strings.Join(*s, " ")
|
||
|
}
|
||
|
|
||
|
func (s *appendStringValue) Set(value string) error {
|
||
|
for _, v := range strings.Split(value, " ") {
|
||
|
if v != "" {
|
||
|
s.appendIfMissing(strings.ToLower(v))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *appendStringValue) appendIfMissing(value string) {
|
||
|
for _, existing := range *s {
|
||
|
if existing == value {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*s = append(*s, value)
|
||
|
}
|