mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
feat: Convert flag fields into lists
This allows users to specify different sets of compiler, assembler, and/or linker flags for different packages. It also makes it possible to specify generic flags that contain spaces. It does this while maintaining compatibility with the old format of the fields in question by up-converting bare strings into single-element lists. Resolves #668
This commit is contained in:
parent
9b4cf40ef6
commit
5252f74ade
@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/apex/log"
|
||||
"gopkg.in/yaml.v2"
|
||||
@ -78,6 +79,42 @@ type IgnoredBuild struct {
|
||||
Goos, Goarch, Goarm string
|
||||
}
|
||||
|
||||
// StringArray is a wrapper for an array of strings
|
||||
type StringArray []string
|
||||
|
||||
// UnmarshalYAML is a custom unmarshaler that wraps strings in arrays
|
||||
func (a *StringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var strings []string
|
||||
if err := unmarshal(&strings); err != nil {
|
||||
var str string
|
||||
if err := unmarshal(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = []string{str}
|
||||
} else {
|
||||
*a = strings
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlagArray is a wrapper for an array of strings
|
||||
type FlagArray []string
|
||||
|
||||
// UnmarshalYAML is a custom unmarshaler that wraps strings in arrays
|
||||
func (a *FlagArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var flags []string
|
||||
if err := unmarshal(&flags); err != nil {
|
||||
var flagstr string
|
||||
if err := unmarshal(&flagstr); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = strings.Fields(flagstr)
|
||||
} else {
|
||||
*a = flags
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build contains the build configuration section
|
||||
type Build struct {
|
||||
Goos []string `yaml:",omitempty"`
|
||||
@ -86,14 +123,14 @@ type Build struct {
|
||||
Targets []string `yaml:",omitempty"`
|
||||
Ignore []IgnoredBuild `yaml:",omitempty"`
|
||||
Main string `yaml:",omitempty"`
|
||||
Ldflags string `yaml:",omitempty"`
|
||||
Flags string `yaml:",omitempty"`
|
||||
Ldflags StringArray `yaml:",omitempty"`
|
||||
Flags FlagArray `yaml:",omitempty"`
|
||||
Binary string `yaml:",omitempty"`
|
||||
Hooks Hooks `yaml:",omitempty"`
|
||||
Env []string `yaml:",omitempty"`
|
||||
Lang string `yaml:",omitempty"`
|
||||
Asmflags string `yaml:",omitempty"`
|
||||
Gcflags string `yaml:",omitempty"`
|
||||
Asmflags StringArray `yaml:",omitempty"`
|
||||
Gcflags StringArray `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
// FormatOverride is used to specify a custom format for a specific GOOS.
|
||||
|
126
config/config_array_test.go
Normal file
126
config/config_array_test.go
Normal file
@ -0,0 +1,126 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type Unmarshaled struct {
|
||||
Strings StringArray `yaml:",omitempty"`
|
||||
Flags FlagArray `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
type yamlUnmarshalTestCase struct {
|
||||
yaml string
|
||||
expected Unmarshaled
|
||||
err string
|
||||
}
|
||||
|
||||
var stringArrayTests = []yamlUnmarshalTestCase{
|
||||
{
|
||||
"",
|
||||
Unmarshaled{},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"strings: []",
|
||||
Unmarshaled{
|
||||
Strings: StringArray{},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"strings: [one two, three]",
|
||||
Unmarshaled{
|
||||
Strings: StringArray{"one two", "three"},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"strings: one two",
|
||||
Unmarshaled{
|
||||
Strings: StringArray{"one two"},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"strings: {key: val}",
|
||||
Unmarshaled{},
|
||||
"yaml: unmarshal errors:\n line 1: cannot unmarshal !!map into string",
|
||||
},
|
||||
}
|
||||
|
||||
var flagArrayTests = []yamlUnmarshalTestCase{
|
||||
{
|
||||
"",
|
||||
Unmarshaled{},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"flags: []",
|
||||
Unmarshaled{
|
||||
Flags: FlagArray{},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"flags: [one two, three]",
|
||||
Unmarshaled{
|
||||
Flags: FlagArray{"one two", "three"},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"flags: one two",
|
||||
Unmarshaled{
|
||||
Flags: FlagArray{"one", "two"},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"flags: {key: val}",
|
||||
Unmarshaled{},
|
||||
"yaml: unmarshal errors:\n line 1: cannot unmarshal !!map into string",
|
||||
},
|
||||
}
|
||||
|
||||
func TestStringArray(t *testing.T) {
|
||||
for _, testCase := range stringArrayTests {
|
||||
var actual Unmarshaled
|
||||
|
||||
err := yaml.UnmarshalStrict([]byte(testCase.yaml), &actual)
|
||||
if testCase.err == "" {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCase.expected, actual)
|
||||
} else {
|
||||
assert.EqualError(t, err, testCase.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func TestStringArrayFailure(t *testing.T) {
|
||||
// var source = `
|
||||
// strings:
|
||||
// key: val
|
||||
// `
|
||||
|
||||
// var actual Unmarshaled
|
||||
// err := yaml.UnmarshalStrict([]byte(source), &actual)
|
||||
// // assert.EqualError(t, err, )
|
||||
// }
|
||||
|
||||
func TestFlagArray(t *testing.T) {
|
||||
for _, testCase := range flagArrayTests {
|
||||
var actual Unmarshaled
|
||||
|
||||
err := yaml.UnmarshalStrict([]byte(testCase.yaml), &actual)
|
||||
if testCase.err == "" {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.EqualError(t, err, testCase.err)
|
||||
}
|
||||
assert.Equal(t, testCase.expected, actual)
|
||||
}
|
||||
}
|
@ -44,8 +44,8 @@ func (*Builder) WithDefaults(build config.Build) config.Build {
|
||||
if len(build.Goarm) == 0 {
|
||||
build.Goarm = []string{"6"}
|
||||
}
|
||||
if build.Ldflags == "" {
|
||||
build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
|
||||
if len(build.Ldflags) == 0 {
|
||||
build.Ldflags = []string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"}
|
||||
}
|
||||
if len(build.Targets) == 0 {
|
||||
build.Targets = matrix(build)
|
||||
@ -59,31 +59,29 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
|
||||
return err
|
||||
}
|
||||
cmd := []string{"go", "build"}
|
||||
if build.Flags != "" {
|
||||
cmd = append(cmd, strings.Fields(build.Flags)...)
|
||||
}
|
||||
|
||||
if build.Asmflags != "" {
|
||||
flags, err := processField(ctx, build.Asmflags, "asmflags")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = append(cmd, "-asmflags="+flags)
|
||||
}
|
||||
cmd = append(cmd, build.Flags...)
|
||||
|
||||
if build.Gcflags != "" {
|
||||
flags, err := processField(ctx, build.Gcflags, "gcflags")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = append(cmd, "-gcflags="+flags)
|
||||
}
|
||||
|
||||
flags, err := processField(ctx, build.Ldflags, "ldflags")
|
||||
asmflags, err := processFlags(ctx, build.Asmflags, "asmflags", "-asmflags=")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = append(cmd, "-ldflags="+flags, "-o", options.Path, build.Main)
|
||||
cmd = append(cmd, asmflags...)
|
||||
|
||||
gcflags, err := processFlags(ctx, build.Gcflags, "gcflags", "-gcflags=")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = append(cmd, gcflags...)
|
||||
|
||||
ldflags, err := processFlags(ctx, build.Ldflags, "ldflags", "-ldflags=")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd = append(cmd, ldflags...)
|
||||
|
||||
cmd = append(cmd, "-o", options.Path, build.Main)
|
||||
|
||||
target, err := newBuildTarget(options.Target)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -107,6 +105,18 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
|
||||
return nil
|
||||
}
|
||||
|
||||
func processFlags(ctx *context.Context, flags []string, flagName string, flagPrefix string) ([]string, error) {
|
||||
processed := make([]string, 0, len(flags))
|
||||
for _, rawFlag := range flags {
|
||||
flag, err := processField(ctx, rawFlag, flagName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
processed = append(processed, flagPrefix+flag)
|
||||
}
|
||||
return processed, nil
|
||||
}
|
||||
|
||||
func processField(ctx *context.Context, field string, fieldName string) (string, error) {
|
||||
var data = struct {
|
||||
Commit string
|
||||
|
@ -84,8 +84,8 @@ func TestBuild(t *testing.T) {
|
||||
"windows_amd64",
|
||||
"linux_arm_6",
|
||||
},
|
||||
Asmflags: "all=",
|
||||
Gcflags: "all=",
|
||||
Asmflags: []string{".=", "all="},
|
||||
Gcflags: []string{"all="},
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -160,7 +160,7 @@ func TestBuildFailed(t *testing.T) {
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Flags: "-flag-that-dont-exists-to-force-failure",
|
||||
Flags: []string{"-flag-that-dont-exists-to-force-failure"},
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
@ -207,7 +207,7 @@ func TestRunInvalidAsmflags(t *testing.T) {
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "nametest",
|
||||
Asmflags: "{{.Version}",
|
||||
Asmflags: []string{"{{.Version}"},
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
@ -229,7 +229,7 @@ func TestRunInvalidGcflags(t *testing.T) {
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "nametest",
|
||||
Gcflags: "{{.Version}",
|
||||
Gcflags: []string{"{{.Version}"},
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
@ -251,8 +251,8 @@ func TestRunInvalidLdflags(t *testing.T) {
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "nametest",
|
||||
Flags: "-v",
|
||||
Ldflags: "-s -w -X main.version={{.Version}",
|
||||
Flags: []string{"-v"},
|
||||
Ldflags: []string{"-s -w -X main.version={{.Version}"},
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
@ -352,7 +352,9 @@ func TestLdFlagsFullTemplate(t *testing.T) {
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Ldflags: `-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}" -X main.time={{ time "20060102" }}`,
|
||||
Ldflags: []string{
|
||||
`-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}" -X main.time={{ time "20060102" }}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -365,7 +367,7 @@ func TestLdFlagsFullTemplate(t *testing.T) {
|
||||
Config: config,
|
||||
Env: map[string]string{"FOO": "123"},
|
||||
}
|
||||
flags, err := processField(ctx, ctx.Config.Builds[0].Ldflags, "ldflags")
|
||||
flags, err := processField(ctx, ctx.Config.Builds[0].Ldflags[0], "ldflags")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, flags, "-s -w")
|
||||
assert.Contains(t, flags, "-X main.version=1.2.3")
|
||||
@ -385,7 +387,7 @@ func TestInvalidTemplate(t *testing.T) {
|
||||
t.Run(template, func(tt *testing.T) {
|
||||
var config = config.Project{
|
||||
Builds: []config.Build{
|
||||
{Ldflags: template},
|
||||
{Ldflags: []string{template}},
|
||||
},
|
||||
}
|
||||
var ctx = &context.Context{
|
||||
@ -398,6 +400,41 @@ func TestInvalidTemplate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessFlags(t *testing.T) {
|
||||
var ctx = &context.Context{
|
||||
Version: "1.2.3",
|
||||
}
|
||||
|
||||
var source = []string{
|
||||
"{{.Version}}",
|
||||
"flag",
|
||||
}
|
||||
|
||||
var expected = []string{
|
||||
"-testflag=1.2.3",
|
||||
"-testflag=flag",
|
||||
}
|
||||
|
||||
flags, err := processFlags(ctx, source, "testflag", "-testflag=")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, flags, 2)
|
||||
assert.Equal(t, expected, flags)
|
||||
}
|
||||
|
||||
func TestProcessFlagsInvalid(t *testing.T) {
|
||||
var ctx = &context.Context{}
|
||||
|
||||
var source = []string{
|
||||
"{{.Version}",
|
||||
}
|
||||
|
||||
var expected = `template: testflag:1: unexpected "}" in operand`
|
||||
|
||||
flags, err := processFlags(ctx, source, "testflag", "-testflag=")
|
||||
assert.EqualError(t, err, expected)
|
||||
assert.Nil(t, flags)
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
@ -53,7 +53,7 @@ func TestBuild(t *testing.T) {
|
||||
{
|
||||
Lang: "fake",
|
||||
Binary: "testing.v{{.Version}}",
|
||||
Flags: "-n",
|
||||
Flags: []string{"-n"},
|
||||
Env: []string{"BLAH=1"},
|
||||
},
|
||||
},
|
||||
@ -77,8 +77,8 @@ func TestRunPipe(t *testing.T) {
|
||||
{
|
||||
Lang: "fake",
|
||||
Binary: "testing",
|
||||
Flags: "-v",
|
||||
Ldflags: "-X main.test=testing",
|
||||
Flags: []string{"-v"},
|
||||
Ldflags: []string{"-X main.test=testing"},
|
||||
Targets: []string{"whatever"},
|
||||
},
|
||||
},
|
||||
@ -98,8 +98,8 @@ func TestRunFullPipe(t *testing.T) {
|
||||
{
|
||||
Lang: "fake",
|
||||
Binary: "testing",
|
||||
Flags: "-v",
|
||||
Ldflags: "-X main.test=testing",
|
||||
Flags: []string{"-v"},
|
||||
Ldflags: []string{"-X main.test=testing"},
|
||||
Hooks: config.Hooks{
|
||||
Pre: "touch " + pre,
|
||||
Post: "touch " + post,
|
||||
@ -125,8 +125,8 @@ func TestRunFullPipeFail(t *testing.T) {
|
||||
{
|
||||
Lang: "fakeFail",
|
||||
Binary: "testing",
|
||||
Flags: "-v",
|
||||
Ldflags: "-X main.test=testing",
|
||||
Flags: []string{"-v"},
|
||||
Ldflags: []string{"-X main.test=testing"},
|
||||
Hooks: config.Hooks{
|
||||
Pre: "touch " + pre,
|
||||
Post: "touch " + post,
|
||||
@ -212,7 +212,8 @@ func TestDefaultEmptyBuild(t *testing.T) {
|
||||
assert.Equal(t, []string{"linux", "darwin"}, build.Goos)
|
||||
assert.Equal(t, []string{"amd64", "386"}, build.Goarch)
|
||||
assert.Equal(t, []string{"6"}, build.Goarm)
|
||||
assert.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}", build.Ldflags)
|
||||
assert.Len(t, build.Ldflags, 1)
|
||||
assert.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}", build.Ldflags[0])
|
||||
}
|
||||
|
||||
func TestDefaultPartialBuilds(t *testing.T) {
|
||||
@ -226,7 +227,7 @@ func TestDefaultPartialBuilds(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Binary: "foo",
|
||||
Ldflags: "-s -w",
|
||||
Ldflags: []string{"-s -w"},
|
||||
Goarch: []string{"386"},
|
||||
},
|
||||
},
|
||||
@ -240,7 +241,8 @@ func TestDefaultPartialBuilds(t *testing.T) {
|
||||
assert.Equal(t, []string{"linux"}, build.Goos)
|
||||
assert.Equal(t, []string{"amd64", "386"}, build.Goarch)
|
||||
assert.Equal(t, []string{"6"}, build.Goarm)
|
||||
assert.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}", build.Ldflags)
|
||||
assert.Len(t, build.Ldflags, 1)
|
||||
assert.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}", build.Ldflags[0])
|
||||
})
|
||||
t.Run("build1", func(t *testing.T) {
|
||||
var build = ctx.Config.Builds[1]
|
||||
@ -249,7 +251,8 @@ func TestDefaultPartialBuilds(t *testing.T) {
|
||||
assert.Equal(t, []string{"linux", "darwin"}, build.Goos)
|
||||
assert.Equal(t, []string{"386"}, build.Goarch)
|
||||
assert.Equal(t, []string{"6"}, build.Goarm)
|
||||
assert.Equal(t, "-s -w", build.Ldflags)
|
||||
assert.Len(t, build.Ldflags, 1)
|
||||
assert.Equal(t, "-s -w", build.Ldflags[0])
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -32,10 +32,12 @@ builds:
|
||||
|
||||
# Set flags for custom build tags.
|
||||
# Default is empty.
|
||||
flags: -tags dev
|
||||
flags:
|
||||
- -tags
|
||||
- dev
|
||||
|
||||
# Custom asmflags template.
|
||||
# This is parsed with the Go template engine and the following variables
|
||||
# Custom asmflags templates.
|
||||
# These are parsed with the Go template engine and the following variables
|
||||
# are available:
|
||||
# - Date
|
||||
# - Commit
|
||||
@ -47,10 +49,12 @@ builds:
|
||||
# `time "2006-01-02"` too if you need custom formats
|
||||
#
|
||||
# Default is empty.
|
||||
asmflags: all=-trimpath={{.Env.GOPATH}}
|
||||
asmflags:
|
||||
- -D mysymbol
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
|
||||
# Custom gcflags template.
|
||||
# This is parsed with the Go template engine and the following variables
|
||||
# Custom gcflags templates.
|
||||
# These are parsed with the Go template engine and the following variables
|
||||
# are available:
|
||||
# - Date
|
||||
# - Commit
|
||||
@ -62,10 +66,12 @@ builds:
|
||||
# `time "2006-01-02"` too if you need custom formats
|
||||
#
|
||||
# Default is empty.
|
||||
gcflags: all=-trimpath={{.Env.GOPATH}}
|
||||
gcflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
- ./dontoptimizeme=-N
|
||||
|
||||
# Custom ldflags template.
|
||||
# This is parsed with the Go template engine and the following variables
|
||||
# Custom ldflags templates.
|
||||
# These are parsed with the Go template engine and the following variables
|
||||
# are available:
|
||||
# - Date
|
||||
# - Commit
|
||||
@ -77,7 +83,9 @@ builds:
|
||||
# `time "2006-01-02"` too if you need custom formats
|
||||
#
|
||||
# Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}`.
|
||||
ldflags: -s -w -X main.build={{.Version}}
|
||||
ldflags:
|
||||
- -s -w -X main.build={{.Version}}
|
||||
- ./usemsan=-msan
|
||||
|
||||
# Custom environment variables to be set during the builds.
|
||||
# Default is empty.
|
||||
@ -130,7 +138,8 @@ example:
|
||||
|
||||
```yaml
|
||||
builds:
|
||||
- ldflags: -s -w -X "main.goversion={{.Env.GOVERSION}}"
|
||||
- ldflags:
|
||||
- -s -w -X "main.goversion={{.Env.GOVERSION}}"
|
||||
```
|
||||
|
||||
Then you can run:
|
||||
|
Loading…
x
Reference in New Issue
Block a user