1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-11 14:39:28 +02:00

feat: allow to override build flags by goos/goarch/etc (#2860)

* feat: allow to override build flags by goos/goarch/etc

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* test: improve

* fix: typos

* feat: templates, merges

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* test: fix

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* test: one more case

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* Update internal/builders/golang/build.go

Co-authored-by: Brian Flad <bflad417@gmail.com>

* Update internal/builders/golang/build.go

Co-authored-by: Brian Flad <bflad417@gmail.com>

* test: more tests

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

Co-authored-by: Brian Flad <bflad417@gmail.com>
This commit is contained in:
Carlos Alexandro Becker 2022-02-02 00:01:34 -03:00 committed by GitHub
parent df09e16f6d
commit 1bdfc9cdff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 354 additions and 54 deletions

View File

@ -19,6 +19,7 @@ import (
api "github.com/goreleaser/goreleaser/pkg/build"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/imdario/mergo"
)
// Default builder instance.
@ -129,29 +130,60 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
return nil
}
func withOverrides(ctx *context.Context, build config.Build, options api.Options) (config.BuildDetails, error) {
optsTarget := options.Goos + options.Goarch + options.Goarm + options.Gomips
for _, o := range build.BuildDetailsOverrides {
overrideTarget, err := tmpl.New(ctx).Apply(o.Goos + o.Goarch + o.Gomips + o.Goarm)
if err != nil {
return build.BuildDetails, err
}
if optsTarget == overrideTarget {
dets := config.BuildDetails{
Ldflags: build.BuildDetails.Ldflags,
Tags: build.BuildDetails.Tags,
Flags: build.BuildDetails.Flags,
Asmflags: build.BuildDetails.Asmflags,
Gcflags: build.BuildDetails.Gcflags,
}
if err := mergo.Merge(&dets, o.BuildDetails, mergo.WithOverride); err != nil {
return build.BuildDetails, err
}
log.WithField("dets", dets).Info("will use")
return dets, nil
}
}
return build.BuildDetails, nil
}
func buildGoBuildLine(ctx *context.Context, build config.Build, options api.Options, artifact *artifact.Artifact, env []string) ([]string, error) {
cmd := []string{build.GoBinary, "build"}
flags, err := processFlags(ctx, artifact, env, build.Flags, "")
details, err := withOverrides(ctx, build, options)
if err != nil {
return cmd, err
}
flags, err := processFlags(ctx, artifact, env, details.Flags, "")
if err != nil {
return cmd, err
}
cmd = append(cmd, flags...)
asmflags, err := processFlags(ctx, artifact, env, build.Asmflags, "-asmflags=")
asmflags, err := processFlags(ctx, artifact, env, details.Asmflags, "-asmflags=")
if err != nil {
return cmd, err
}
cmd = append(cmd, asmflags...)
gcflags, err := processFlags(ctx, artifact, env, build.Gcflags, "-gcflags=")
gcflags, err := processFlags(ctx, artifact, env, details.Gcflags, "-gcflags=")
if err != nil {
return cmd, err
}
cmd = append(cmd, gcflags...)
// tags is not a repeatable flag
if len(build.Tags) > 0 {
tags, err := processFlags(ctx, artifact, env, build.Tags, "")
if len(details.Tags) > 0 {
tags, err := processFlags(ctx, artifact, env, details.Tags, "")
if err != nil {
return cmd, err
}
@ -159,9 +191,9 @@ func buildGoBuildLine(ctx *context.Context, build config.Build, options api.Opti
}
// ldflags is not a repeatable flag
if len(build.Ldflags) > 0 {
if len(details.Ldflags) > 0 {
// flag prefix is skipped because ldflags need to output a single string
ldflags, err := processFlags(ctx, artifact, env, build.Ldflags, "")
ldflags, err := processFlags(ctx, artifact, env, details.Ldflags, "")
if err != nil {
return cmd, err
}

View File

@ -226,11 +226,13 @@ func TestBuild(t *testing.T) {
"linux_mips_softfloat",
"linux_mips64le_softfloat",
},
Asmflags: []string{".=", "all="},
Gcflags: []string{"all="},
Flags: []string{"{{.Env.GO_FLAGS}}"},
Tags: []string{"osusergo", "netgo", "static_build"},
GoBinary: "go",
BuildDetails: config.BuildDetails{
Asmflags: []string{".=", "all="},
Gcflags: []string{"all="},
Flags: []string{"{{.Env.GO_FLAGS}}"},
Tags: []string{"osusergo", "netgo", "static_build"},
},
},
},
}
@ -450,8 +452,10 @@ func TestBuildFailed(t *testing.T) {
config := config.Project{
Builds: []config.Build{
{
ID: "buildid",
Flags: []string{"-flag-that-dont-exists-to-force-failure"},
ID: "buildid",
BuildDetails: config.BuildDetails{
Flags: []string{"-flag-that-dont-exists-to-force-failure"},
},
Targets: []string{
runtimeTarget,
},
@ -474,8 +478,10 @@ func TestRunInvalidAsmflags(t *testing.T) {
config := config.Project{
Builds: []config.Build{
{
Binary: "nametest",
Asmflags: []string{"{{.Version}"},
Binary: "nametest",
BuildDetails: config.BuildDetails{
Asmflags: []string{"{{.Version}"},
},
Targets: []string{
runtimeTarget,
},
@ -496,8 +502,10 @@ func TestRunInvalidGcflags(t *testing.T) {
config := config.Project{
Builds: []config.Build{
{
Binary: "nametest",
Gcflags: []string{"{{.Version}"},
Binary: "nametest",
BuildDetails: config.BuildDetails{
Gcflags: []string{"{{.Version}"},
},
Targets: []string{
runtimeTarget,
},
@ -518,9 +526,11 @@ func TestRunInvalidLdflags(t *testing.T) {
config := config.Project{
Builds: []config.Build{
{
Binary: "nametest",
Flags: []string{"-v"},
Ldflags: []string{"-s -w -X main.version={{.Version}"},
Binary: "nametest",
BuildDetails: config.BuildDetails{
Flags: []string{"-v"},
Ldflags: []string{"-s -w -X main.version={{.Version}"},
},
Targets: []string{
runtimeTarget,
},
@ -542,7 +552,9 @@ func TestRunInvalidFlags(t *testing.T) {
Builds: []config.Build{
{
Binary: "nametest",
Flags: []string{"{{.Env.GOOS}"},
BuildDetails: config.BuildDetails{
Flags: []string{"{{.Env.GOOS}"},
},
Targets: []string{
runtimeTarget,
},
@ -832,9 +844,11 @@ func TestBuildModTimestamp(t *testing.T) {
"linux_mips_softfloat",
"linux_mips64le_softfloat",
},
Asmflags: []string{".=", "all="},
Gcflags: []string{"all="},
Flags: []string{"{{.Env.GO_FLAGS}}"},
BuildDetails: config.BuildDetails{
Asmflags: []string{".=", "all="},
Gcflags: []string{"all="},
Flags: []string{"{{.Env.GO_FLAGS}}"},
},
ModTimestamp: fmt.Sprintf("%d", modTime.Unix()),
GoBinary: "go",
},
@ -888,19 +902,25 @@ func TestBuildGoBuildLine(t *testing.T) {
ctx.Version = "1.2.3"
ctx.Git.Commit = "aaa"
line, err := buildGoBuildLine(ctx, config.Builds[0], api.Options{Path: "foo"}, &artifact.Artifact{}, []string{})
line, err := buildGoBuildLine(ctx, config.Builds[0], api.Options{
Path: "foo",
Goos: "linux",
Goarch: "amd64",
}, &artifact.Artifact{}, []string{})
require.NoError(t, err)
require.Equal(t, expected, line)
}
t.Run("full", func(t *testing.T) {
requireEqualCmd(t, config.Build{
Main: ".",
Asmflags: []string{"asmflag1", "asmflag2"},
Gcflags: []string{"gcflag1", "gcflag2"},
Flags: []string{"-flag1", "-flag2"},
Tags: []string{"tag1", "tag2"},
Ldflags: []string{"ldflag1", "ldflag2"},
Main: ".",
BuildDetails: config.BuildDetails{
Asmflags: []string{"asmflag1", "asmflag2"},
Gcflags: []string{"gcflag1", "gcflag2"},
Flags: []string{"-flag1", "-flag2"},
Tags: []string{"tag1", "tag2"},
Ldflags: []string{"ldflag1", "ldflag2"},
},
GoBinary: "go",
}, []string{
"go", "build",
@ -913,6 +933,41 @@ func TestBuildGoBuildLine(t *testing.T) {
})
})
t.Run("with overrides", func(t *testing.T) {
requireEqualCmd(t, config.Build{
Main: ".",
BuildDetails: config.BuildDetails{
Asmflags: []string{"asmflag1", "asmflag2"},
Gcflags: []string{"gcflag1", "gcflag2"},
Flags: []string{"-flag1", "-flag2"},
Tags: []string{"tag1", "tag2"},
Ldflags: []string{"ldflag1", "ldflag2"},
},
BuildDetailsOverrides: []config.BuildDetailsOverride{
{
Goos: "linux",
Goarch: "amd64",
BuildDetails: config.BuildDetails{
Asmflags: []string{"asmflag3"},
Gcflags: []string{"gcflag3"},
Flags: []string{"-flag3"},
Tags: []string{"tag3"},
Ldflags: []string{"ldflag3"},
},
},
},
GoBinary: "go",
}, []string{
"go", "build",
"-flag3",
"-asmflags=asmflag3",
"-gcflags=gcflag3",
"-tags=tag3",
"-ldflags=ldflag3",
"-o", "foo", ".",
})
})
t.Run("simple", func(t *testing.T) {
requireEqualCmd(t, config.Build{
Main: ".",
@ -922,8 +977,10 @@ func TestBuildGoBuildLine(t *testing.T) {
t.Run("ldflags1", func(t *testing.T) {
requireEqualCmd(t, config.Build{
Main: ".",
Ldflags: []string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.builtBy=goreleaser"},
Main: ".",
BuildDetails: config.BuildDetails{
Ldflags: []string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.builtBy=goreleaser"},
},
GoBinary: "go",
}, []string{
"go", "build",
@ -934,13 +991,179 @@ func TestBuildGoBuildLine(t *testing.T) {
t.Run("ldflags2", func(t *testing.T) {
requireEqualCmd(t, config.Build{
Main: ".",
Ldflags: []string{"-s -w", "-X main.version={{.Version}}"},
Main: ".",
BuildDetails: config.BuildDetails{
Ldflags: []string{"-s -w", "-X main.version={{.Version}}"},
},
GoBinary: "go",
}, []string{"go", "build", "-ldflags=-s -w -X main.version=1.2.3", "-o", "foo", "."})
})
}
func TestOverrides(t *testing.T) {
t.Run("linux amd64", func(t *testing.T) {
dets, err := withOverrides(
context.New(config.Project{}),
config.Build{
BuildDetails: config.BuildDetails{
Ldflags: []string{"original"},
},
BuildDetailsOverrides: []config.BuildDetailsOverride{
{
Goos: "linux",
Goarch: "amd64",
BuildDetails: config.BuildDetails{
Ldflags: []string{"overridden"},
},
},
},
}, api.Options{
Goos: "linux",
Goarch: "amd64",
},
)
require.NoError(t, err)
require.Equal(t, dets, config.BuildDetails{
Ldflags: []string{"overridden"},
})
})
t.Run("single sided", func(t *testing.T) {
dets, err := withOverrides(
context.New(config.Project{}),
config.Build{
BuildDetails: config.BuildDetails{},
BuildDetailsOverrides: []config.BuildDetailsOverride{
{
Goos: "linux",
Goarch: "amd64",
BuildDetails: config.BuildDetails{
Ldflags: []string{"overridden"},
Tags: []string{"tag1"},
Asmflags: []string{"asm1"},
Gcflags: []string{"gcflag1"},
},
},
},
}, api.Options{
Goos: "linux",
Goarch: "amd64",
},
)
require.NoError(t, err)
require.Equal(t, dets, config.BuildDetails{
Ldflags: []string{"overridden"},
Gcflags: []string{"gcflag1"},
Asmflags: []string{"asm1"},
Tags: []string{"tag1"},
})
})
t.Run("with template", func(t *testing.T) {
dets, err := withOverrides(
context.New(config.Project{}),
config.Build{
BuildDetails: config.BuildDetails{
Ldflags: []string{"original"},
Asmflags: []string{"asm1"},
},
BuildDetailsOverrides: []config.BuildDetailsOverride{
{
Goos: "{{ .Runtime.Goos }}",
Goarch: "{{ .Runtime.Goarch }}",
BuildDetails: config.BuildDetails{
Ldflags: []string{"overridden"},
},
},
},
}, api.Options{
Goos: runtime.GOOS,
Goarch: runtime.GOARCH,
},
)
require.NoError(t, err)
require.Equal(t, dets, config.BuildDetails{
Ldflags: []string{"overridden"},
Asmflags: []string{"asm1"},
})
})
t.Run("with invalid template", func(t *testing.T) {
_, err := withOverrides(
context.New(config.Project{}),
config.Build{
BuildDetailsOverrides: []config.BuildDetailsOverride{
{
Goos: "{{ .Runtime.Goos }",
},
},
}, api.Options{
Goos: runtime.GOOS,
Goarch: runtime.GOARCH,
},
)
require.EqualError(t, err, `template: tmpl:1: unexpected "}" in operand`)
})
t.Run("with goarm", func(t *testing.T) {
dets, err := withOverrides(
context.New(config.Project{}),
config.Build{
BuildDetails: config.BuildDetails{
Ldflags: []string{"original"},
},
BuildDetailsOverrides: []config.BuildDetailsOverride{
{
Goos: "linux",
Goarch: "arm",
Goarm: "6",
BuildDetails: config.BuildDetails{
Ldflags: []string{"overridden"},
},
},
},
}, api.Options{
Goos: "linux",
Goarch: "arm",
Goarm: "6",
},
)
require.NoError(t, err)
require.Equal(t, dets, config.BuildDetails{
Ldflags: []string{"overridden"},
})
})
t.Run("with gomips", func(t *testing.T) {
dets, err := withOverrides(
context.New(config.Project{}),
config.Build{
BuildDetails: config.BuildDetails{
Ldflags: []string{"original"},
},
BuildDetailsOverrides: []config.BuildDetailsOverride{
{
Goos: "linux",
Goarch: "mips",
Gomips: "softfloat",
BuildDetails: config.BuildDetails{
Ldflags: []string{"overridden"},
},
},
},
}, api.Options{
Goos: "linux",
Goarch: "mips",
Gomips: "softfloat",
},
)
require.NoError(t, err)
require.Equal(t, dets, config.BuildDetails{
Ldflags: []string{"overridden"},
})
})
}
//
// Helpers
//

View File

@ -70,8 +70,10 @@ func TestBuild(t *testing.T) {
{
Builder: "fake",
Binary: "testing.v{{.Version}}",
Flags: []string{"-n"},
Env: []string{"BLAH=1"},
BuildDetails: config.BuildDetails{
Flags: []string{"-n"},
},
Env: []string{"BLAH=1"},
},
},
}
@ -98,8 +100,10 @@ func TestRunPipe(t *testing.T) {
{
Builder: "fake",
Binary: "testing",
Flags: []string{"-v"},
Ldflags: []string{"-X main.test=testing"},
BuildDetails: config.BuildDetails{
Flags: []string{"-v"},
Ldflags: []string{"-X main.test=testing"},
},
Targets: []string{"linux_amd64"},
},
},
@ -122,8 +126,10 @@ func TestRunFullPipe(t *testing.T) {
ID: "build1",
Builder: "fake",
Binary: "testing",
Flags: []string{"-v"},
Ldflags: []string{"-X main.test=testing"},
BuildDetails: config.BuildDetails{
Flags: []string{"-v"},
Ldflags: []string{"-X main.test=testing"},
},
Hooks: config.BuildHookConfig{
Pre: []config.Hook{
{Cmd: "touch " + pre},
@ -159,8 +165,10 @@ func TestRunFullPipeFail(t *testing.T) {
{
Builder: "fakeFail",
Binary: "testing",
Flags: []string{"-v"},
Ldflags: []string{"-X main.test=testing"},
BuildDetails: config.BuildDetails{
Flags: []string{"-v"},
Ldflags: []string{"-X main.test=testing"},
},
Hooks: config.BuildHookConfig{
Pre: []config.Hook{
{Cmd: "touch " + pre},
@ -320,11 +328,13 @@ func TestDefaultPartialBuilds(t *testing.T) {
Main: "./cmd/main.go",
},
{
ID: "build2",
Binary: "foo",
Dir: "baz",
Ldflags: []string{"-s -w"},
Goarch: []string{"386"},
ID: "build2",
Binary: "foo",
Dir: "baz",
BuildDetails: config.BuildDetails{
Ldflags: []string{"-s -w"},
},
Goarch: []string{"386"},
},
},
},
@ -657,7 +667,9 @@ func TestRunHookFailWithLogs(t *testing.T) {
{
Builder: "fakeFail",
Binary: "testing",
Flags: []string{"-v"},
BuildDetails: config.BuildDetails{
Flags: []string{"-v"},
},
Hooks: config.BuildHookConfig{
Pre: []config.Hook{
{Cmd: "sh -c 'echo foo; exit 1'"},

View File

@ -297,21 +297,35 @@ type Build struct {
Ignore []IgnoredBuild `yaml:"ignore,omitempty"`
Dir string `yaml:"dir,omitempty"`
Main string `yaml:"main,omitempty"`
Ldflags StringArray `yaml:"ldflags,omitempty"`
Tags FlagArray `yaml:"tags,omitempty"`
Flags FlagArray `yaml:"flags,omitempty"`
Binary string `yaml:"binary,omitempty"`
Hooks BuildHookConfig `yaml:"hooks,omitempty"`
Env []string `yaml:"env,omitempty"`
Builder string `yaml:"builder,omitempty"`
Asmflags StringArray `yaml:"asmflags,omitempty"`
Gcflags StringArray `yaml:"gcflags,omitempty"`
ModTimestamp string `yaml:"mod_timestamp,omitempty"`
Skip bool `yaml:"skip,omitempty"`
GoBinary string `yaml:"gobinary,omitempty"`
NoUniqueDistDir bool `yaml:"no_unique_dist_dir,omitempty"`
UnproxiedMain string `yaml:"-"` // used by gomod.proxy
UnproxiedDir string `yaml:"-"` // used by gomod.proxy
BuildDetails `yaml:",inline"` // nolint: tagliatelle
BuildDetailsOverrides []BuildDetailsOverride `yaml:"overrides,omitempty"`
}
type BuildDetailsOverride struct {
Goos string `yaml:"goos,omitempty"`
Goarch string `yaml:"goarch,omitempty"`
Goarm string `yaml:"goarm,omitempty"`
Gomips string `yaml:"gomips,omitempty"`
BuildDetails `yaml:",inline"` // nolint: tagliatelle
}
type BuildDetails struct {
Ldflags StringArray `yaml:"ldflags,omitempty"`
Tags FlagArray `yaml:"tags,omitempty"`
Flags FlagArray `yaml:"flags,omitempty"`
Asmflags StringArray `yaml:"asmflags,omitempty"`
Gcflags StringArray `yaml:"gcflags,omitempty"`
}
type BuildHookConfig struct {

View File

@ -155,6 +155,25 @@ builds:
# Valid options are: `go` and `prebuilt`.
# Defaults to `go`.
builder: prebuilt
# Overrides allows to override some fields for specific targets.
# This can be specially useful when using CGO.
# Note: it'll only match if the full target matches.
#
# Defaults to empty.
overrides:
- goos: darwin
goarch: arm64
goarm: ''
gomips: ''
ldflags:
- foo
tags:
- bar
asmflags:
- foobar
gcflags:
- foobaz
```
!!! tip