diff --git a/builder/compiler-builtin.go b/builder/compiler-builtin.go deleted file mode 100644 index 8fd37c90..00000000 --- a/builder/compiler-builtin.go +++ /dev/null @@ -1,60 +0,0 @@ -// +build byollvm - -package builder - -import ( - "errors" - "os" - "os/exec" - "unsafe" - - "github.com/tinygo-org/tinygo/goenv" -) - -/* -#cgo CXXFLAGS: -fno-rtti -#include -#include -bool tinygo_clang_driver(int argc, char **argv); -*/ -import "C" - -// runCCompiler invokes a C compiler with the given arguments. -// -// This version invokes the built-in Clang when trying to run the Clang compiler. -func runCCompiler(command string, flags ...string) error { - switch command { - case "clang": - // Compile this with the internal Clang compiler. - headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) - if headerPath == "" { - return errors.New("could not locate Clang headers") - } - flags = append(flags, "-I"+headerPath) - flags = append([]string{"tinygo:" + command}, flags...) - var cflag *C.char - buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag))) - cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)] - for i, flag := range flags { - cflag := C.CString(flag) - cflags[i] = cflag - defer C.free(unsafe.Pointer(cflag)) - } - ok := C.tinygo_clang_driver(C.int(len(flags)), (**C.char)(buf)) - if !ok { - return errors.New("failed to compile using built-in clang") - } - return nil - default: - // Running some other compiler. Maybe it has been defined in the - // commands map (unlikely). - if cmdNames, ok := commands[command]; ok { - return execCommand(cmdNames, flags...) - } - // Alternatively, run the compiler directly. - cmd := exec.Command(command, flags...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() - } -} diff --git a/builder/compiler-external.go b/builder/compiler-external.go deleted file mode 100644 index b8bc688a..00000000 --- a/builder/compiler-external.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build !byollvm - -package builder - -// This file provides a way to a C compiler as an external command. See also: -// clang-external.go - -import ( - "os" - "os/exec" -) - -// runCCompiler invokes a C compiler with the given arguments. -// -// This version always runs the compiler as an external command. -func runCCompiler(command string, flags ...string) error { - if cmdNames, ok := commands[command]; ok { - return execCommand(cmdNames, flags...) - } - cmd := exec.Command(command, flags...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} diff --git a/builder/linker-builtin.go b/builder/linker-builtin.go deleted file mode 100644 index ace0d2eb..00000000 --- a/builder/linker-builtin.go +++ /dev/null @@ -1,71 +0,0 @@ -// +build byollvm - -package builder - -// This file provides a Link() function that uses the bundled lld if possible. - -import ( - "errors" - "os" - "os/exec" - "unsafe" - - "github.com/tinygo-org/tinygo/goenv" -) - -/* -#include -#include -bool tinygo_link_elf(int argc, char **argv); -bool tinygo_link_wasm(int argc, char **argv); -*/ -import "C" - -// link invokes a linker with the given name and flags. -// -// This version uses the built-in linker when trying to use lld. -func link(linker string, flags ...string) error { - switch linker { - 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))) - cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)] - for i, flag := range flags { - cflag := C.CString(flag) - cflags[i] = cflag - defer C.free(unsafe.Pointer(cflag)) - } - ok := C.tinygo_link_elf(C.int(len(flags)), (**C.char)(buf)) - if !ok { - return errors.New("failed to link using built-in ld.lld") - } - return nil - 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))) - defer C.free(buf) - cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)] - for i, flag := range flags { - cflag := C.CString(flag) - cflags[i] = cflag - defer C.free(unsafe.Pointer(cflag)) - } - ok := C.tinygo_link_wasm(C.int(len(flags)), (**C.char)(buf)) - if !ok { - return errors.New("failed to link using built-in wasm-ld") - } - 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 - cmd.Dir = goenv.Get("TINYGOROOT") - return cmd.Run() - } -} diff --git a/builder/linker-external.go b/builder/linker-external.go deleted file mode 100644 index 472645eb..00000000 --- a/builder/linker-external.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build !byollvm - -package builder - -// This file provides a Link() function that always runs an external command. It -// is provided for when tinygo is built without linking to liblld. - -import ( - "os" - "os/exec" - - "github.com/tinygo-org/tinygo/goenv" -) - -// link invokes a linker with the given name and arguments. -// -// 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 - cmd.Dir = goenv.Get("TINYGOROOT") - return cmd.Run() -} diff --git a/builder/tools-builtin.go b/builder/tools-builtin.go new file mode 100644 index 00000000..61e7a325 --- /dev/null +++ b/builder/tools-builtin.go @@ -0,0 +1,54 @@ +// +build byollvm + +package builder + +import ( + "errors" + "unsafe" +) + +/* +#cgo CXXFLAGS: -fno-rtti +#include +#include +bool tinygo_clang_driver(int argc, char **argv); +bool tinygo_link_elf(int argc, char **argv); +bool tinygo_link_wasm(int argc, char **argv); +*/ +import "C" + +const hasBuiltinTools = true + +// RunTool runs the given tool (such as clang). +// +// This version actually runs the tools because TinyGo was compiled while +// linking statically with LLVM (with the byollvm build tag). +func RunTool(tool string, args ...string) error { + args = append([]string{"tinygo:" + tool}, args...) + + var cflag *C.char + buf := C.calloc(C.size_t(len(args)), C.size_t(unsafe.Sizeof(cflag))) + defer C.free(buf) + cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(args):len(args)] + for i, flag := range args { + cflag := C.CString(flag) + cflags[i] = cflag + defer C.free(unsafe.Pointer(cflag)) + } + + var ok C.bool + switch tool { + case "clang": + ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf)) + case "ld.lld": + ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf)) + case "wasm-ld": + ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf)) + default: + return errors.New("unknown tool: " + tool) + } + if !ok { + return errors.New("failed to run tool: " + tool) + } + return nil +} diff --git a/builder/tools-external.go b/builder/tools-external.go new file mode 100644 index 00000000..4955bd2c --- /dev/null +++ b/builder/tools-external.go @@ -0,0 +1,15 @@ +// +build !byollvm + +package builder + +import "errors" + +const hasBuiltinTools = false + +// RunTool runs the given tool (such as clang). +// +// This version doesn't actually run the tool: TinyGo has not been compiled by +// statically linking to LLVM. +func RunTool(tool string, args ...string) error { + return errors.New("cannot run tool: " + tool) +} diff --git a/builder/tools.go b/builder/tools.go new file mode 100644 index 00000000..bc0f9782 --- /dev/null +++ b/builder/tools.go @@ -0,0 +1,59 @@ +package builder + +import ( + "errors" + "os" + "os/exec" + + "github.com/tinygo-org/tinygo/goenv" +) + +// runCCompiler invokes a C compiler with the given arguments. +func runCCompiler(command string, flags ...string) error { + if hasBuiltinTools && command == "clang" { + // Compile this with the internal Clang compiler. + headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT")) + if headerPath == "" { + return errors.New("could not locate Clang headers") + } + flags = append(flags, "-I"+headerPath) + cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() + } + + // Running some other compiler. Maybe it has been defined in the + // commands map (unlikely). + if cmdNames, ok := commands[command]; ok { + return execCommand(cmdNames, flags...) + } + + // Alternatively, run the compiler directly. + cmd := exec.Command(command, flags...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +// link invokes a linker with the given name and flags. +func link(linker string, flags ...string) error { + if hasBuiltinTools && (linker == "ld.lld" || linker == "wasm-ld") { + // Run command with internal linker. + cmd := exec.Command(os.Args[0], append([]string{linker}, flags...)...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() + } + + // 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 + cmd.Dir = goenv.Get("TINYGOROOT") + return cmd.Run() +} diff --git a/main.go b/main.go index f91f62b9..bf171758 100644 --- a/main.go +++ b/main.go @@ -726,6 +726,18 @@ func main() { } command := os.Args[1] + // Early command processing, before commands are interpreted by the Go flag + // library. + switch command { + case "clang", "ld.lld", "wasm-ld": + err := builder.RunTool(command, os.Args[2:]...) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) + } + flag.CommandLine.Parse(os.Args[2:]) options := &compileopts.Options{ Target: *target, diff --git a/main_test.go b/main_test.go index 3031f6b5..c8f46915 100644 --- a/main_test.go +++ b/main_test.go @@ -6,6 +6,7 @@ package main import ( "bufio" "bytes" + "fmt" "io/ioutil" "os" "os/exec" @@ -16,6 +17,7 @@ import ( "testing" "time" + "github.com/tinygo-org/tinygo/builder" "github.com/tinygo-org/tinygo/compileopts" ) @@ -218,3 +220,24 @@ func runTest(path, target string, t *testing.T) { t.Fail() } } + +// This TestMain is necessary because TinyGo may also be invoked to run certain +// LLVM tools in a separate process. Not capturing these invocations would lead +// to recursive tests. +func TestMain(m *testing.M) { + if len(os.Args) >= 2 { + switch os.Args[1] { + case "clang", "ld.lld", "wasm-ld": + // Invoke a specific tool. + err := builder.RunTool(os.Args[1], os.Args[2:]...) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) + } + } + + // Run normal tests. + os.Exit(m.Run()) +}