builder: run tools (clang, ...) as separate processes
This is necessary because LLVM defines many options in global variables that are modified when invoking Clang. In particular, LLVM 10 seems to have a bug in which it always sets the -pgo-warn-misexpect flag. Setting it multiple times (over various cc1 invocations) results in an error: clang (LLVM option parsing): for the --pgo-warn-misexpect option: may only occur zero or one times! This is fixed by running the Clang invocation in a new `tinygo` invocation. Because we've had issues with lld in the past, also run lld in a separate process so similar issues won't happen with lld in the future.
Этот коммит содержится в:
родитель
407149e323
коммит
f06d7d1bd6
9 изменённых файлов: 163 добавлений и 182 удалений
|
@ -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 <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
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()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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 <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
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()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
54
builder/tools-builtin.go
Обычный файл
54
builder/tools-builtin.go
Обычный файл
|
@ -0,0 +1,54 @@
|
|||
// +build byollvm
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CXXFLAGS: -fno-rtti
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
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
|
||||
}
|
15
builder/tools-external.go
Обычный файл
15
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)
|
||||
}
|
59
builder/tools.go
Обычный файл
59
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()
|
||||
}
|
12
main.go
12
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,
|
||||
|
|
23
main_test.go
23
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())
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче