1
0
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:
Eli Young 2018-05-14 17:22:55 -07:00 committed by Carlos Alexandro Becker
parent 9b4cf40ef6
commit 5252f74ade
6 changed files with 280 additions and 58 deletions

View File

@ -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
View 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)
}
}

View File

@ -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

View File

@ -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
//

View File

@ -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])
})
}

View File

@ -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: