github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/builder/commands.go (about) 1 package builder 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "os/exec" 8 "runtime" 9 "strings" 10 11 "tinygo.org/x/go-llvm" 12 ) 13 14 // Commands lists command alternatives for various operating systems. These 15 // commands may have a slightly different name across operating systems and 16 // distributions or may not even exist in $PATH, in which case absolute paths 17 // may be used. 18 var commands = map[string][]string{} 19 20 func init() { 21 llvmMajor := strings.Split(llvm.Version, ".")[0] 22 commands["clang"] = []string{"clang-" + llvmMajor} 23 commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"} 24 commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"} 25 commands["lldb"] = []string{"lldb-" + llvmMajor, "lldb"} 26 // Add the path to a Homebrew-installed LLVM for ease of use (no need to 27 // manually set $PATH). 28 if runtime.GOOS == "darwin" { 29 var prefix string 30 switch runtime.GOARCH { 31 case "amd64": 32 prefix = "/usr/local/opt/llvm@" + llvmMajor + "/bin/" 33 case "arm64": 34 prefix = "/opt/homebrew/opt/llvm@" + llvmMajor + "/bin/" 35 default: 36 // unknown GOARCH 37 panic(fmt.Sprintf("unknown GOARCH: %s on darwin", runtime.GOARCH)) 38 } 39 commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor) 40 commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld") 41 commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld") 42 commands["lldb"] = append(commands["lldb"], prefix+"lldb") 43 } 44 // Add the path for when LLVM was installed with the installer from 45 // llvm.org, which by default doesn't add LLVM to the $PATH environment 46 // variable. 47 if runtime.GOOS == "windows" { 48 commands["clang"] = append(commands["clang"], "clang", "C:\\Program Files\\LLVM\\bin\\clang.exe") 49 commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe") 50 commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe") 51 commands["lldb"] = append(commands["lldb"], "C:\\Program Files\\LLVM\\bin\\lldb.exe") 52 } 53 // Add the path to LLVM installed from ports. 54 if runtime.GOOS == "freebsd" { 55 prefix := "/usr/local/llvm" + llvmMajor + "/bin/" 56 commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor) 57 commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld") 58 commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld") 59 commands["lldb"] = append(commands["lldb"], prefix+"lldb") 60 } 61 } 62 63 // LookupCommand looks up the executable name for a given LLVM tool such as 64 // clang or wasm-ld. It returns the (relative) command that can be used to 65 // invoke the tool or an error if it could not be found. 66 func LookupCommand(name string) (string, error) { 67 for _, cmdName := range commands[name] { 68 _, err := exec.LookPath(cmdName) 69 if err != nil { 70 if errors.Unwrap(err) == exec.ErrNotFound { 71 continue 72 } 73 return cmdName, err 74 } 75 return cmdName, nil 76 } 77 return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " ")) 78 } 79 80 func execCommand(name string, args ...string) error { 81 name, err := LookupCommand(name) 82 if err != nil { 83 return err 84 } 85 cmd := exec.Command(name, args...) 86 cmd.Stdout = os.Stdout 87 cmd.Stderr = os.Stderr 88 return cmd.Run() 89 }