From 3b0ed63c295656c041cbf4e7c8bbc57e06729573 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 31 Oct 2019 17:39:22 +0100 Subject: [PATCH] all: refactor compile options Move most of the logic of determining which compiler configuration to use (such as GOOS/GOARCH, build tags, whether to include debug symbols, panic strategy, etc.) into the compileopts package. This makes it a single source of truth for anything related to compiler configuration. It has a few advantages: * The compile configuration is independent of the compiler package. This makes it possible to move optimization passes out of the compiler, as they don't rely on compiler.Config anymore. * There is only one place to look if an incorrect compile option is used. * The compileopts provides some resistance against unintentionally picking the wrong option, such as with c.selectGC() vs c.GC() in the compiler. * It is now a lot easier to change compile options, as most options are getters now. --- compileopts/config.go | 135 ++++++++++++++++++++++++++++----- compiler/compiler.go | 96 +++++++++-------------- compiler/func.go | 2 +- compiler/gc.go | 4 +- compiler/goroutine-lowering.go | 4 +- compiler/goroutine.go | 2 +- compiler/interface.go | 2 +- compiler/optimizer.go | 6 +- compiler/syscall.go | 16 ++-- main.go | 60 ++++----------- 10 files changed, 181 insertions(+), 146 deletions(-) diff --git a/compileopts/config.go b/compileopts/config.go index b861034a..4f55945c 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -2,27 +2,124 @@ // binary. package compileopts +import ( + "fmt" + "strings" + + "github.com/tinygo-org/tinygo/goenv" +) + // Config keeps all configuration affecting the build in a single struct. type Config struct { - Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default) - CPU string // LLVM CPU name, e.g. atmega328p (empty string means default) - Features []string // LLVM CPU features - GOOS string // - GOARCH string // - GC string // garbage collection strategy - Scheduler string // scheduler implementation ("coroutines" or "tasks") - PanicStrategy string // panic strategy ("print" or "trap") - CFlags []string // cflags to pass to cgo - LDFlags []string // ldflags to pass to cgo - ClangHeaders string // Clang built-in header include path - DumpSSA bool // dump Go SSA, for compiler debugging - VerifyIR bool // run extra checks on the IR - Debug bool // add debug symbols for gdb - GOROOT string // GOROOT - TINYGOROOT string // GOROOT for TinyGo - GOPATH string // GOPATH, like `go env GOPATH` - BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH}) - TestConfig TestConfig + Options *Options + Target *TargetSpec + GoMinorVersion int + ClangHeaders string // Clang built-in header include path + TestConfig TestConfig +} + +// Triple returns the LLVM target triple, like armv6m-none-eabi. +func (c *Config) Triple() string { + return c.Target.Triple +} + +// CPU returns the LLVM CPU name, like atmega328p or arm7tdmi. It may return an +// empty string if the CPU name is not known. +func (c *Config) CPU() string { + return c.Target.CPU +} + +// Features returns a list of features this CPU supports. For example, for a +// RISC-V processor, that could be ["+a", "+c", "+m"]. For many targets, an +// empty list will be returned. +func (c *Config) Features() []string { + return c.Target.Features +} + +// GOOS returns the GOOS of the target. This might not always be the actual OS: +// for example, bare-metal targets will usually pretend to be linux to get the +// standard library to compile. +func (c *Config) GOOS() string { + return c.Target.GOOS +} + +// GOARCH returns the GOARCH of the target. This might not always be the actual +// archtecture: for example, the AVR target is not supported by the Go standard +// library so such targets will usually pretend to be linux/arm. +func (c *Config) GOARCH() string { + return c.Target.GOARCH +} + +// BuildTags returns the complete list of build tags used during this build. +func (c *Config) BuildTags() []string { + tags := append(c.Target.BuildTags, []string{"tinygo", "gc." + c.GC(), "scheduler." + c.Scheduler()}...) + for i := 1; i <= c.GoMinorVersion; i++ { + tags = append(tags, fmt.Sprintf("go1.%d", i)) + } + if extraTags := strings.Fields(c.Options.Tags); len(extraTags) != 0 { + tags = append(tags, extraTags...) + } + return tags +} + +// GC returns the garbage collection strategy in use on this platform. Valid +// values are "none", "leaking", and "conservative". +func (c *Config) GC() string { + if c.Options.GC != "" { + return c.Options.GC + } + if c.Target.GC != "" { + return c.Target.GC + } + return "conservative" +} + +// Scheduler returns the scheduler implementation. Valid values are "coroutines" +// and "tasks". +func (c *Config) Scheduler() string { + if c.Options.Scheduler != "" { + return c.Options.Scheduler + } + if c.Target.Scheduler != "" { + return c.Target.Scheduler + } + // Fall back to coroutines, which are supported everywhere. + return "coroutines" +} + +// PanicStrategy returns the panic strategy selected for this target. Valid +// values are "print" (print the panic value, then exit) or "trap" (issue a trap +// instruction). +func (c *Config) PanicStrategy() string { + return c.Options.PanicStrategy +} + +// CFlags returns the flags to pass to the C compiler. This is necessary for CGo +// preprocessing. +func (c *Config) CFlags() []string { + cflags := append([]string{}, c.Options.CFlags...) + for _, flag := range c.Target.CFlags { + cflags = append(cflags, strings.Replace(flag, "{root}", goenv.Get("TINYGOROOT"), -1)) + } + return cflags +} + +// DumpSSA returns whether to dump Go SSA while compiling (-dumpssa flag). Only +// enable this for debugging. +func (c *Config) DumpSSA() bool { + return c.Options.DumpSSA +} + +// VerifyIR returns whether to run extra checks on the IR. This is normally +// disabled but enabled during testing. +func (c *Config) VerifyIR() bool { + return c.Options.VerifyIR +} + +// Debug returns whether to add debug symbols to the IR, for debugging with GDB +// and similar. +func (c *Config) Debug() bool { + return c.Options.Debug } type TestConfig struct { diff --git a/compiler/compiler.go b/compiler/compiler.go index 427bffe7..a6effa4a 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -15,6 +15,7 @@ import ( "strings" "github.com/tinygo-org/tinygo/compileopts" + "github.com/tinygo-org/tinygo/goenv" "github.com/tinygo-org/tinygo/ir" "github.com/tinygo-org/tinygo/loader" "golang.org/x/tools/go/ssa" @@ -103,35 +104,26 @@ type Phi struct { } func NewCompiler(pkgName string, config *compileopts.Config) (*Compiler, error) { - if config.Triple == "" { - config.Triple = llvm.DefaultTargetTriple() - } - if len(config.BuildTags) == 0 { - config.BuildTags = []string{config.GOOS, config.GOARCH} - } c := &Compiler{ Config: config, difiles: make(map[string]llvm.Metadata), ditypes: make(map[types.Type]llvm.Metadata), } - target, err := llvm.GetTargetFromTriple(config.Triple) + target, err := llvm.GetTargetFromTriple(config.Triple()) if err != nil { return nil, err } - features := "" - if len(config.Features) > 0 { - features = strings.Join(config.Features, `,`) - } - c.machine = target.CreateTargetMachine(config.Triple, config.CPU, features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault) + features := strings.Join(config.Features(), ",") + c.machine = target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault) c.targetData = c.machine.CreateTargetData() c.ctx = llvm.NewContext() c.mod = c.ctx.NewModule(pkgName) - c.mod.SetTarget(config.Triple) + c.mod.SetTarget(config.Triple()) c.mod.SetDataLayout(c.targetData.String()) c.builder = c.ctx.NewBuilder() - if c.Debug { + if c.Debug() { c.dibuilder = llvm.NewDIBuilder(c.mod) } @@ -164,35 +156,16 @@ func (c *Compiler) Module() llvm.Module { return c.mod } -// selectGC picks an appropriate GC strategy if none was provided. -func (c *Compiler) selectGC() string { - if c.GC != "" { - return c.GC - } - return "conservative" -} - -// selectScheduler picks an appropriate scheduler for the target if none was -// given. -func (c *Compiler) selectScheduler() string { - if c.Scheduler != "" { - // A scheduler was specified in the target description. - return c.Scheduler - } - // Fall back to coroutines, which are supported everywhere. - return "coroutines" -} - // getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization. func (c *Compiler) getFunctionsUsedInTransforms() []string { fnused := functionsUsedInTransforms - switch c.selectScheduler() { + switch c.Scheduler() { case "coroutines": fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...) case "tasks": fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...) default: - panic(fmt.Errorf("invalid scheduler %q", c.selectScheduler())) + panic(fmt.Errorf("invalid scheduler %q", c.Scheduler())) } return fnused } @@ -202,38 +175,37 @@ func (c *Compiler) getFunctionsUsedInTransforms() []string { func (c *Compiler) Compile(mainPath string) []error { // Prefix the GOPATH with the system GOROOT, as GOROOT is already set to // the TinyGo root. - overlayGopath := c.GOPATH + overlayGopath := goenv.Get("GOPATH") if overlayGopath == "" { - overlayGopath = c.GOROOT + overlayGopath = goenv.Get("GOROOT") } else { - overlayGopath = c.GOROOT + string(filepath.ListSeparator) + overlayGopath + overlayGopath = goenv.Get("GOROOT") + string(filepath.ListSeparator) + overlayGopath } wd, err := os.Getwd() if err != nil { return []error{err} } - buildTags := append([]string{"tinygo", "gc." + c.selectGC(), "scheduler." + c.selectScheduler()}, c.BuildTags...) lprogram := &loader.Program{ Build: &build.Context{ - GOARCH: c.GOARCH, - GOOS: c.GOOS, - GOROOT: c.GOROOT, - GOPATH: c.GOPATH, + GOARCH: c.GOARCH(), + GOOS: c.GOOS(), + GOROOT: goenv.Get("GOROOT"), + GOPATH: goenv.Get("GOPATH"), CgoEnabled: true, UseAllFiles: false, Compiler: "gc", // must be one of the recognized compilers - BuildTags: buildTags, + BuildTags: c.BuildTags(), }, OverlayBuild: &build.Context{ - GOARCH: c.GOARCH, - GOOS: c.GOOS, - GOROOT: c.TINYGOROOT, + GOARCH: c.GOARCH(), + GOOS: c.GOOS(), + GOROOT: goenv.Get("TINYGOROOT"), GOPATH: overlayGopath, CgoEnabled: true, UseAllFiles: false, Compiler: "gc", // must be one of the recognized compilers - BuildTags: buildTags, + BuildTags: c.BuildTags(), }, OverlayPath: func(path string) string { // Return the (overlay) import path when it should be overlaid, and @@ -250,7 +222,7 @@ func (c *Compiler) Compile(mainPath string) []error { if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") { return path } else if path == "syscall" { - for _, tag := range c.BuildTags { + for _, tag := range c.BuildTags() { if tag == "baremetal" || tag == "darwin" { return path } @@ -267,8 +239,8 @@ func (c *Compiler) Compile(mainPath string) []error { }, }, Dir: wd, - TINYGOROOT: c.TINYGOROOT, - CFlags: c.CFlags, + TINYGOROOT: goenv.Get("TINYGOROOT"), + CFlags: c.CFlags(), ClangHeaders: c.ClangHeaders, } @@ -300,7 +272,7 @@ func (c *Compiler) Compile(mainPath string) []error { c.ir.SimpleDCE() // Initialize debug information. - if c.Debug { + if c.Debug() { c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{ Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?) File: mainPath, @@ -344,7 +316,7 @@ func (c *Compiler) Compile(mainPath string) []error { initFn := c.ir.GetFunction(c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)) initFn.LLVMFn.SetLinkage(llvm.InternalLinkage) initFn.LLVMFn.SetUnnamedAddr(true) - if c.Debug { + if c.Debug() { difunc := c.attachDebugInfo(initFn) pos := c.ir.Program.Fset.Position(initFn.Pos()) c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) @@ -408,7 +380,7 @@ func (c *Compiler) Compile(mainPath string) []error { } // see: https://reviews.llvm.org/D18355 - if c.Debug { + if c.Debug() { c.mod.AddNamedMetadataOperand("llvm.module.flags", c.ctx.MDNode([]llvm.Metadata{ llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch @@ -856,7 +828,7 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, } func (c *Compiler) parseFunc(frame *Frame) { - if c.DumpSSA { + if c.DumpSSA() { fmt.Printf("\nfunc %s:\n", frame.fn.Function) } if !frame.fn.LLVMFn.IsDeclaration() { @@ -867,7 +839,7 @@ func (c *Compiler) parseFunc(frame *Frame) { frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage) frame.fn.LLVMFn.SetUnnamedAddr(true) } - if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple, "avr") { + if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple(), "avr") { frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL } @@ -884,7 +856,7 @@ func (c *Compiler) parseFunc(frame *Frame) { } // Add debug info, if needed. - if c.Debug { + if c.Debug() { if frame.fn.Synthetic == "package initializer" { // Package initializers have no debug info. Create some fake debug // info to at least have *something*. @@ -918,7 +890,7 @@ func (c *Compiler) parseFunc(frame *Frame) { frame.locals[param] = c.collapseFormalParam(llvmType, fields) // Add debug information to this parameter (if available) - if c.Debug && frame.fn.Syntax() != nil { + if c.Debug() && frame.fn.Syntax() != nil { pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos()) diType := c.getDIType(param.Type()) dbgParam := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{ @@ -980,7 +952,7 @@ func (c *Compiler) parseFunc(frame *Frame) { // Fill blocks with instructions. for _, block := range frame.fn.DomPreorder() { - if c.DumpSSA { + if c.DumpSSA() { fmt.Printf("%d: %s:\n", block.Index, block.Comment) } c.builder.SetInsertPointAtEnd(frame.blockEntries[block]) @@ -989,7 +961,7 @@ func (c *Compiler) parseFunc(frame *Frame) { if _, ok := instr.(*ssa.DebugRef); ok { continue } - if c.DumpSSA { + if c.DumpSSA() { if val, ok := instr.(ssa.Value); ok && val.Name() != "" { fmt.Printf("\t%s = %s\n", val.Name(), val.String()) } else { @@ -1015,7 +987,7 @@ func (c *Compiler) parseFunc(frame *Frame) { } func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { - if c.Debug { + if c.Debug() { pos := c.ir.Program.Fset.Position(instr.Pos()) c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{}) } @@ -1076,7 +1048,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { // * The function pointer (for tasks). funcPtr, context := c.decodeFuncValue(c.getValue(frame, instr.Call.Value), instr.Call.Value.Type().(*types.Signature)) params = append(params, context) // context parameter - switch c.selectScheduler() { + switch c.Scheduler() { case "coroutines": // There are no additional parameters needed for the goroutine start operation. case "tasks": diff --git a/compiler/func.go b/compiler/func.go index df364b13..14f1416b 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -34,7 +34,7 @@ const ( func (c *Compiler) funcImplementation() funcValueImplementation { // Always pick the switch implementation, as it allows the use of blocking // inside a function that is used as a func value. - switch c.selectScheduler() { + switch c.Scheduler() { case "coroutines": return funcValueSwitch case "tasks": diff --git a/compiler/gc.go b/compiler/gc.go index 763902ff..85ec2d52 100644 --- a/compiler/gc.go +++ b/compiler/gc.go @@ -14,10 +14,10 @@ import ( // needsStackObjects returns true if the compiler should insert stack objects // that can be traced by the garbage collector. func (c *Compiler) needsStackObjects() bool { - if c.selectGC() != "conservative" { + if c.GC() != "conservative" { return false } - for _, tag := range c.BuildTags { + for _, tag := range c.BuildTags() { if tag == "baremetal" { return false } diff --git a/compiler/goroutine-lowering.go b/compiler/goroutine-lowering.go index 5641a6ed..b34ac8a6 100644 --- a/compiler/goroutine-lowering.go +++ b/compiler/goroutine-lowering.go @@ -126,7 +126,7 @@ type asyncFunc struct { // coroutine or the tasks implementation of goroutines, and whether goroutines // are necessary at all. func (c *Compiler) LowerGoroutines() error { - switch c.selectScheduler() { + switch c.Scheduler() { case "coroutines": return c.lowerCoroutines() case "tasks": @@ -312,7 +312,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) { // Check whether a scheduler is needed. makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine") - if strings.HasPrefix(c.Triple, "avr") { + if strings.HasPrefix(c.Triple(), "avr") { needsScheduler = false getCoroutine := c.mod.NamedFunction("runtime.getCoroutine") for _, inst := range getUses(getCoroutine) { diff --git a/compiler/goroutine.go b/compiler/goroutine.go index 23979357..972aaed4 100644 --- a/compiler/goroutine.go +++ b/compiler/goroutine.go @@ -13,7 +13,7 @@ import "tinygo.org/x/go-llvm" // // Because a go statement doesn't return anything, return undef. func (c *Compiler) emitStartGoroutine(funcPtr llvm.Value, params []llvm.Value) llvm.Value { - switch c.selectScheduler() { + switch c.Scheduler() { case "tasks": paramBundle := c.emitPointerPack(params) paramBundle = c.builder.CreatePtrToInt(paramBundle, c.uintptrType, "") diff --git a/compiler/interface.go b/compiler/interface.go index c7a257ef..3eef9394 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -483,7 +483,7 @@ func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) { wrapper.SetUnnamedAddr(true) // add debug info if needed - if c.Debug { + if c.Debug() { pos := c.ir.Program.Fset.Position(fn.Pos()) difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line) c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) diff --git a/compiler/optimizer.go b/compiler/optimizer.go index 480d3283..c7fd0ba3 100644 --- a/compiler/optimizer.go +++ b/compiler/optimizer.go @@ -19,12 +19,12 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro } builder.AddCoroutinePassesToExtensionPoints() - if c.PanicStrategy == "trap" { + if c.PanicStrategy() == "trap" { c.replacePanicsWithTrap() // -panic=trap } // run a check of all of our code - if c.VerifyIR { + if c.VerifyIR() { err := c.checkModule() if err != nil { return err @@ -96,7 +96,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro return err } } - if c.VerifyIR { + if c.VerifyIR() { if err := c.checkModule(); err != nil { return err } diff --git a/compiler/syscall.go b/compiler/syscall.go index 9b99fcb6..9799669d 100644 --- a/compiler/syscall.go +++ b/compiler/syscall.go @@ -16,8 +16,8 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, num := c.getValue(frame, call.Args[0]) var syscallResult llvm.Value switch { - case c.GOARCH == "amd64": - if c.GOOS == "darwin" { + case c.GOARCH() == "amd64": + if c.GOOS() == "darwin" { // Darwin adds this magic number to system call numbers: // // > Syscall classes for 64-bit system call entry. @@ -58,7 +58,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, fnType := llvm.FunctionType(c.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH == "386" && c.GOOS == "linux": + case c.GOARCH() == "386" && c.GOOS() == "linux": // Sources: // syscall(2) man page // https://stackoverflow.com/a/2538212 @@ -84,7 +84,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, fnType := llvm.FunctionType(c.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel) syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH == "arm" && c.GOOS == "linux": + case c.GOARCH() == "arm" && c.GOOS() == "linux": // Implement the EABI system call convention for Linux. // Source: syscall(2) man page. args := []llvm.Value{} @@ -116,7 +116,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, fnType := llvm.FunctionType(c.uintptrType, argTypes, false) target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) syscallResult = c.builder.CreateCall(target, args, "") - case c.GOARCH == "arm64" && c.GOOS == "linux": + case c.GOARCH() == "arm64" && c.GOOS() == "linux": // Source: syscall(2) man page. args := []llvm.Value{} argTypes := []llvm.Type{} @@ -149,9 +149,9 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0) syscallResult = c.builder.CreateCall(target, args, "") default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) + return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH()) } - switch c.GOOS { + switch c.GOOS() { case "linux": // Return values: r0, r1 uintptr, err Errno // Pseudocode: @@ -187,6 +187,6 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, retval = c.builder.CreateInsertValue(retval, errResult, 2, "") return retval, nil default: - return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) + return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH()) } } diff --git a/main.go b/main.go index adb4f253..2e5aae97 100644 --- a/main.go +++ b/main.go @@ -50,29 +50,13 @@ func (e *multiError) Error() string { // Helper function for Compiler object. func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *compileopts.Options, action func(string) error) error { - if options.GC == "" && spec.GC != "" { - options.GC = spec.GC - } root := goenv.Get("TINYGOROOT") - // Merge and adjust CFlags. - cflags := append([]string{}, options.CFlags...) - for _, flag := range spec.CFlags { - cflags = append(cflags, strings.Replace(flag, "{root}", root, -1)) - } - - // Merge and adjust LDFlags. - ldflags := append([]string{}, options.LDFlags...) - for _, flag := range spec.LDFlags { - ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1)) - } - goroot := goenv.Get("GOROOT") if goroot == "" { return errors.New("cannot locate $GOROOT, please set it manually") } - tags := spec.BuildTags major, minor, err := getGorootVersion(goroot) if err != nil { return fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err) @@ -80,36 +64,12 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com if major != 1 || (minor != 11 && minor != 12 && minor != 13) { return fmt.Errorf("requires go version 1.11, 1.12, or 1.13, got go%d.%d", major, minor) } - for i := 1; i <= minor; i++ { - tags = append(tags, fmt.Sprintf("go1.%d", i)) - } - if extraTags := strings.Fields(options.Tags); len(extraTags) != 0 { - tags = append(tags, extraTags...) - } - scheduler := spec.Scheduler - if options.Scheduler != "" { - scheduler = options.Scheduler - } compilerConfig := &compileopts.Config{ - Triple: spec.Triple, - CPU: spec.CPU, - Features: spec.Features, - GOOS: spec.GOOS, - GOARCH: spec.GOARCH, - GC: options.GC, - PanicStrategy: options.PanicStrategy, - Scheduler: scheduler, - CFlags: cflags, - LDFlags: ldflags, - ClangHeaders: getClangHeaderPath(root), - Debug: options.Debug, - DumpSSA: options.DumpSSA, - VerifyIR: options.VerifyIR, - TINYGOROOT: root, - GOROOT: goroot, - GOPATH: goenv.Get("GOPATH"), - BuildTags: tags, - TestConfig: options.TestConfig, + Options: options, + Target: spec, + GoMinorVersion: minor, + ClangHeaders: getClangHeaderPath(root), + TestConfig: options.TestConfig, } c, err := compiler.NewCompiler(pkgName, compilerConfig) if err != nil { @@ -227,6 +187,12 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com } } + // Merge and adjust LDFlags. + ldflags := append([]string{}, options.LDFlags...) + for _, flag := range spec.LDFlags { + ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1)) + } + // Prepare link command. executable := filepath.Join(dir, "main") tmppath := executable // final file @@ -249,7 +215,7 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com if names, ok := commands[spec.Compiler]; ok { cmdNames = names } - err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, abspath)...) + err := execCommand(cmdNames, append(compilerConfig.CFlags(), "-c", "-o", outpath, abspath)...) if err != nil { return &commandError{"failed to build", path, err} } @@ -265,7 +231,7 @@ func Compile(pkgName, outpath string, spec *compileopts.TargetSpec, options *com if names, ok := commands[spec.Compiler]; ok { cmdNames = names } - err := execCommand(cmdNames, append(cflags, "-c", "-o", outpath, path)...) + err := execCommand(cmdNames, append(compilerConfig.CFlags(), "-c", "-o", outpath, path)...) if err != nil { return &commandError{"failed to build", path, err} }