diff --git a/builder/build.go b/builder/build.go index 5fe1429b85..24b233263f 100644 --- a/builder/build.go +++ b/builder/build.go @@ -842,22 +842,25 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe } ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat - if config.GOOS() == "windows" { - // Options for the MinGW wrapper for the lld COFF linker. + switch config.LinkerFlavor() { + case "coff": + // Options for driving ld.lld in PE/COFF mode. ldflags = append(ldflags, "-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel), "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto")) - } else if config.GOOS() == "darwin" { + case "darwin": // Options for the ld64-compatible lld linker. ldflags = append(ldflags, "--lto-O"+strconv.Itoa(speedLevel), "-cache_path_lto", filepath.Join(cacheDir, "thinlto")) - } else { + case "gnu": // Options for the ELF linker. ldflags = append(ldflags, "--lto-O"+strconv.Itoa(speedLevel), "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"), ) + default: + return fmt.Errorf("unknown linker flavor: %s", config.LinkerFlavor()) } if config.CodeModel() != "default" { ldflags = append(ldflags, diff --git a/compileopts/config.go b/compileopts/config.go index 673de40f95..a007c3f4aa 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -466,6 +466,26 @@ func (c *Config) LDFlags() []string { return ldflags } +// LinkerFlavor returns how the configured linker should be driven. +// Usually this is derived from GOOS, but targets may override it explicitly. +func (c *Config) LinkerFlavor() string { + if c.Target.LinkerFlavor != "" { + return c.Target.LinkerFlavor + } + goos := c.GOOS() + if goos == "" { + goos = c.Options.GOOS + } + switch goos { + case "windows": + return "coff" + case "darwin": + return "darwin" + default: + return "gnu" + } +} + // ExtraFiles returns the list of extra files to be built and linked with the // executable. This can include extra C and assembly files. func (c *Config) ExtraFiles() []string { diff --git a/compileopts/target.go b/compileopts/target.go index 70c7047462..42e3734870 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -38,7 +38,8 @@ type TargetSpec struct { Scheduler string `json:"scheduler,omitempty"` Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none) Linker string `json:"linker,omitempty"` - RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt) + LinkerFlavor string `json:"linker-flavor,omitempty"` // how to drive the configured linker (for example: gnu, coff, darwin) + RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt) Libc string `json:"libc,omitempty"` AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time. DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time. diff --git a/compileopts/target_test.go b/compileopts/target_test.go index d8a17a5e34..c700418839 100644 --- a/compileopts/target_test.go +++ b/compileopts/target_test.go @@ -112,3 +112,51 @@ func TestOverrideProperties(t *testing.T) { } } + +func TestConfigLinkerFlavor(t *testing.T) { + tests := []struct { + name string + target *TargetSpec + goos string + want string + }{ + { + name: "default gnu", + target: &TargetSpec{}, + goos: "linux", + want: "gnu", + }, + { + name: "default coff", + target: &TargetSpec{}, + goos: "windows", + want: "coff", + }, + { + name: "default darwin", + target: &TargetSpec{}, + goos: "darwin", + want: "darwin", + }, + { + name: "target override", + target: &TargetSpec{ + LinkerFlavor: "coff", + }, + goos: "linux", + want: "coff", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + config := &Config{ + Options: &Options{GOOS: tc.goos}, + Target: tc.target, + } + if got := config.LinkerFlavor(); got != tc.want { + t.Fatalf("LinkerFlavor() = %q, want %q", got, tc.want) + } + }) + } +}