From 9cad8bd0c8bc2d950b603123f5b8d00107c461b1 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 3 May 2019 19:30:54 +0200 Subject: [PATCH] main: add fallback mechanism for LLVM commands On Debian, all LLVM commands have a version suffix (clang-8, ld.lld-8, wasm-ld-8, etc.). However. Most other distributions only provide a version prefix for Clang and not for all the other commands. This commit fixes the issue by trying the command with the version suffix first and falling back to one without if needed. --- builtins.go | 10 +++------- commands.go | 37 ++++++++++++++++++++++++++++++------- commands_macos.go | 10 ---------- linker-builtin.go | 7 +++++-- linker-external.go | 3 +++ main.go | 28 +++++++++------------------- target.go | 2 +- 7 files changed, 51 insertions(+), 46 deletions(-) delete mode 100644 commands_macos.go diff --git a/builtins.go b/builtins.go index 75b6592a..8ac127f7 100644 --- a/builtins.go +++ b/builtins.go @@ -5,7 +5,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path/filepath" "strings" "time" @@ -192,13 +191,13 @@ func loadBuiltins(target string) (path string, err error) { srcs[i] = filepath.Join(builtinsDir, name) } - if path, err := cacheLoad(outfile, commands["clang"], srcs); path != "" || err != nil { + if path, err := cacheLoad(outfile, commands["clang"][0], srcs); path != "" || err != nil { return path, err } var cachepath string err = compileBuiltins(target, func(path string) error { - path, err := cacheStore(path, outfile, commands["clang"], srcs) + path, err := cacheStore(path, outfile, commands["clang"][0], srcs) cachepath = path return err }) @@ -240,10 +239,7 @@ func compileBuiltins(target string, callback func(path string) error) error { // Note: -fdebug-prefix-map is necessary to make the output archive // reproducible. Otherwise the temporary directory is stored in the // archive itself, which varies each run. - cmd := exec.Command(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() + err := execCommand(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath) if err != nil { return &commandError{"failed to build", srcpath, err} } diff --git a/commands.go b/commands.go index a975b5b2..5f7ff8c1 100644 --- a/commands.go +++ b/commands.go @@ -1,10 +1,33 @@ -// +build !darwin - package main -// commands used by the compilation process might have different file names on Linux than those used on macOS. -var commands = map[string]string{ - "clang": "clang-8", - "ld.lld": "ld.lld-8", - "wasm-ld": "wasm-ld-8", +import ( + "errors" + "os" + "os/exec" + "strings" +) + +// Commands used by the compilation process might have different file names +// across operating systems and distributions. +var commands = map[string][]string{ + "clang": {"clang-8"}, + "ld.lld": {"ld.lld-8", "ld.lld"}, + "wasm-ld": {"wasm-ld-8", "wasm-ld"}, +} + +func execCommand(cmdNames []string, args ...string) error { + for _, cmdName := range cmdNames { + cmd := exec.Command(cmdName, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + if err, ok := err.(*exec.Error); ok && err.Err == exec.ErrNotFound { + // this command was not found, try the next + continue + } + } + return nil + } + return errors.New("none of these commands were found in your $PATH: " + strings.Join(cmdNames, " ")) } diff --git a/commands_macos.go b/commands_macos.go deleted file mode 100644 index 9ec428ec..00000000 --- a/commands_macos.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build darwin - -package main - -// commands used by the compilation process might have different file names on macOS than those used on Linux. -var commands = map[string]string{ - "clang": "clang-8", - "ld.lld": "ld.lld", - "wasm-ld": "wasm-ld", -} diff --git a/linker-builtin.go b/linker-builtin.go index c8987938..d0a01681 100644 --- a/linker-builtin.go +++ b/linker-builtin.go @@ -24,7 +24,7 @@ import "C" // This version uses the built-in linker when trying to use lld. func Link(linker string, flags ...string) error { switch linker { - case "ld.lld", commands["ld.lld"]: + case "ld.lld": flags = append([]string{"tinygo:" + linker}, flags...) var cflag *C.char buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag))) @@ -39,7 +39,7 @@ func Link(linker string, flags ...string) error { return errors.New("failed to link using built-in ld.lld") } return nil - case "wasm-ld", commands["wasm-ld"]: + case "wasm-ld": flags = append([]string{"tinygo:" + linker}, flags...) var cflag *C.char buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag))) @@ -57,6 +57,9 @@ func Link(linker string, flags ...string) error { return nil default: // Fall back to external command. + if cmdNames, ok := commands[linker]; ok { + return execCommand(cmdNames, flags...) + } cmd := exec.Command(linker, flags...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/linker-external.go b/linker-external.go index 446b6808..48e4d2ec 100644 --- a/linker-external.go +++ b/linker-external.go @@ -14,6 +14,9 @@ import ( // // This version always runs the linker as an external command. func Link(linker string, flags ...string) error { + if cmdNames, ok := commands[linker]; ok { + return execCommand(cmdNames, flags...) + } cmd := exec.Command(linker, flags...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/main.go b/main.go index a06825ee..b662d65c 100644 --- a/main.go +++ b/main.go @@ -222,14 +222,11 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act for i, path := range spec.ExtraFiles { abspath := filepath.Join(root, path) outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o") - cmdName := spec.Compiler - if name, ok := commands[cmdName]; ok { - cmdName = name + cmdNames := []string{spec.Compiler} + if names, ok := commands[spec.Compiler]; ok { + cmdNames = names } - cmd := exec.Command(cmdName, append(cflags, "-c", "-o", outpath, abspath)...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() + err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, abspath)...) if err != nil { return &commandError{"failed to build", path, err} } @@ -241,14 +238,11 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act for _, file := range pkg.CFiles { path := filepath.Join(pkg.Package.Dir, file) outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o") - cmdName := spec.Compiler - if name, ok := commands[cmdName]; ok { - cmdName = name + cmdNames := []string{spec.Compiler} + if names, ok := commands[spec.Compiler]; ok { + cmdNames = names } - cmd := exec.Command(cmdName, append(cflags, "-c", "-o", outpath, path)...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() + err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, path)...) if err != nil { return &commandError{"failed to build", path, err} } @@ -257,11 +251,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act } // Link the object files together. - if linker, ok := commands[spec.Linker]; ok { - err = Link(linker, ldflags...) - } else { - err = Link(spec.Linker, ldflags...) - } + err = Link(spec.Linker, ldflags...) if err != nil { return &commandError{"failed to link", executable, err} } diff --git a/target.go b/target.go index 16970215..546883f3 100644 --- a/target.go +++ b/target.go @@ -211,7 +211,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { GOOS: goos, GOARCH: goarch, BuildTags: []string{goos, goarch}, - Compiler: commands["clang"], + Compiler: "clang", Linker: "cc", GDB: "gdb", GDBCmds: []string{"run"},