diff --git a/builder/config.go b/builder/config.go index 82693f7f..fcfa5548 100644 --- a/builder/config.go +++ b/builder/config.go @@ -13,7 +13,7 @@ import ( // uses the currently active GOPATH (from the goenv package) to determine the Go // version to use. func NewConfig(options *compileopts.Options) (*compileopts.Config, error) { - spec, err := compileopts.LoadTarget(options.Target) + spec, err := compileopts.LoadTarget(options) if err != nil { return nil, err } diff --git a/compileopts/options.go b/compileopts/options.go index 7e7bfcaf..ec47a84f 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -16,8 +16,11 @@ var ( ) // Options contains extra options to give to the compiler. These options are -// usually passed from the command line. +// usually passed from the command line, but can also be passed in environment +// variables for example. type Options struct { + GOOS string // environment variable + GOARCH string // environment variable Target string Opt string GC string diff --git a/compileopts/target.go b/compileopts/target.go index 8d00d6ef..7a2bc15f 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -161,36 +161,34 @@ func (spec *TargetSpec) resolveInherits() error { } // Load a target specification. -func LoadTarget(target string) (*TargetSpec, error) { - if target == "" { +func LoadTarget(options *Options) (*TargetSpec, error) { + if options.Target == "" { // Configure based on GOOS/GOARCH environment variables (falling back to // runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it. - goos := goenv.Get("GOOS") - goarch := goenv.Get("GOARCH") - llvmos := goos + llvmos := options.GOOS llvmarch := map[string]string{ "386": "i386", "amd64": "x86_64", "arm64": "aarch64", "arm": "armv7", - }[goarch] + }[options.GOARCH] if llvmarch == "" { - llvmarch = goarch + llvmarch = options.GOARCH } // Target triples (which actually have four components, but are called // triples for historical reasons) have the form: // arch-vendor-os-environment - target = llvmarch + "-unknown-" + llvmos - if goarch == "arm" { + target := llvmarch + "-unknown-" + llvmos + if options.GOARCH == "arm" { target += "-gnueabihf" } - return defaultTarget(goos, goarch, target) + return defaultTarget(options.GOOS, options.GOARCH, target) } // See whether there is a target specification for this target (e.g. // Arduino). spec := &TargetSpec{} - err := spec.loadFromGivenStr(target) + err := spec.loadFromGivenStr(options.Target) if err == nil { // Successfully loaded this target from a built-in .json file. Make sure // it includes all parents as specified in the "inherits" key. @@ -206,7 +204,7 @@ func LoadTarget(target string) (*TargetSpec, error) { } else { // Load target from given triple, ignore GOOS/GOARCH environment // variables. - tripleSplit := strings.Split(target, "-") + tripleSplit := strings.Split(options.Target, "-") if len(tripleSplit) < 3 { return nil, errors.New("expected a full LLVM target or a custom target in -target flag") } diff --git a/compileopts/target_test.go b/compileopts/target_test.go index aa646238..b065258b 100644 --- a/compileopts/target_test.go +++ b/compileopts/target_test.go @@ -6,12 +6,12 @@ import ( ) func TestLoadTarget(t *testing.T) { - _, err := LoadTarget("arduino") + _, err := LoadTarget(&Options{Target: "arduino"}) if err != nil { t.Error("LoadTarget test failed:", err) } - _, err = LoadTarget("notexist") + _, err = LoadTarget(&Options{Target: "notexist"}) if err == nil { t.Error("LoadTarget should have failed with non existing target") } diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index a4ac0f7b..c80b822c 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -73,12 +73,15 @@ func TestCompiler(t *testing.T) { } t.Run(name, func(t *testing.T) { - target, err := compileopts.LoadTarget(targetString) + options := &compileopts.Options{ + Target: targetString, + } + target, err := compileopts.LoadTarget(options) if err != nil { t.Fatal("failed to load target:", err) } config := &compileopts.Config{ - Options: &compileopts.Options{}, + Options: options, Target: target, } compilerConfig := &Config{ diff --git a/main.go b/main.go index fdba44f6..64311424 100644 --- a/main.go +++ b/main.go @@ -1147,6 +1147,8 @@ func main() { } options := &compileopts.Options{ + GOOS: goenv.Get("GOOS"), + GOARCH: goenv.Get("GOARCH"), Target: *target, Opt: *opt, GC: *gc, @@ -1309,7 +1311,7 @@ func main() { continue } path := filepath.Join(dir, entry.Name()) - spec, err := compileopts.LoadTarget(path) + spec, err := compileopts.LoadTarget(&compileopts.Options{Target: path}) if err != nil { fmt.Fprintln(os.Stderr, "could not list target:", err) os.Exit(1) diff --git a/main_test.go b/main_test.go index d27bb745..d62cab2f 100644 --- a/main_test.go +++ b/main_test.go @@ -67,13 +67,13 @@ func TestCompiler(t *testing.T) { // This makes it possible to run one specific test (instead of all), // which is especially useful to quickly check whether some changes // affect a particular target architecture. - runPlatTests(*testTarget, tests, t) + runPlatTests(optionsFromTarget(*testTarget), tests, t) return } if runtime.GOOS != "windows" { t.Run("Host", func(t *testing.T) { - runPlatTests("", tests, t) + runPlatTests(optionsFromTarget(""), tests, t) }) } @@ -82,32 +82,32 @@ func TestCompiler(t *testing.T) { } t.Run("EmulatedCortexM3", func(t *testing.T) { - runPlatTests("cortex-m-qemu", tests, t) + runPlatTests(optionsFromTarget("cortex-m-qemu"), tests, t) }) if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { // Note: running only on Windows and macOS because Linux (as of 2020) // usually has an outdated QEMU version that doesn't support RISC-V yet. t.Run("EmulatedRISCV", func(t *testing.T) { - runPlatTests("riscv-qemu", tests, t) + runPlatTests(optionsFromTarget("riscv-qemu"), tests, t) }) } if runtime.GOOS == "linux" { t.Run("X86Linux", func(t *testing.T) { - runPlatTests("i386-unknown-linux", tests, t) + runPlatTests(optionsFromOSARCH("linux", "386"), tests, t) }) t.Run("ARMLinux", func(t *testing.T) { - runPlatTests("armv7-unknown-linux-gnueabihf", tests, t) + runPlatTests(optionsFromOSARCH("linux", "arm"), tests, t) }) t.Run("ARM64Linux", func(t *testing.T) { - runPlatTests("aarch64-unknown-linux", tests, t) + runPlatTests(optionsFromOSARCH("linux", "arm64"), tests, t) }) t.Run("WebAssembly", func(t *testing.T) { - runPlatTests("wasm", tests, t) + runPlatTests(optionsFromTarget("wasm"), tests, t) }) t.Run("WASI", func(t *testing.T) { - runPlatTests("wasi", tests, t) + runPlatTests(optionsFromTarget("wasi"), tests, t) }) } @@ -122,8 +122,10 @@ func TestCompiler(t *testing.T) { // Test with few optimizations enabled (no inlining, etc). t.Run("opt=1", func(t *testing.T) { t.Parallel() - runTestWithConfig("stdlib.go", "", t, compileopts.Options{ - Opt: "1", + runTestWithConfig("stdlib.go", t, compileopts.Options{ + GOOS: goenv.Get("GOOS"), + GOARCH: goenv.Get("GOARCH"), + Opt: "1", }, nil, nil) }) @@ -131,14 +133,18 @@ func TestCompiler(t *testing.T) { // TODO: fix this for stdlib.go, which currently fails. t.Run("opt=0", func(t *testing.T) { t.Parallel() - runTestWithConfig("print.go", "", t, compileopts.Options{ - Opt: "0", + runTestWithConfig("print.go", t, compileopts.Options{ + GOOS: goenv.Get("GOOS"), + GOARCH: goenv.Get("GOARCH"), + Opt: "0", }, nil, nil) }) t.Run("ldflags", func(t *testing.T) { t.Parallel() - runTestWithConfig("ldflags.go", "", t, compileopts.Options{ + runTestWithConfig("ldflags.go", t, compileopts.Options{ + GOOS: goenv.Get("GOOS"), + GOARCH: goenv.Get("GOARCH"), GlobalValues: map[string]map[string]string{ "main": { "someGlobal": "foobar", @@ -149,30 +155,30 @@ func TestCompiler(t *testing.T) { }) } -func runPlatTests(target string, tests []string, t *testing.T) { +func runPlatTests(options compileopts.Options, tests []string, t *testing.T) { t.Parallel() for _, name := range tests { name := name // redefine to avoid race condition t.Run(name, func(t *testing.T) { t.Parallel() - runTest(name, target, t, nil, nil) + runTest(name, options, t, nil, nil) }) } t.Run("env.go", func(t *testing.T) { t.Parallel() - runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"}) + runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"}) }) - if target == "" || target == "wasi" { + if options.Target == "" || options.Target == "wasi" { t.Run("filesystem.go", func(t *testing.T) { t.Parallel() - runTest("filesystem.go", target, t, nil, nil) + runTest("filesystem.go", options, t, nil, nil) }) } - if target == "" || target == "wasi" || target == "wasm" { + if options.Target == "" || options.Target == "wasi" || options.Target == "wasm" { t.Run("rand.go", func(t *testing.T) { t.Parallel() - runTest("rand.go", target, t, nil, nil) + runTest("rand.go", options, t, nil, nil) }) } } @@ -189,14 +195,27 @@ func runBuild(src, out string, opts *compileopts.Options) error { return Build(src, out, opts) } -func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) { - options := compileopts.Options{ +func optionsFromTarget(target string) compileopts.Options { + return compileopts.Options{ + // GOOS/GOARCH are only used if target == "" + GOOS: goenv.Get("GOOS"), + GOARCH: goenv.Get("GOARCH"), Target: target, } - runTestWithConfig(name, target, t, options, cmdArgs, environmentVars) } -func runTestWithConfig(name, target string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) { +func optionsFromOSARCH(goos, goarch string) compileopts.Options { + return compileopts.Options{ + GOOS: goos, + GOARCH: goarch, + } +} + +func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, environmentVars []string) { + runTestWithConfig(name, t, options, cmdArgs, environmentVars) +} + +func runTestWithConfig(name string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) { // Set default config. options.Debug = true options.VerifyIR = true @@ -227,7 +246,7 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op // we need to pass command line arguments and environment variables through // global variables (built into the binary directly) instead of the // conventional way. - spec, err := compileopts.LoadTarget(target) + spec, err := compileopts.LoadTarget(&options) if err != nil { t.Fatal("failed to load target spec:", err) } @@ -314,7 +333,7 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op } }() err = cmd.Wait() - if _, ok := err.(*exec.ExitError); ok && target != "" { + if _, ok := err.(*exec.ExitError); ok && options.Target != "" { err = nil // workaround for QEMU } close(runComplete) diff --git a/transform/transform_test.go b/transform/transform_test.go index 0b4cc0d2..f16baabd 100644 --- a/transform/transform_test.go +++ b/transform/transform_test.go @@ -140,7 +140,7 @@ func filterIrrelevantIRLines(lines []string) []string { // run. // If there are any errors, they are reported via the *testing.T instance. func compileGoFileForTesting(t *testing.T, filename string) llvm.Module { - target, err := compileopts.LoadTarget("i686--linux") + target, err := compileopts.LoadTarget(&compileopts.Options{GOOS: "linux", GOARCH: "386"}) if err != nil { t.Fatal("failed to load target:", err) }