package builder import ( "errors" "fmt" "os" "os/exec" "runtime" "strings" "tinygo.org/x/go-llvm" ) // Commands lists command alternatives for various operating systems. These // commands may have a slightly different name across operating systems and // distributions or may not even exist in $PATH, in which case absolute paths // may be used. var commands = map[string][]string{} func init() { llvmMajor := strings.Split(llvm.Version, ".")[0] commands["clang"] = []string{"clang-" + llvmMajor} commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"} commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"} commands["lldb"] = []string{"lldb-" + llvmMajor, "lldb"} // Add the path to a Homebrew-installed LLVM for ease of use (no need to // manually set $PATH). if runtime.GOOS == "darwin" { var prefix string switch runtime.GOARCH { case "amd64": prefix = "/usr/local/opt/llvm@" + llvmMajor + "/bin/" case "arm64": prefix = "/opt/homebrew/opt/llvm@" + llvmMajor + "/bin/" default: // unknown GOARCH panic(fmt.Sprintf("unknown GOARCH: %s on darwin", runtime.GOARCH)) } commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor) commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld") commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld") commands["lldb"] = append(commands["lldb"], prefix+"lldb") } // Add the path for when LLVM was installed with the installer from // llvm.org, which by default doesn't add LLVM to the $PATH environment // variable. if runtime.GOOS == "windows" { commands["clang"] = append(commands["clang"], "clang", "C:\\Program Files\\LLVM\\bin\\clang.exe") commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe") commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe") commands["lldb"] = append(commands["lldb"], "C:\\Program Files\\LLVM\\bin\\lldb.exe") } // Add the path to LLVM installed from ports. if runtime.GOOS == "freebsd" { prefix := "/usr/local/llvm" + llvmMajor + "/bin/" commands["clang"] = append(commands["clang"], prefix+"clang-"+llvmMajor) commands["ld.lld"] = append(commands["ld.lld"], prefix+"ld.lld") commands["wasm-ld"] = append(commands["wasm-ld"], prefix+"wasm-ld") commands["lldb"] = append(commands["lldb"], prefix+"lldb") } } // LookupCommand looks up the executable name for a given LLVM tool such as // clang or wasm-ld. It returns the (relative) command that can be used to // invoke the tool or an error if it could not be found. func LookupCommand(name string) (string, error) { for _, cmdName := range commands[name] { _, err := exec.LookPath(cmdName) if err != nil { if errors.Unwrap(err) == exec.ErrNotFound { continue } return cmdName, err } return cmdName, nil } return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " ")) } func execCommand(name string, args ...string) error { name, err := LookupCommand(name) if err != nil { return err } cmd := exec.Command(name, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }