1*03ce13f7SAndroid Build Coastguard Worker// Copyright 2020 The SwiftShader Authors. All Rights Reserved. 2*03ce13f7SAndroid Build Coastguard Worker// 3*03ce13f7SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*03ce13f7SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*03ce13f7SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*03ce13f7SAndroid Build Coastguard Worker// 7*03ce13f7SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*03ce13f7SAndroid Build Coastguard Worker// 9*03ce13f7SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*03ce13f7SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*03ce13f7SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*03ce13f7SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*03ce13f7SAndroid Build Coastguard Worker// limitations under the License. 14*03ce13f7SAndroid Build Coastguard Worker 15*03ce13f7SAndroid Build Coastguard Worker// Package llvm provides functions and types for locating and using the llvm 16*03ce13f7SAndroid Build Coastguard Worker// toolchains. 17*03ce13f7SAndroid Build Coastguard Workerpackage llvm 18*03ce13f7SAndroid Build Coastguard Worker 19*03ce13f7SAndroid Build Coastguard Workerimport ( 20*03ce13f7SAndroid Build Coastguard Worker "bytes" 21*03ce13f7SAndroid Build Coastguard Worker "fmt" 22*03ce13f7SAndroid Build Coastguard Worker "io/ioutil" 23*03ce13f7SAndroid Build Coastguard Worker "net/http" 24*03ce13f7SAndroid Build Coastguard Worker "os" 25*03ce13f7SAndroid Build Coastguard Worker "os/exec" 26*03ce13f7SAndroid Build Coastguard Worker "path/filepath" 27*03ce13f7SAndroid Build Coastguard Worker "regexp" 28*03ce13f7SAndroid Build Coastguard Worker "runtime" 29*03ce13f7SAndroid Build Coastguard Worker "sort" 30*03ce13f7SAndroid Build Coastguard Worker "strconv" 31*03ce13f7SAndroid Build Coastguard Worker 32*03ce13f7SAndroid Build Coastguard Worker "swiftshader.googlesource.com/SwiftShader/tests/regres/util" 33*03ce13f7SAndroid Build Coastguard Worker) 34*03ce13f7SAndroid Build Coastguard Worker 35*03ce13f7SAndroid Build Coastguard Workerconst maxLLVMVersion = 17 36*03ce13f7SAndroid Build Coastguard Worker 37*03ce13f7SAndroid Build Coastguard Worker// Version holds the build version information of an LLVM toolchain. 38*03ce13f7SAndroid Build Coastguard Workertype Version struct { 39*03ce13f7SAndroid Build Coastguard Worker Major, Minor, Point int 40*03ce13f7SAndroid Build Coastguard Worker} 41*03ce13f7SAndroid Build Coastguard Worker 42*03ce13f7SAndroid Build Coastguard Workerfunc (v Version) String() string { 43*03ce13f7SAndroid Build Coastguard Worker return fmt.Sprintf("%v.%v.%v", v.Major, v.Minor, v.Point) 44*03ce13f7SAndroid Build Coastguard Worker} 45*03ce13f7SAndroid Build Coastguard Worker 46*03ce13f7SAndroid Build Coastguard Worker// GreaterEqual returns true if v >= rhs. 47*03ce13f7SAndroid Build Coastguard Workerfunc (v Version) GreaterEqual(rhs Version) bool { 48*03ce13f7SAndroid Build Coastguard Worker if v.Major > rhs.Major { 49*03ce13f7SAndroid Build Coastguard Worker return true 50*03ce13f7SAndroid Build Coastguard Worker } 51*03ce13f7SAndroid Build Coastguard Worker if v.Major < rhs.Major { 52*03ce13f7SAndroid Build Coastguard Worker return false 53*03ce13f7SAndroid Build Coastguard Worker } 54*03ce13f7SAndroid Build Coastguard Worker if v.Minor > rhs.Minor { 55*03ce13f7SAndroid Build Coastguard Worker return true 56*03ce13f7SAndroid Build Coastguard Worker } 57*03ce13f7SAndroid Build Coastguard Worker if v.Minor < rhs.Minor { 58*03ce13f7SAndroid Build Coastguard Worker return false 59*03ce13f7SAndroid Build Coastguard Worker } 60*03ce13f7SAndroid Build Coastguard Worker return v.Point >= rhs.Point 61*03ce13f7SAndroid Build Coastguard Worker} 62*03ce13f7SAndroid Build Coastguard Worker 63*03ce13f7SAndroid Build Coastguard Worker// Download downloads and verifies the LLVM toolchain for the current OS. 64*03ce13f7SAndroid Build Coastguard Workerfunc (v Version) Download() ([]byte, error) { 65*03ce13f7SAndroid Build Coastguard Worker return v.DownloadForOS(runtime.GOOS) 66*03ce13f7SAndroid Build Coastguard Worker} 67*03ce13f7SAndroid Build Coastguard Worker 68*03ce13f7SAndroid Build Coastguard Worker// DownloadForOS downloads and verifies the LLVM toolchain for the given OS. 69*03ce13f7SAndroid Build Coastguard Workerfunc (v Version) DownloadForOS(osName string) ([]byte, error) { 70*03ce13f7SAndroid Build Coastguard Worker url, sig, key, err := v.DownloadInfoForOS(osName) 71*03ce13f7SAndroid Build Coastguard Worker if err != nil { 72*03ce13f7SAndroid Build Coastguard Worker return nil, err 73*03ce13f7SAndroid Build Coastguard Worker } 74*03ce13f7SAndroid Build Coastguard Worker 75*03ce13f7SAndroid Build Coastguard Worker resp, err := http.Get(url) 76*03ce13f7SAndroid Build Coastguard Worker if err != nil { 77*03ce13f7SAndroid Build Coastguard Worker return nil, fmt.Errorf("Could not download LLVM from %v: %v", url, err) 78*03ce13f7SAndroid Build Coastguard Worker } 79*03ce13f7SAndroid Build Coastguard Worker defer resp.Body.Close() 80*03ce13f7SAndroid Build Coastguard Worker 81*03ce13f7SAndroid Build Coastguard Worker content, err := ioutil.ReadAll(resp.Body) 82*03ce13f7SAndroid Build Coastguard Worker if err != nil { 83*03ce13f7SAndroid Build Coastguard Worker return nil, fmt.Errorf("Could not download LLVM from %v: %v", url, err) 84*03ce13f7SAndroid Build Coastguard Worker } 85*03ce13f7SAndroid Build Coastguard Worker 86*03ce13f7SAndroid Build Coastguard Worker if sig != "" { 87*03ce13f7SAndroid Build Coastguard Worker sigfile, err := os.Open(sig) 88*03ce13f7SAndroid Build Coastguard Worker if err != nil { 89*03ce13f7SAndroid Build Coastguard Worker return nil, fmt.Errorf("Couldn't open file '%s': %v", sig, err) 90*03ce13f7SAndroid Build Coastguard Worker } 91*03ce13f7SAndroid Build Coastguard Worker defer sigfile.Close() 92*03ce13f7SAndroid Build Coastguard Worker 93*03ce13f7SAndroid Build Coastguard Worker keyfile, err := os.Open(key) 94*03ce13f7SAndroid Build Coastguard Worker if err != nil { 95*03ce13f7SAndroid Build Coastguard Worker return nil, fmt.Errorf("Couldn't open file '%s': %v", key, err) 96*03ce13f7SAndroid Build Coastguard Worker } 97*03ce13f7SAndroid Build Coastguard Worker defer keyfile.Close() 98*03ce13f7SAndroid Build Coastguard Worker 99*03ce13f7SAndroid Build Coastguard Worker if err := util.CheckPGP(bytes.NewReader(content), sigfile, keyfile); err != nil { 100*03ce13f7SAndroid Build Coastguard Worker return nil, err 101*03ce13f7SAndroid Build Coastguard Worker } 102*03ce13f7SAndroid Build Coastguard Worker } 103*03ce13f7SAndroid Build Coastguard Worker return content, nil 104*03ce13f7SAndroid Build Coastguard Worker} 105*03ce13f7SAndroid Build Coastguard Worker 106*03ce13f7SAndroid Build Coastguard Worker// DownloadInfoForOS returns the download url, signature and key for the given 107*03ce13f7SAndroid Build Coastguard Worker// LLVM version for the given OS. 108*03ce13f7SAndroid Build Coastguard Workerfunc (v Version) DownloadInfoForOS(os string) (url, sig, key string, err error) { 109*03ce13f7SAndroid Build Coastguard Worker switch v { 110*03ce13f7SAndroid Build Coastguard Worker case Version{10, 0, 0}: 111*03ce13f7SAndroid Build Coastguard Worker key = relfile("10.0.0.pub.key") 112*03ce13f7SAndroid Build Coastguard Worker switch os { 113*03ce13f7SAndroid Build Coastguard Worker case "linux": 114*03ce13f7SAndroid Build Coastguard Worker url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" 115*03ce13f7SAndroid Build Coastguard Worker sig = relfile("10.0.0-ubuntu.sig") 116*03ce13f7SAndroid Build Coastguard Worker return 117*03ce13f7SAndroid Build Coastguard Worker case "darwin": 118*03ce13f7SAndroid Build Coastguard Worker url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-apple-darwin.tar.xz" 119*03ce13f7SAndroid Build Coastguard Worker sig = relfile("10.0.0-darwin.sig") 120*03ce13f7SAndroid Build Coastguard Worker return 121*03ce13f7SAndroid Build Coastguard Worker case "windows": 122*03ce13f7SAndroid Build Coastguard Worker url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe" 123*03ce13f7SAndroid Build Coastguard Worker sig = relfile("10.0.0-win64.sig") 124*03ce13f7SAndroid Build Coastguard Worker return 125*03ce13f7SAndroid Build Coastguard Worker default: 126*03ce13f7SAndroid Build Coastguard Worker return "", "", "", fmt.Errorf("Unsupported OS: %v", os) 127*03ce13f7SAndroid Build Coastguard Worker } 128*03ce13f7SAndroid Build Coastguard Worker case Version{17, 0, 6}: 129*03ce13f7SAndroid Build Coastguard Worker switch os { 130*03ce13f7SAndroid Build Coastguard Worker case "linux": 131*03ce13f7SAndroid Build Coastguard Worker url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.6/clang+llvm-17.0.6-x86_64-linux-gnu-ubuntu-22.04.tar.xz" 132*03ce13f7SAndroid Build Coastguard Worker return 133*03ce13f7SAndroid Build Coastguard Worker case "darwin": 134*03ce13f7SAndroid Build Coastguard Worker url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.6/clang+llvm-17.0.6-arm64-apple-darwin22.0.tar.xz" 135*03ce13f7SAndroid Build Coastguard Worker return 136*03ce13f7SAndroid Build Coastguard Worker case "windows": 137*03ce13f7SAndroid Build Coastguard Worker url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.6/LLVM-17.0.6-win64.exe" 138*03ce13f7SAndroid Build Coastguard Worker return 139*03ce13f7SAndroid Build Coastguard Worker default: 140*03ce13f7SAndroid Build Coastguard Worker return "", "", "", fmt.Errorf("Unsupported OS: %v", os) 141*03ce13f7SAndroid Build Coastguard Worker } 142*03ce13f7SAndroid Build Coastguard Worker default: 143*03ce13f7SAndroid Build Coastguard Worker return "", "", "", fmt.Errorf("Unknown download for LLVM %v", v) 144*03ce13f7SAndroid Build Coastguard Worker } 145*03ce13f7SAndroid Build Coastguard Worker} 146*03ce13f7SAndroid Build Coastguard Workerfunc relfile(path string) string { 147*03ce13f7SAndroid Build Coastguard Worker _, thisFile, _, _ := runtime.Caller(1) 148*03ce13f7SAndroid Build Coastguard Worker thisDir := filepath.Dir(thisFile) 149*03ce13f7SAndroid Build Coastguard Worker return filepath.Join(thisDir, path) 150*03ce13f7SAndroid Build Coastguard Worker} 151*03ce13f7SAndroid Build Coastguard Worker 152*03ce13f7SAndroid Build Coastguard Worker// Toolchain holds the paths and version information about an LLVM toolchain. 153*03ce13f7SAndroid Build Coastguard Workertype Toolchain struct { 154*03ce13f7SAndroid Build Coastguard Worker Version Version 155*03ce13f7SAndroid Build Coastguard Worker BinDir string 156*03ce13f7SAndroid Build Coastguard Worker} 157*03ce13f7SAndroid Build Coastguard Worker 158*03ce13f7SAndroid Build Coastguard Worker// Toolchains is a list of Toolchain 159*03ce13f7SAndroid Build Coastguard Workertype Toolchains []Toolchain 160*03ce13f7SAndroid Build Coastguard Worker 161*03ce13f7SAndroid Build Coastguard Worker// Find looks for a toolchain with the specific version. 162*03ce13f7SAndroid Build Coastguard Workerfunc (l Toolchains) Find(v Version) *Toolchain { 163*03ce13f7SAndroid Build Coastguard Worker for _, t := range l { 164*03ce13f7SAndroid Build Coastguard Worker if t.Version == v { 165*03ce13f7SAndroid Build Coastguard Worker return &t 166*03ce13f7SAndroid Build Coastguard Worker } 167*03ce13f7SAndroid Build Coastguard Worker } 168*03ce13f7SAndroid Build Coastguard Worker return nil 169*03ce13f7SAndroid Build Coastguard Worker} 170*03ce13f7SAndroid Build Coastguard Worker 171*03ce13f7SAndroid Build Coastguard Worker// FindAtLeast looks for a toolchain with the given version, returning the highest found version. 172*03ce13f7SAndroid Build Coastguard Workerfunc (l Toolchains) FindAtLeast(v Version) *Toolchain { 173*03ce13f7SAndroid Build Coastguard Worker out := (*Toolchain)(nil) 174*03ce13f7SAndroid Build Coastguard Worker for _, t := range l { 175*03ce13f7SAndroid Build Coastguard Worker if t.Version.GreaterEqual(v) && (out == nil || out.Version.GreaterEqual(t.Version)) { 176*03ce13f7SAndroid Build Coastguard Worker t := t 177*03ce13f7SAndroid Build Coastguard Worker out = &t 178*03ce13f7SAndroid Build Coastguard Worker } 179*03ce13f7SAndroid Build Coastguard Worker } 180*03ce13f7SAndroid Build Coastguard Worker return out 181*03ce13f7SAndroid Build Coastguard Worker} 182*03ce13f7SAndroid Build Coastguard Worker 183*03ce13f7SAndroid Build Coastguard Worker// Search looks for llvm toolchains in paths. 184*03ce13f7SAndroid Build Coastguard Worker// If paths is empty, then PATH is searched. 185*03ce13f7SAndroid Build Coastguard Workerfunc Search(paths ...string) Toolchains { 186*03ce13f7SAndroid Build Coastguard Worker toolchains := map[Version]Toolchain{} 187*03ce13f7SAndroid Build Coastguard Worker search := func(name string) { 188*03ce13f7SAndroid Build Coastguard Worker if len(paths) > 0 { 189*03ce13f7SAndroid Build Coastguard Worker for _, path := range paths { 190*03ce13f7SAndroid Build Coastguard Worker if util.IsFile(path) { 191*03ce13f7SAndroid Build Coastguard Worker path = filepath.Dir(path) 192*03ce13f7SAndroid Build Coastguard Worker } 193*03ce13f7SAndroid Build Coastguard Worker if t := toolchain(path); t != nil { 194*03ce13f7SAndroid Build Coastguard Worker toolchains[t.Version] = *t 195*03ce13f7SAndroid Build Coastguard Worker continue 196*03ce13f7SAndroid Build Coastguard Worker } 197*03ce13f7SAndroid Build Coastguard Worker if t := toolchain(filepath.Join(path, "bin")); t != nil { 198*03ce13f7SAndroid Build Coastguard Worker toolchains[t.Version] = *t 199*03ce13f7SAndroid Build Coastguard Worker continue 200*03ce13f7SAndroid Build Coastguard Worker } 201*03ce13f7SAndroid Build Coastguard Worker } 202*03ce13f7SAndroid Build Coastguard Worker } else { 203*03ce13f7SAndroid Build Coastguard Worker path, err := exec.LookPath(name) 204*03ce13f7SAndroid Build Coastguard Worker if err == nil { 205*03ce13f7SAndroid Build Coastguard Worker if t := toolchain(filepath.Dir(path)); t != nil { 206*03ce13f7SAndroid Build Coastguard Worker toolchains[t.Version] = *t 207*03ce13f7SAndroid Build Coastguard Worker } 208*03ce13f7SAndroid Build Coastguard Worker } 209*03ce13f7SAndroid Build Coastguard Worker } 210*03ce13f7SAndroid Build Coastguard Worker } 211*03ce13f7SAndroid Build Coastguard Worker 212*03ce13f7SAndroid Build Coastguard Worker search("clang") 213*03ce13f7SAndroid Build Coastguard Worker for i := 8; i < maxLLVMVersion; i++ { 214*03ce13f7SAndroid Build Coastguard Worker search(fmt.Sprintf("clang-%d", i)) 215*03ce13f7SAndroid Build Coastguard Worker } 216*03ce13f7SAndroid Build Coastguard Worker 217*03ce13f7SAndroid Build Coastguard Worker out := make([]Toolchain, 0, len(toolchains)) 218*03ce13f7SAndroid Build Coastguard Worker for _, t := range toolchains { 219*03ce13f7SAndroid Build Coastguard Worker out = append(out, t) 220*03ce13f7SAndroid Build Coastguard Worker } 221*03ce13f7SAndroid Build Coastguard Worker sort.Slice(out, func(i, j int) bool { return out[i].Version.GreaterEqual(out[j].Version) }) 222*03ce13f7SAndroid Build Coastguard Worker 223*03ce13f7SAndroid Build Coastguard Worker return out 224*03ce13f7SAndroid Build Coastguard Worker} 225*03ce13f7SAndroid Build Coastguard Worker 226*03ce13f7SAndroid Build Coastguard Worker// Clang returns the path to the clang executable. 227*03ce13f7SAndroid Build Coastguard Workerfunc (t Toolchain) Clang() string { 228*03ce13f7SAndroid Build Coastguard Worker return filepath.Join(t.BinDir, "clang"+exeExt()) 229*03ce13f7SAndroid Build Coastguard Worker} 230*03ce13f7SAndroid Build Coastguard Worker 231*03ce13f7SAndroid Build Coastguard Worker// ClangXX returns the path to the clang++ executable. 232*03ce13f7SAndroid Build Coastguard Workerfunc (t Toolchain) ClangXX() string { 233*03ce13f7SAndroid Build Coastguard Worker return filepath.Join(t.BinDir, "clang++"+exeExt()) 234*03ce13f7SAndroid Build Coastguard Worker} 235*03ce13f7SAndroid Build Coastguard Worker 236*03ce13f7SAndroid Build Coastguard Worker// Cov returns the path to the llvm-cov executable. 237*03ce13f7SAndroid Build Coastguard Workerfunc (t Toolchain) Cov() string { 238*03ce13f7SAndroid Build Coastguard Worker return filepath.Join(t.BinDir, "llvm-cov"+exeExt()) 239*03ce13f7SAndroid Build Coastguard Worker} 240*03ce13f7SAndroid Build Coastguard Worker 241*03ce13f7SAndroid Build Coastguard Worker// Profdata returns the path to the llvm-profdata executable. 242*03ce13f7SAndroid Build Coastguard Workerfunc (t Toolchain) Profdata() string { 243*03ce13f7SAndroid Build Coastguard Worker return filepath.Join(t.BinDir, "llvm-profdata"+exeExt()) 244*03ce13f7SAndroid Build Coastguard Worker} 245*03ce13f7SAndroid Build Coastguard Worker 246*03ce13f7SAndroid Build Coastguard Workerfunc toolchain(dir string) *Toolchain { 247*03ce13f7SAndroid Build Coastguard Worker t := Toolchain{BinDir: dir} 248*03ce13f7SAndroid Build Coastguard Worker if t.resolve() { 249*03ce13f7SAndroid Build Coastguard Worker return &t 250*03ce13f7SAndroid Build Coastguard Worker } 251*03ce13f7SAndroid Build Coastguard Worker return nil 252*03ce13f7SAndroid Build Coastguard Worker} 253*03ce13f7SAndroid Build Coastguard Worker 254*03ce13f7SAndroid Build Coastguard Workerfunc (t *Toolchain) resolve() bool { 255*03ce13f7SAndroid Build Coastguard Worker if !util.IsFile(t.Profdata()) { // llvm-profdata doesn't have --version flag 256*03ce13f7SAndroid Build Coastguard Worker return false 257*03ce13f7SAndroid Build Coastguard Worker } 258*03ce13f7SAndroid Build Coastguard Worker version, ok := parseVersion(t.Cov()) 259*03ce13f7SAndroid Build Coastguard Worker t.Version = version 260*03ce13f7SAndroid Build Coastguard Worker return ok 261*03ce13f7SAndroid Build Coastguard Worker} 262*03ce13f7SAndroid Build Coastguard Worker 263*03ce13f7SAndroid Build Coastguard Workerfunc exeExt() string { 264*03ce13f7SAndroid Build Coastguard Worker switch runtime.GOOS { 265*03ce13f7SAndroid Build Coastguard Worker case "windows": 266*03ce13f7SAndroid Build Coastguard Worker return ".exe" 267*03ce13f7SAndroid Build Coastguard Worker default: 268*03ce13f7SAndroid Build Coastguard Worker return "" 269*03ce13f7SAndroid Build Coastguard Worker } 270*03ce13f7SAndroid Build Coastguard Worker} 271*03ce13f7SAndroid Build Coastguard Worker 272*03ce13f7SAndroid Build Coastguard Workervar versionRE = regexp.MustCompile(`(?:clang|LLVM) version ([0-9]+)\.([0-9]+)\.([0-9]+)`) 273*03ce13f7SAndroid Build Coastguard Worker 274*03ce13f7SAndroid Build Coastguard Workerfunc parseVersion(tool string) (Version, bool) { 275*03ce13f7SAndroid Build Coastguard Worker out, err := exec.Command(tool, "--version").Output() 276*03ce13f7SAndroid Build Coastguard Worker if err != nil { 277*03ce13f7SAndroid Build Coastguard Worker return Version{}, false 278*03ce13f7SAndroid Build Coastguard Worker } 279*03ce13f7SAndroid Build Coastguard Worker matches := versionRE.FindStringSubmatch(string(out)) 280*03ce13f7SAndroid Build Coastguard Worker if len(matches) < 4 { 281*03ce13f7SAndroid Build Coastguard Worker return Version{}, false 282*03ce13f7SAndroid Build Coastguard Worker } 283*03ce13f7SAndroid Build Coastguard Worker major, majorErr := strconv.Atoi(matches[1]) 284*03ce13f7SAndroid Build Coastguard Worker minor, minorErr := strconv.Atoi(matches[2]) 285*03ce13f7SAndroid Build Coastguard Worker point, pointErr := strconv.Atoi(matches[3]) 286*03ce13f7SAndroid Build Coastguard Worker if majorErr != nil || minorErr != nil || pointErr != nil { 287*03ce13f7SAndroid Build Coastguard Worker return Version{}, false 288*03ce13f7SAndroid Build Coastguard Worker } 289*03ce13f7SAndroid Build Coastguard Worker return Version{major, minor, point}, true 290*03ce13f7SAndroid Build Coastguard Worker} 291