github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/utils/platform/platform.go (about) 1 // Copyright © 2022 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package platform 16 17 import ( 18 "path" 19 "regexp" 20 "runtime" 21 "strings" 22 23 "github.com/pkg/errors" 24 v1 "github.com/sealerio/sealer/types/api/v1" 25 ) 26 27 var ( 28 specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`) 29 ) 30 31 func ParsePlatforms(v string) ([]*v1.Platform, error) { 32 var pp []*v1.Platform 33 for _, v := range strings.Split(v, ",") { 34 p, err := Parse(v) 35 if err != nil { 36 return nil, errors.Wrapf(err, "failed to parse target platform %s", v) 37 } 38 p = Normalize(p) 39 pp = append(pp, &p) 40 } 41 42 return pp, nil 43 } 44 45 func Normalize(platform v1.Platform) v1.Platform { 46 platform.OS = normalizeOS(platform.OS) 47 platform.Architecture, platform.Variant = NormalizeArch(platform.Architecture, platform.Variant) 48 49 return platform 50 } 51 52 func Parse(specifier string) (v1.Platform, error) { 53 if strings.Contains(specifier, "*") { 54 return v1.Platform{}, errors.Wrapf(ErrInvalidArgument, "%q: wildcards not yet supported", specifier) 55 } 56 57 parts := strings.Split(specifier, "/") 58 59 for _, part := range parts { 60 if !specifierRe.MatchString(part) { 61 return v1.Platform{}, errors.Wrapf(ErrInvalidArgument, "%q is an invalid component of %q: platform specifier component must match %q", part, specifier, specifierRe.String()) 62 } 63 } 64 65 var p v1.Platform 66 switch len(parts) { 67 case 1: 68 // in this case, we will test that the value might be an OS, then look 69 // it up. If it is not known, we'll treat it as an architecture. Since 70 // we have very little information about the platform here, we are 71 // going to be a little stricter if we don't know about the argument 72 // value. 73 p.OS = normalizeOS(parts[0]) 74 if isKnownOS(p.OS) { 75 // picks a default architecture 76 p.Architecture = runtime.GOARCH 77 if p.Architecture == ARM && cpuVariant() != "v7" { 78 p.Variant = cpuVariant() 79 } 80 81 return p, nil 82 } 83 84 p.Architecture, p.Variant = NormalizeArch(parts[0], "") 85 if p.Architecture == ARM && p.Variant == "v7" { 86 p.Variant = "" 87 } 88 if isKnownArch(p.Architecture) { 89 p.OS = runtime.GOOS 90 return p, nil 91 } 92 93 return v1.Platform{}, errors.Wrapf(ErrInvalidArgument, "%q: unknown operating system or architecture", specifier) 94 case 2: 95 // In this case, we treat as a regular os/arch pair. We don't care 96 // about whether we know of the platform. 97 p.OS = normalizeOS(parts[0]) 98 p.Architecture, p.Variant = NormalizeArch(parts[1], "") 99 if p.Architecture == ARM && p.Variant == "v7" { 100 p.Variant = "" 101 } 102 103 return p, nil 104 case 3: 105 // we have a fully specified variant, this is rare 106 p.OS = normalizeOS(parts[0]) 107 p.Architecture, p.Variant = NormalizeArch(parts[1], parts[2]) 108 if p.Architecture == ARM64 && p.Variant == "" { 109 p.Variant = "v8" 110 } 111 112 return p, nil 113 } 114 115 return v1.Platform{}, errors.Wrapf(ErrInvalidArgument, "%q: cannot parse platform specifier", specifier) 116 } 117 118 func GetDefaultPlatform() v1.Platform { 119 return v1.Platform{ 120 OS: runtime.GOOS, 121 Architecture: runtime.GOARCH, 122 // The Variant field will be empty if arch != ARM. 123 Variant: cpuVariant(), 124 } 125 } 126 127 // Format returns a string specifier from the provided platform specification. 128 func Format(platform v1.Platform) string { 129 if platform.OS == "" { 130 return "unknown" 131 } 132 133 return path.Join(platform.OS, platform.Architecture, platform.Variant) 134 } 135 136 // Matched check if src == dest 137 func Matched(src, dest v1.Platform) bool { 138 if src.OS == dest.OS && 139 src.Architecture == ARM64 && dest.Architecture == ARM64 { 140 return true 141 } 142 143 return src.OS == dest.OS && 144 src.Architecture == dest.Architecture && 145 src.Variant == dest.Variant 146 } 147 148 func isLinuxOS(os string) bool { 149 return os == "linux" 150 } 151 152 // NormalizeArch normalizes the architecture. 153 func NormalizeArch(arch, variant string) (string, string) { 154 arch, variant = strings.ToLower(arch), strings.ToLower(variant) 155 switch arch { 156 case "i386": 157 arch = "386" 158 variant = "" 159 case "x86_64", "x86-64": 160 arch = "amd64" 161 variant = "" 162 //nolint 163 case "aarch64", "arm64": 164 arch = "arm64" 165 variant = "v8" 166 case "armhf": 167 //nolint 168 arch = "arm" 169 variant = "v7" 170 case "armel": 171 arch = "arm" 172 variant = "v6" 173 case "arm": 174 switch variant { 175 case "", "7": 176 variant = "v7" 177 case "5", "6", "8": 178 variant = "v" + variant 179 } 180 } 181 182 return arch, variant 183 } 184 185 func isKnownOS(os string) bool { 186 switch os { 187 case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", 188 "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos": 189 return true 190 } 191 return false 192 } 193 194 func isArmArch(arch string) bool { 195 switch arch { 196 case "arm", "arm64": 197 return true 198 } 199 return false 200 } 201 202 func isKnownArch(arch string) bool { 203 switch arch { 204 case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", 205 "ppc64", "ppc64le", "loong64", "mips", "mipsle", "mips64", "mips64le", "mips64p32", 206 "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm": 207 return true 208 } 209 return false 210 } 211 212 func normalizeOS(os string) string { 213 if os == "" { 214 return runtime.GOOS 215 } 216 os = strings.ToLower(os) 217 218 switch os { 219 case "macos": 220 os = "darwin" 221 } 222 return os 223 }