1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-17 20:47:50 +02:00

refactor(build): preparing to support multiple languages (#5307)

This starts laying the foundation for supporting more languages, the
first of which will probably be Zig, and then Rust.

I already have a zig prototype working in another branch, just raw
dogged it to see if it would work, and since it does, now I'll do it
piece by piece but with hopefully slightly better code.
This commit is contained in:
Carlos Alexandro Becker 2024-11-25 23:00:28 -03:00 committed by GitHub
parent f061ae92ad
commit d43f84aa3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 497 additions and 311 deletions

View File

@ -38,6 +38,46 @@ func init() {
// Builder is golang builder. // Builder is golang builder.
type Builder struct{} type Builder struct{}
// Parse implements build.Builder.
func (b *Builder) Parse(target string) (api.Target, error) {
target = fixTarget(target)
parts := strings.Split(target, "_")
if len(parts) < 2 {
return nil, fmt.Errorf("%s is not a valid build target", target)
}
goos := parts[0]
goarch := parts[1]
t := Target{
Target: target,
Goos: goos,
Goarch: goarch,
}
if len(parts) > 2 {
extra := parts[2]
switch goarch {
case "amd64":
t.Goamd64 = extra
case "arm64":
t.Goarm64 = extra
case "386":
t.Go386 = extra
case "arm":
t.Goarm = extra
case "mips", "mipsle", "mips64", "mips64le":
t.Gomips = extra
case "ppc64":
t.Goppc64 = extra
case "riscv":
t.Goriscv64 = extra
}
}
return t, nil
}
// WithDefaults sets the defaults for a golang build and returns it. // WithDefaults sets the defaults for a golang build and returns it.
func (*Builder) WithDefaults(build config.Build) (config.Build, error) { func (*Builder) WithDefaults(build config.Build) (config.Build, error) {
if build.GoBinary == "" { if build.GoBinary == "" {
@ -187,19 +227,21 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
return err return err
} }
t := options.Target.(Target)
a := &artifact.Artifact{ a := &artifact.Artifact{
Type: artifact.Binary, Type: artifact.Binary,
Path: options.Path, Path: options.Path,
Name: options.Name, Name: options.Name,
Goos: options.Goos, Goos: t.Goos,
Goarch: options.Goarch, Goarch: t.Goarch,
Goamd64: options.Goamd64, Goamd64: t.Goamd64,
Go386: options.Go386, Go386: t.Go386,
Goarm: options.Goarm, Goarm: t.Goarm,
Goarm64: options.Goarm64, Goarm64: t.Goarm64,
Gomips: options.Gomips, Gomips: t.Gomips,
Goppc64: options.Goppc64, Goppc64: t.Goppc64,
Goriscv64: options.Goriscv64, Goriscv64: t.Goriscv64,
Extra: map[string]interface{}{ Extra: map[string]interface{}{
artifact.ExtraBinary: strings.TrimSuffix(filepath.Base(options.Path), options.Ext), artifact.ExtraBinary: strings.TrimSuffix(filepath.Base(options.Path), options.Ext),
artifact.ExtraExt: options.Ext, artifact.ExtraExt: options.Ext,
@ -211,12 +253,12 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
a.Type = artifact.CArchive a.Type = artifact.CArchive
ctx.Artifacts.Add(getHeaderArtifactForLibrary(build, options)) ctx.Artifacts.Add(getHeaderArtifactForLibrary(build, options))
} }
if build.Buildmode == "c-shared" && !strings.Contains(options.Target, "wasm") { if build.Buildmode == "c-shared" && !strings.Contains(t.Target, "wasm") {
a.Type = artifact.CShared a.Type = artifact.CShared
ctx.Artifacts.Add(getHeaderArtifactForLibrary(build, options)) ctx.Artifacts.Add(getHeaderArtifactForLibrary(build, options))
} }
details, err := withOverrides(ctx, build, options) details, err := withOverrides(ctx, build, t)
if err != nil { if err != nil {
return err return err
} }
@ -238,20 +280,8 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
} }
} }
} }
env = append(
env,
"GOOS="+options.Goos,
"GOARCH="+options.Goarch,
"GOAMD64="+options.Goamd64,
"GO386="+options.Go386,
"GOARM="+options.Goarm,
"GOARM64="+options.Goarm64,
"GOMIPS="+options.Gomips,
"GOMIPS64="+options.Gomips,
"GOPPC64="+options.Goppc64,
"GORISCV64="+options.Goriscv64,
)
env = append(env, t.env()...)
if v := os.Getenv("GOCACHEPROG"); v != "" { if v := os.Getenv("GOCACHEPROG"); v != "" {
env = append(env, "GOCACHEPROG="+v) env = append(env, "GOCACHEPROG="+v)
} }
@ -281,18 +311,10 @@ func (*Builder) Build(ctx *context.Context, build config.Build, options api.Opti
return nil return nil
} }
func withOverrides(ctx *context.Context, build config.Build, options api.Options) (config.BuildDetails, error) { func withOverrides(ctx *context.Context, build config.Build, target Target) (config.BuildDetails, error) {
optsTarget := options.Goos + "_" + options.Goarch optsTarget := target.Target
if extra := options.Goamd64 + options.Go386 + options.Goarm + options.Goarm64 + options.Gomips + options.Goppc64 + options.Goriscv64; extra != "" {
optsTarget += "_" + extra
}
optsTarget = fixTarget(optsTarget)
for _, o := range build.BuildDetailsOverrides { for _, o := range build.BuildDetailsOverrides {
s := o.Goos + "_" + o.Goarch overrideTarget, err := tmpl.New(ctx).Apply(formatTarget(o))
if extra := o.Goamd64 + o.Go386 + o.Goarm + o.Goarm64 + o.Gomips + o.Goppc64 + o.Goriscv64; extra != "" {
s += "_" + extra
}
overrideTarget, err := tmpl.New(ctx).Apply(s)
if err != nil { if err != nil {
return build.BuildDetails, err return build.BuildDetails, err
} }
@ -521,20 +543,21 @@ func getHeaderArtifactForLibrary(build config.Build, options api.Options) *artif
basePath := filepath.Base(fullPathWithoutExt) basePath := filepath.Base(fullPathWithoutExt)
fullPath := fullPathWithoutExt + ".h" fullPath := fullPathWithoutExt + ".h"
headerName := basePath + ".h" headerName := basePath + ".h"
t := options.Target.(Target)
return &artifact.Artifact{ return &artifact.Artifact{
Type: artifact.Header, Type: artifact.Header,
Path: fullPath, Path: fullPath,
Name: headerName, Name: headerName,
Goos: options.Goos, Goos: t.Goos,
Goarch: options.Goarch, Goarch: t.Goarch,
Goamd64: options.Goamd64, Goamd64: t.Goamd64,
Go386: options.Go386, Go386: t.Go386,
Goarm: options.Goarm, Goarm: t.Goarm,
Goarm64: options.Goarm64, Goarm64: t.Goarm64,
Gomips: options.Gomips, Gomips: t.Gomips,
Goppc64: options.Goppc64, Goppc64: t.Goppc64,
Goriscv64: options.Goriscv64, Goriscv64: t.Goriscv64,
Extra: map[string]interface{}{ Extra: map[string]interface{}{
artifact.ExtraBinary: headerName, artifact.ExtraBinary: headerName,
artifact.ExtraExt: ".h", artifact.ExtraExt: ".h",

View File

@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/goreleaser/goreleaser/v2/internal/artifact" "github.com/goreleaser/goreleaser/v2/internal/artifact"
"github.com/goreleaser/goreleaser/v2/internal/experimental"
"github.com/goreleaser/goreleaser/v2/internal/testctx" "github.com/goreleaser/goreleaser/v2/internal/testctx"
"github.com/goreleaser/goreleaser/v2/internal/testlib" "github.com/goreleaser/goreleaser/v2/internal/testlib"
"github.com/goreleaser/goreleaser/v2/internal/tmpl" "github.com/goreleaser/goreleaser/v2/internal/tmpl"
@ -33,6 +34,78 @@ var go118FirstClassAdjustedTargets = []string{
"windows_amd64_v1", "windows_amd64_v1",
} }
func TestParse(t *testing.T) {
for target, dst := range map[string]Target{
"linux_amd64": {
Target: "linux_amd64_v1",
Goos: "linux",
Goarch: "amd64",
Goamd64: "v1",
},
"linux_amd64_v2": {
Target: "linux_amd64_v2",
Goos: "linux",
Goarch: "amd64",
Goamd64: "v2",
},
"linux_arm": {
Target: "linux_arm_" + experimental.DefaultGOARM(),
Goos: "linux",
Goarch: "arm",
Goarm: experimental.DefaultGOARM(),
},
"linux_arm_7": {
Target: "linux_arm_7",
Goos: "linux",
Goarch: "arm",
Goarm: "7",
},
"linux_mips": {
Target: "linux_mips_hardfloat",
Goos: "linux",
Goarch: "mips",
Gomips: "hardfloat",
},
"linux_mips_softfloat": {
Target: "linux_mips_softfloat",
Goos: "linux",
Goarch: "mips",
Gomips: "softfloat",
},
"linux_386": {
Target: "linux_386_sse2",
Goos: "linux",
Goarch: "386",
Go386: "sse2",
},
"linux_386_hardfloat": {
Target: "linux_386_hardfloat",
Goos: "linux",
Goarch: "386",
Go386: "hardfloat",
},
"linux_arm64": {
Target: "linux_arm64_v8.0",
Goos: "linux",
Goarch: "arm64",
Goarm64: "v8.0",
},
"linux_arm64_v9.0": {
Target: "linux_arm64_v9.0",
Goos: "linux",
Goarch: "arm64",
Goarm64: "v9.0",
},
} {
t.Run(target, func(t *testing.T) {
got, err := Default.Parse(target)
require.NoError(t, err)
require.IsType(t, Target{}, got)
require.Equal(t, dst, got.(Target))
})
}
}
func TestWithDefaults(t *testing.T) { func TestWithDefaults(t *testing.T) {
for name, testcase := range map[string]struct { for name, testcase := range map[string]struct {
build config.Build build config.Build
@ -429,30 +502,14 @@ func TestBuild(t *testing.T) {
// injecting some delay here to force inconsistent mod times on bins // injecting some delay here to force inconsistent mod times on bins
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
parts := strings.Split(target, "_") gtarget, err := Default.Parse(target)
goos := parts[0] require.NoError(t, err)
goarch := parts[1] require.NoError(t, Default.Build(ctx, build, api.Options{
goarm := "" Target: gtarget,
gomips := ""
if len(parts) > 2 {
if strings.Contains(goarch, "arm") {
goarm = parts[2]
}
if strings.Contains(goarch, "mips") {
gomips = parts[2]
}
}
err := Default.Build(ctx, build, api.Options{
Target: target,
Name: bin + ext, Name: bin + ext,
Path: filepath.Join(folder, "dist", target, bin+ext), Path: filepath.Join(folder, "dist", target, bin+ext),
Goos: goos,
Goarch: goarch,
Goarm: goarm,
Gomips: gomips,
Ext: ext, Ext: ext,
}) }))
require.NoError(t, err)
} }
list := ctx.Artifacts list := ctx.Artifacts
require.NoError(t, list.Visit(func(a *artifact.Artifact) error { require.NoError(t, list.Visit(func(a *artifact.Artifact) error {
@ -462,12 +519,13 @@ func TestBuild(t *testing.T) {
} }
return nil return nil
})) }))
require.ElementsMatch(t, list.List(), []*artifact.Artifact{ expected := []*artifact.Artifact{
{ {
Name: "bin/foo-v5.6.7", Name: "bin/foo-v5.6.7",
Path: filepath.ToSlash(filepath.Join("dist", "linux_amd64", "bin", "foo-v5.6.7")), Path: filepath.ToSlash(filepath.Join("dist", "linux_amd64", "bin", "foo-v5.6.7")),
Goos: "linux", Goos: "linux",
Goarch: "amd64", Goarch: "amd64",
Goamd64: "v1",
Type: artifact.Binary, Type: artifact.Binary,
Extra: map[string]interface{}{ Extra: map[string]interface{}{
artifact.ExtraExt: "", artifact.ExtraExt: "",
@ -509,6 +567,7 @@ func TestBuild(t *testing.T) {
Path: filepath.ToSlash(filepath.Join("dist", "darwin_amd64", "bin", "foo-v5.6.7")), Path: filepath.ToSlash(filepath.Join("dist", "darwin_amd64", "bin", "foo-v5.6.7")),
Goos: "darwin", Goos: "darwin",
Goarch: "amd64", Goarch: "amd64",
Goamd64: "v1",
Type: artifact.Binary, Type: artifact.Binary,
Extra: map[string]interface{}{ Extra: map[string]interface{}{
artifact.ExtraExt: "", artifact.ExtraExt: "",
@ -536,6 +595,7 @@ func TestBuild(t *testing.T) {
Path: filepath.ToSlash(filepath.Join("dist", "windows_amd64", "bin", "foo-v5.6.7.exe")), Path: filepath.ToSlash(filepath.Join("dist", "windows_amd64", "bin", "foo-v5.6.7.exe")),
Goos: "windows", Goos: "windows",
Goarch: "amd64", Goarch: "amd64",
Goamd64: "v1",
Type: artifact.Binary, Type: artifact.Binary,
Extra: map[string]interface{}{ Extra: map[string]interface{}{
artifact.ExtraExt: ".exe", artifact.ExtraExt: ".exe",
@ -557,7 +617,10 @@ func TestBuild(t *testing.T) {
"testEnvs": []string{"TEST_T="}, "testEnvs": []string{"TEST_T="},
}, },
}, },
}) }
got := list.List()
testlib.RequireEqualArtifacts(t, expected, got)
modTimes := map[int64]bool{} modTimes := map[int64]bool{}
for _, bin := range ctx.Artifacts.List() { for _, bin := range ctx.Artifacts.List() {
@ -599,7 +662,7 @@ func TestBuildInvalidEnv(t *testing.T) {
}, testctx.WithCurrentTag("5.6.7")) }, testctx.WithCurrentTag("5.6.7"))
build := ctx.Config.Builds[0] build := ctx.Config.Builds[0]
err := Default.Build(ctx, build, api.Options{ err := Default.Build(ctx, build, api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
Name: build.Binary, Name: build.Binary,
Path: filepath.Join("dist", runtimeTarget, build.Binary), Path: filepath.Join("dist", runtimeTarget, build.Binary),
Ext: "", Ext: "",
@ -632,7 +695,7 @@ func TestBuildCodeInSubdir(t *testing.T) {
}, testctx.WithCurrentTag("5.6.7")) }, testctx.WithCurrentTag("5.6.7"))
build := ctx.Config.Builds[0] build := ctx.Config.Builds[0]
err = Default.Build(ctx, build, api.Options{ err = Default.Build(ctx, build, api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
Name: build.Binary, Name: build.Binary,
Path: filepath.Join("dist", runtimeTarget, build.Binary), Path: filepath.Join("dist", runtimeTarget, build.Binary),
Ext: "", Ext: "",
@ -660,7 +723,7 @@ func TestBuildWithDotGoDir(t *testing.T) {
}, testctx.WithCurrentTag("5.6.7")) }, testctx.WithCurrentTag("5.6.7"))
build := ctx.Config.Builds[0] build := ctx.Config.Builds[0]
require.NoError(t, Default.Build(ctx, build, api.Options{ require.NoError(t, Default.Build(ctx, build, api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
Name: build.Binary, Name: build.Binary,
Path: filepath.Join("dist", runtimeTarget, build.Binary), Path: filepath.Join("dist", runtimeTarget, build.Binary),
Ext: "", Ext: "",
@ -686,7 +749,7 @@ func TestBuildFailed(t *testing.T) {
}, },
}, testctx.WithCurrentTag("5.6.7")) }, testctx.WithCurrentTag("5.6.7"))
err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: "darwin_amd64", Target: mustParse(t, "darwin_amd64"),
}) })
require.ErrorContains(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`) require.ErrorContains(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
require.Empty(t, ctx.Artifacts.List()) require.Empty(t, ctx.Artifacts.List())
@ -709,7 +772,7 @@ func TestRunInvalidAsmflags(t *testing.T) {
}, },
}, testctx.WithCurrentTag("5.6.7")) }, testctx.WithCurrentTag("5.6.7"))
err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}) })
testlib.RequireTemplateError(t, err) testlib.RequireTemplateError(t, err)
} }
@ -731,7 +794,7 @@ func TestRunInvalidGcflags(t *testing.T) {
}, },
}, testctx.WithCurrentTag("5.6.7")) }, testctx.WithCurrentTag("5.6.7"))
err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}) })
testlib.RequireTemplateError(t, err) testlib.RequireTemplateError(t, err)
} }
@ -754,7 +817,7 @@ func TestRunInvalidLdflags(t *testing.T) {
}, },
}, testctx.WithCurrentTag("5.6.7")) }, testctx.WithCurrentTag("5.6.7"))
err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}) })
testlib.RequireTemplateError(t, err) testlib.RequireTemplateError(t, err)
} }
@ -776,7 +839,7 @@ func TestRunInvalidFlags(t *testing.T) {
}, },
}) })
err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{ err := Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}) })
testlib.RequireTemplateError(t, err) testlib.RequireTemplateError(t, err)
} }
@ -795,28 +858,28 @@ func TestRunPipeWithoutMainFunc(t *testing.T) {
ctx := newCtx(t) ctx := newCtx(t)
ctx.Config.Builds[0].Main = "" ctx.Config.Builds[0].Main = ""
require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}), errNoMain{"no-main"}.Error()) }), errNoMain{"no-main"}.Error())
}) })
t.Run("not main.go", func(t *testing.T) { t.Run("not main.go", func(t *testing.T) {
ctx := newCtx(t) ctx := newCtx(t)
ctx.Config.Builds[0].Main = "foo.go" ctx.Config.Builds[0].Main = "foo.go"
require.ErrorIs(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.ErrorIs(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}), os.ErrNotExist) }), os.ErrNotExist)
}) })
t.Run("glob", func(t *testing.T) { t.Run("glob", func(t *testing.T) {
ctx := newCtx(t) ctx := newCtx(t)
ctx.Config.Builds[0].Main = "." ctx.Config.Builds[0].Main = "."
require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}), errNoMain{"no-main"}.Error()) }), errNoMain{"no-main"}.Error())
}) })
t.Run("fixed main.go", func(t *testing.T) { t.Run("fixed main.go", func(t *testing.T) {
ctx := newCtx(t) ctx := newCtx(t)
ctx.Config.Builds[0].Main = "main.go" ctx.Config.Builds[0].Main = "main.go"
require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}), errNoMain{"no-main"}.Error()) }), errNoMain{"no-main"}.Error())
}) })
t.Run("using gomod.proxy", func(t *testing.T) { t.Run("using gomod.proxy", func(t *testing.T) {
@ -827,7 +890,7 @@ func TestRunPipeWithoutMainFunc(t *testing.T) {
ctx.Config.Builds[0].UnproxiedDir = "." ctx.Config.Builds[0].UnproxiedDir = "."
ctx.Config.Builds[0].UnproxiedMain = "." ctx.Config.Builds[0].UnproxiedMain = "."
require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
}), errNoMain{"no-main"}.Error()) }), errNoMain{"no-main"}.Error())
}) })
} }
@ -848,7 +911,7 @@ func TestBuildTests(t *testing.T) {
build, err := Default.WithDefaults(ctx.Config.Builds[0]) build, err := Default.WithDefaults(ctx.Config.Builds[0])
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, Default.Build(ctx, build, api.Options{ require.NoError(t, Default.Build(ctx, build, api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
})) }))
} }
@ -899,7 +962,7 @@ import _ "github.com/goreleaser/goreleaser"
}) })
require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
})) }))
} }
@ -929,19 +992,19 @@ func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
t.Run("empty", func(t *testing.T) { t.Run("empty", func(t *testing.T) {
ctx.Config.Builds[0].Main = "" ctx.Config.Builds[0].Main = ""
require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
})) }))
}) })
t.Run("foo.go", func(t *testing.T) { t.Run("foo.go", func(t *testing.T) {
ctx.Config.Builds[0].Main = "foo.go" ctx.Config.Builds[0].Main = "foo.go"
require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
})) }))
}) })
t.Run("glob", func(t *testing.T) { t.Run("glob", func(t *testing.T) {
ctx.Config.Builds[0].Main = "." ctx.Config.Builds[0].Main = "."
require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{ require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
})) }))
}) })
} }
@ -1099,7 +1162,7 @@ func TestBuildModTimestamp(t *testing.T) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
err := Default.Build(ctx, build, api.Options{ err := Default.Build(ctx, build, api.Options{
Target: target, Target: mustParse(t, runtimeTarget),
Name: bin + ext, Name: bin + ext,
Path: filepath.Join(folder, "dist", target, bin+ext), Path: filepath.Join(folder, "dist", target, bin+ext),
Ext: ext, Ext: ext,
@ -1131,11 +1194,10 @@ func TestBuildGoBuildLine(t *testing.T) {
) )
options := api.Options{ options := api.Options{
Path: ctx.Config.Builds[0].Binary, Path: ctx.Config.Builds[0].Binary,
Goos: "linux", Target: mustParse(t, "linux_amd64"),
Goarch: "amd64",
} }
dets, err := withOverrides(ctx, build, options) dets, err := withOverrides(ctx, build, options.Target.(Target))
require.NoError(t, err) require.NoError(t, err)
line, err := buildGoBuildLine( line, err := buildGoBuildLine(
@ -1298,10 +1360,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_"+arch),
Goos: "linux",
Goarch: arch,
},
) )
require.NoError(t, err) require.NoError(t, err)
require.ElementsMatch(t, dets.Ldflags, []string{"overridden"}) require.ElementsMatch(t, dets.Ldflags, []string{"overridden"})
@ -1326,10 +1385,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_amd64"),
Goos: "linux",
Goarch: "amd64",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1358,10 +1414,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, runtimeTarget),
Goos: runtime.GOOS,
Goarch: runtime.GOARCH,
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1380,10 +1433,7 @@ func TestOverrides(t *testing.T) {
Goos: "{{ .Runtime.Goos }", Goos: "{{ .Runtime.Goos }",
}, },
}, },
}, api.Options{ }, mustParse(t, runtimeTarget),
Goos: runtime.GOOS,
Goarch: runtime.GOARCH,
},
) )
testlib.RequireTemplateError(t, err) testlib.RequireTemplateError(t, err)
}) })
@ -1405,11 +1455,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_arm64_v8.0"),
Goos: "linux",
Goarch: "arm64",
Goarm64: "v8.0",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1434,11 +1480,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_arm64_v8.0"),
Goos: "linux",
Goarch: "arm64",
Goarm64: "v8.0",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1464,11 +1506,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_arm_6"),
Goos: "linux",
Goarch: "arm",
Goarm: "6",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1493,11 +1531,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_arm_6"),
Goos: "linux",
Goarch: "arm",
Goarm: "6",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1523,11 +1557,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_mips_softfloat"),
Goos: "linux",
Goarch: "mips",
Gomips: "softfloat",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1552,11 +1582,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_mips_hardfloat"),
Goos: "linux",
Goarch: "mips",
Gomips: "hardfloat",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1582,11 +1608,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_riscv64_rva22u64"),
Goos: "linux",
Goarch: "riscv64",
Goriscv64: "rva22u64",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1612,11 +1634,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_riscv64_rva22u64"),
Goos: "linux",
Goarch: "riscv64",
Goriscv64: "rva22u64",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1642,11 +1660,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_386_sse2"),
Goos: "linux",
Goarch: "386",
Go386: "sse2",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1672,11 +1686,7 @@ func TestOverrides(t *testing.T) {
}, },
}, },
}, },
}, api.Options{ }, mustParse(t, "linux_386_sse2"),
Goos: "linux",
Goarch: "386",
Go386: "sse2",
},
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, config.BuildDetails{ require.Equal(t, config.BuildDetails{
@ -1733,7 +1743,7 @@ func TestInvalidGoBinaryTpl(t *testing.T) {
}) })
build := ctx.Config.Builds[0] build := ctx.Config.Builds[0]
testlib.RequireTemplateError(t, Default.Build(ctx, build, api.Options{ testlib.RequireTemplateError(t, Default.Build(ctx, build, api.Options{
Target: runtimeTarget, Target: mustParse(t, runtimeTarget),
Name: build.Binary, Name: build.Binary,
Path: filepath.Join("dist", runtimeTarget, build.Binary), Path: filepath.Join("dist", runtimeTarget, build.Binary),
Ext: "", Ext: "",
@ -1794,3 +1804,10 @@ func writeTest(t *testing.T, folder string) {
0o666, 0o666,
)) ))
} }
func mustParse(tb testing.TB, target string) Target {
tb.Helper()
got, err := Default.Parse(target)
require.NoError(tb, err)
return got.(Target)
}

View File

@ -0,0 +1,63 @@
package golang
import (
"github.com/goreleaser/goreleaser/v2/internal/tmpl"
"github.com/goreleaser/goreleaser/v2/pkg/config"
)
func formatTarget(o config.BuildDetailsOverride) string {
target := o.Goos + "_" + o.Goarch
if extra := o.Goamd64 + o.Go386 + o.Goarm + o.Goarm64 + o.Gomips + o.Goppc64 + o.Goriscv64; extra != "" {
target += "_" + extra
}
return target
}
// Target is a Go build target.
type Target struct {
Target string
Goos string
Goarch string
Goamd64 string
Go386 string
Goarm string
Goarm64 string
Gomips string
Goppc64 string
Goriscv64 string
}
// Fields implements build.Target.
func (t Target) Fields() map[string]string {
return map[string]string{
tmpl.KeyOS: t.Goos,
tmpl.KeyArch: t.Goarch,
tmpl.KeyAmd64: t.Goamd64,
tmpl.Key386: t.Go386,
tmpl.KeyArm: t.Goarm,
tmpl.KeyArm64: t.Goarm64,
tmpl.KeyMips: t.Gomips,
tmpl.KeyPpc64: t.Goppc64,
tmpl.KeyRiscv64: t.Goriscv64,
}
}
// String implements fmt.Stringer.
func (t Target) String() string {
return t.Target
}
func (t Target) env() []string {
return []string{
"GOOS=" + t.Goos,
"GOARCH=" + t.Goarch,
"GOAMD64=" + t.Goamd64,
"GO386=" + t.Go386,
"GOARM=" + t.Goarm,
"GOARM64=" + t.Goarm64,
"GOMIPS=" + t.Gomips,
"GOMIPS64=" + t.Gomips,
"GOPPC64=" + t.Goppc64,
"GORISCV64=" + t.Goriscv64,
}
}

View File

@ -165,34 +165,15 @@ func buildOptionsForTarget(ctx *context.Context, build config.Build, target stri
return nil, fmt.Errorf("%s is not a valid build target", target) return nil, fmt.Errorf("%s is not a valid build target", target)
} }
goos := parts[0]
goarch := parts[1]
buildOpts := builders.Options{ buildOpts := builders.Options{
Target: target,
Ext: ext, Ext: ext,
Goos: goos,
Goarch: goarch,
} }
if len(parts) > 2 { t, err := builders.For(build.Builder).Parse(target)
//nolint:gocritic if err != nil {
if strings.HasPrefix(goarch, "amd64") { return nil, err
buildOpts.Goamd64 = parts[2]
} else if goarch == "386" {
buildOpts.Go386 = parts[2]
} else if strings.HasPrefix(goarch, "arm64") {
buildOpts.Goarm64 = parts[2]
} else if strings.HasPrefix(goarch, "arm") {
buildOpts.Goarm = parts[2]
} else if strings.HasPrefix(goarch, "mips") {
buildOpts.Gomips = parts[2]
} else if strings.HasPrefix(goarch, "ppc64") {
buildOpts.Goppc64 = parts[2]
} else if goarch == "riscv64" {
buildOpts.Goriscv64 = parts[2]
}
} }
buildOpts.Target = t
bin, err := tmpl.New(ctx).WithBuildOptions(buildOpts).Apply(build.Binary) bin, err := tmpl.New(ctx).WithBuildOptions(buildOpts).Apply(build.Binary)
if err != nil { if err != nil {
@ -200,7 +181,7 @@ func buildOptionsForTarget(ctx *context.Context, build config.Build, target stri
} }
name := bin + ext name := bin + ext
dir := fmt.Sprintf("%s_%s", build.ID, target) dir := fmt.Sprintf("%s_%s", build.ID, t)
noUnique, err := tmpl.New(ctx).Bool(build.NoUniqueDistDir) noUnique, err := tmpl.New(ctx).Bool(build.NoUniqueDistDir)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"github.com/goreleaser/goreleaser/v2/internal/artifact" "github.com/goreleaser/goreleaser/v2/internal/artifact"
@ -24,11 +25,34 @@ var (
errFailedDefault = errors.New("fake builder defaults failed") errFailedDefault = errors.New("fake builder defaults failed")
) )
type fakeTarget struct {
target string
}
// String implements build.Target.
func (f fakeTarget) String() string {
return f.target
}
// Fields implements build.Target.
func (f fakeTarget) Fields() map[string]string {
os, arch, _ := strings.Cut(f.target, "_")
return map[string]string{
tmpl.KeyOS: os,
tmpl.KeyArch: arch,
}
}
type fakeBuilder struct { type fakeBuilder struct {
fail bool fail bool
failDefault bool failDefault bool
} }
// Parse implements build.Builder.
func (f *fakeBuilder) Parse(target string) (api.Target, error) {
return fakeTarget{target}, nil
}
func (f *fakeBuilder) WithDefaults(build config.Build) (config.Build, error) { func (f *fakeBuilder) WithDefaults(build config.Build) (config.Build, error) {
if f.failDefault { if f.failDefault {
return build, errFailedDefault return build, errFailedDefault
@ -585,7 +609,7 @@ func TestBuildOptionsForTarget(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
build config.Build build config.Build
expectedOpts *api.Options expectedOpts api.Options
expectedErr string expectedErr string
}{ }{
{ {
@ -597,13 +621,9 @@ func TestBuildOptionsForTarget(t *testing.T) {
"linux_amd64", "linux_amd64",
}, },
}, },
expectedOpts: &api.Options{ expectedOpts: api.Options{
Name: "testbinary", Name: "testbinary",
Path: filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary"), Path: filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary"),
Target: "linux_amd64_v1",
Goos: "linux",
Goarch: "amd64",
Goamd64: "v1",
}, },
}, },
{ {
@ -615,13 +635,9 @@ func TestBuildOptionsForTarget(t *testing.T) {
"linux_amd64", "linux_amd64",
}, },
}, },
expectedOpts: &api.Options{ expectedOpts: api.Options{
Name: "testbinary_linux_amd64", Name: "testbinary_linux_amd64",
Path: filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary_linux_amd64"), Path: filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary_linux_amd64"),
Target: "linux_amd64_v1",
Goos: "linux",
Goarch: "amd64",
Goamd64: "v1",
}, },
}, },
{ {
@ -634,13 +650,9 @@ func TestBuildOptionsForTarget(t *testing.T) {
}, },
NoUniqueDistDir: `{{ printf "true"}}`, NoUniqueDistDir: `{{ printf "true"}}`,
}, },
expectedOpts: &api.Options{ expectedOpts: api.Options{
Name: "distpath/linux/amd64/testbinary", Name: "distpath/linux/amd64/testbinary",
Path: filepath.Join(tmpDir, "distpath", "linux", "amd64", "testbinary"), Path: filepath.Join(tmpDir, "distpath", "linux", "amd64", "testbinary"),
Target: "linux_amd64_v1",
Goos: "linux",
Goarch: "amd64",
Goamd64: "v1",
}, },
}, },
{ {
@ -653,13 +665,9 @@ func TestBuildOptionsForTarget(t *testing.T) {
}, },
NoUniqueDistDir: `{{ printf "false"}}`, NoUniqueDistDir: `{{ printf "false"}}`,
}, },
expectedOpts: &api.Options{ expectedOpts: api.Options{
Name: "testbinary", Name: "testbinary",
Path: filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary"), Path: filepath.Join(tmpDir, "testid_linux_amd64_v1", "testbinary"),
Target: "linux_amd64_v1",
Goos: "linux",
Goarch: "amd64",
Goamd64: "v1",
}, },
}, },
{ {
@ -671,13 +679,9 @@ func TestBuildOptionsForTarget(t *testing.T) {
"linux_arm_6", "linux_arm_6",
}, },
}, },
expectedOpts: &api.Options{ expectedOpts: api.Options{
Name: "testbinary", Name: "testbinary",
Path: filepath.Join(tmpDir, "testid_linux_arm_6", "testbinary"), Path: filepath.Join(tmpDir, "testid_linux_arm_6", "testbinary"),
Target: "linux_arm_6",
Goos: "linux",
Goarch: "arm",
Goarm: "6",
}, },
}, },
{ {
@ -689,13 +693,9 @@ func TestBuildOptionsForTarget(t *testing.T) {
"linux_mips_softfloat", "linux_mips_softfloat",
}, },
}, },
expectedOpts: &api.Options{ expectedOpts: api.Options{
Name: "testbinary", Name: "testbinary",
Path: filepath.Join(tmpDir, "testid_linux_mips_softfloat", "testbinary"), Path: filepath.Join(tmpDir, "testid_linux_mips_softfloat", "testbinary"),
Target: "linux_mips_softfloat",
Goos: "linux",
Goarch: "mips",
Gomips: "softfloat",
}, },
}, },
{ {
@ -707,13 +707,9 @@ func TestBuildOptionsForTarget(t *testing.T) {
"linux_amd64_v3", "linux_amd64_v3",
}, },
}, },
expectedOpts: &api.Options{ expectedOpts: api.Options{
Name: "testbinary", Name: "testbinary",
Path: filepath.Join(tmpDir, "testid_linux_amd64_v3", "testbinary"), Path: filepath.Join(tmpDir, "testid_linux_amd64_v3", "testbinary"),
Target: "linux_amd64_v3",
Goos: "linux",
Goarch: "amd64",
Goamd64: "v3",
}, },
}, },
} }
@ -728,7 +724,8 @@ func TestBuildOptionsForTarget(t *testing.T) {
opts, err := buildOptionsForTarget(ctx, ctx.Config.Builds[0], ctx.Config.Builds[0].Targets[0]) opts, err := buildOptionsForTarget(ctx, ctx.Config.Builds[0], ctx.Config.Builds[0].Targets[0])
if tc.expectedErr == "" { if tc.expectedErr == "" {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, tc.expectedOpts, opts) opts.Target = nil
require.Equal(t, tc.expectedOpts, *opts)
} else { } else {
require.EqualError(t, err, tc.expectedErr) require.EqualError(t, err, tc.expectedErr)
} }

View File

@ -0,0 +1,14 @@
package universalbinary
import "github.com/goreleaser/goreleaser/v2/internal/tmpl"
type unitarget struct{}
func (unitarget) String() string { return "darwin_all" }
func (unitarget) Fields() map[string]string {
return map[string]string{
tmpl.KeyOS: "darwin",
tmpl.KeyArch: "all",
}
}

View File

@ -55,9 +55,7 @@ func (Pipe) Run(ctx *context.Context) error {
for _, unibin := range ctx.Config.UniversalBinaries { for _, unibin := range ctx.Config.UniversalBinaries {
g.Go(func() error { g.Go(func() error {
opts := build.Options{ opts := build.Options{
Target: "darwin_all", Target: unitarget{},
Goos: "darwin",
Goarch: "all",
} }
if !skips.Any(ctx, skips.PreBuildHooks) { if !skips.Any(ctx, skips.PreBuildHooks) {
if err := runHook(ctx, &opts, unibin.Hooks.Pre); err != nil { if err := runHook(ctx, &opts, unibin.Hooks.Pre); err != nil {

View File

@ -179,7 +179,7 @@ func TestRun(t *testing.T) {
}, },
Post: []config.Hook{ Post: []config.Hook{
{Cmd: testlib.Touch(post)}, {Cmd: testlib.Touch(post)},
{Cmd: testlib.ShC(`echo "{{ .Name }} {{ .Os }} {{ .Arch }} {{ .Arm }} {{ .Target }} {{ .Ext }}" > {{ .Path }}.post`), Output: true}, {Cmd: testlib.ShC(`echo "{{ .Name }} {{ .Os }} {{ .Arch }} {{ .Target }} {{ .Ext }}" > {{ .Path }}.post`), Output: true},
}, },
}, },
}, },
@ -213,7 +213,7 @@ func TestRun(t *testing.T) {
Post: []config.Hook{ Post: []config.Hook{
{Cmd: testlib.Touch(post)}, {Cmd: testlib.Touch(post)},
{ {
Cmd: testlib.ShC(`echo "{{ .Name }} {{ .Os }} {{ .Arch }} {{ .Arm }} {{ .Target }} {{ .Ext }}" > {{ .Path }}.post`), Cmd: testlib.ShC(`echo "{{ .Name }} {{ .Os }} {{ .Arch }} {{ .Target }} {{ .Ext }}" > {{ .Path }}.post`),
Output: true, Output: true,
}, },
}, },

View File

@ -0,0 +1,30 @@
package testlib
import (
"slices"
"strings"
"testing"
"github.com/goreleaser/goreleaser/v2/internal/artifact"
"github.com/stretchr/testify/require"
)
func RequireEqualArtifacts(tb testing.TB, expected, got []*artifact.Artifact) {
tb.Helper()
slices.SortFunc(expected, artifactSort)
slices.SortFunc(got, artifactSort)
require.Equal(tb, filenames(expected), filenames(got))
require.Equal(tb, expected, got)
}
func artifactSort(a, b *artifact.Artifact) int {
return strings.Compare(a.Path, b.Path)
}
func filenames(ts []*artifact.Artifact) []string {
result := make([]string, len(ts))
for i, t := range ts {
result[i] = t.Path
}
return result
}

View File

@ -28,8 +28,21 @@ type Template struct {
// Fields that will be available to the template engine. // Fields that will be available to the template engine.
type Fields map[string]interface{} type Fields map[string]interface{}
// Template fields names used in build targets and more.
const (
KeyOS = "Os"
KeyArch = "Arch"
KeyAmd64 = "Amd64"
Key386 = "I386"
KeyArm = "Arm"
KeyArm64 = "Arm64"
KeyMips = "Mips"
KeyPpc64 = "Ppc64"
KeyRiscv64 = "Riscv64"
)
// general keys.
const ( const (
// general keys.
projectName = "ProjectName" projectName = "ProjectName"
version = "Version" version = "Version"
rawVersion = "RawVersion" rawVersion = "RawVersion"
@ -65,23 +78,18 @@ const (
modulePath = "ModulePath" modulePath = "ModulePath"
releaseNotes = "ReleaseNotes" releaseNotes = "ReleaseNotes"
runtimeK = "Runtime" runtimeK = "Runtime"
)
// artifact-only keys. // artifact-only keys.
osKey = "Os" const (
arch = "Arch"
amd64 = "Amd64"
go386 = "I386"
arm = "Arm"
arm64 = "Arm64"
mips = "Mips"
ppc64 = "Ppc64"
riscv64 = "Riscv64"
binary = "Binary" binary = "Binary"
artifactName = "ArtifactName" artifactName = "ArtifactName"
artifactExt = "ArtifactExt" artifactExt = "ArtifactExt"
artifactPath = "ArtifactPath" artifactPath = "ArtifactPath"
)
// build keys. // build keys.
const (
name = "Name" name = "Name"
ext = "Ext" ext = "Ext"
path = "Path" path = "Path"
@ -177,15 +185,15 @@ func (t *Template) WithEnv(e map[string]string) *Template {
// WithArtifact populates Fields from the artifact. // WithArtifact populates Fields from the artifact.
func (t *Template) WithArtifact(a *artifact.Artifact) *Template { func (t *Template) WithArtifact(a *artifact.Artifact) *Template {
return t.WithExtraFields(Fields{ return t.WithExtraFields(Fields{
osKey: a.Goos, KeyOS: a.Goos,
arch: a.Goarch, KeyArch: a.Goarch,
amd64: a.Goamd64, KeyAmd64: a.Goamd64,
go386: a.Go386, Key386: a.Go386,
arm: a.Goarm, KeyArm: a.Goarm,
arm64: a.Goarm64, KeyArm64: a.Goarm64,
mips: a.Gomips, KeyMips: a.Gomips,
ppc64: a.Goppc64, KeyPpc64: a.Goppc64,
riscv64: a.Goriscv64, KeyRiscv64: a.Goriscv64,
binary: artifact.ExtraOr(*a, binary, t.fields[projectName].(string)), binary: artifact.ExtraOr(*a, binary, t.fields[projectName].(string)),
artifactName: a.Name, artifactName: a.Name,
artifactExt: artifact.ExtraOr(*a, artifact.ExtraExt, ""), artifactExt: artifact.ExtraOr(*a, artifact.ExtraExt, ""),
@ -198,21 +206,29 @@ func (t *Template) WithBuildOptions(opts build.Options) *Template {
} }
func buildOptsToFields(opts build.Options) Fields { func buildOptsToFields(opts build.Options) Fields {
return Fields{ f := Fields{
target: opts.Target, target: opts.Target.String(),
ext: opts.Ext, ext: opts.Ext,
name: opts.Name, name: opts.Name,
path: opts.Path, path: opts.Path,
osKey: opts.Goos,
arch: opts.Goarch, // set them all to empty, which should prevent breaking templates.
amd64: opts.Goamd64, // the .Fields() call will override whichever values are actually
go386: opts.Go386, // available.
arm: opts.Goarm, KeyOS: "",
arm64: opts.Goarm64, KeyArch: "",
mips: opts.Gomips, KeyAmd64: "",
ppc64: opts.Goppc64, Key386: "",
riscv64: opts.Goriscv64, KeyArm: "",
KeyArm64: "",
KeyMips: "",
KeyPpc64: "",
KeyRiscv64: "",
} }
for k, v := range opts.Target.Fields() {
f[k] = v
}
return f
} }
// Bool Apply the given string, and converts it to a bool. // Bool Apply the given string, and converts it to a bool.

View File

@ -460,17 +460,21 @@ func TestInvalidMap(t *testing.T) {
} }
func TestWithBuildOptions(t *testing.T) { func TestWithBuildOptions(t *testing.T) {
// testtarget doesn ot set riscv64, it still should not fail to compile the template
ts := "{{.Name}}_{{.Path}}_{{.Ext}}_{{.Target}}_{{.Os}}_{{.Arch}}_{{.Amd64}}_{{.Arm}}_{{.Mips}}{{with .Riscv64}}{{.}}{{end}}"
out, err := New(testctx.New()).WithBuildOptions(build.Options{ out, err := New(testctx.New()).WithBuildOptions(build.Options{
Name: "name", Name: "name",
Path: "./path", Path: "./path",
Ext: ".ext", Ext: ".ext",
Target: testTarget{
Target: "target", Target: "target",
Goos: "os", Goos: "os",
Goarch: "arch", Goarch: "arch",
Goamd64: "amd64", Goamd64: "amd64",
Goarm: "arm", Goarm: "arm",
Gomips: "mips", Gomips: "mips",
}).Apply("{{.Name}}_{{.Path}}_{{.Ext}}_{{.Target}}_{{.Os}}_{{.Arch}}_{{.Amd64}}_{{.Arm}}_{{.Mips}}") },
}).Apply(ts)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "name_./path_.ext_target_os_arch_amd64_arm_mips", out) require.Equal(t, "name_./path_.ext_target_os_arch_amd64_arm_mips", out)
} }
@ -491,3 +495,25 @@ func TestReuseTpl(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "bar", s3) require.Equal(t, "bar", s3)
} }
type testTarget struct {
Target string
Goos string
Goarch string
Goamd64 string
Goarm string
Gomips string
}
func (t testTarget) String() string { return t.Target }
func (t testTarget) Fields() map[string]string {
return map[string]string{
target: t.Target,
KeyOS: t.Goos,
KeyArch: t.Goarch,
KeyAmd64: t.Goamd64,
KeyArm: t.Goarm,
KeyMips: t.Gomips,
}
}

View File

@ -31,20 +31,24 @@ type Options struct {
Name string Name string
Path string Path string
Ext string // with the leading `.`. Ext string // with the leading `.`.
Target string Target Target
Goos string }
Goarch string
Goamd64 string // Target represents a build target.
Go386 string //
Goarm string // Each Builder implementation can implement its own.
Goarm64 string type Target interface {
Gomips string // String returns the original target.
Goppc64 string String() string
Goriscv64 string
// Fields returns the template fields that will be available for this
// target (e.g. Os, Arch, etc).
Fields() map[string]string
} }
// Builder defines a builder. // Builder defines a builder.
type Builder interface { type Builder interface {
WithDefaults(build config.Build) (config.Build, error) WithDefaults(build config.Build) (config.Build, error)
Build(ctx *context.Context, build config.Build, options Options) error Build(ctx *context.Context, build config.Build, options Options) error
Parse(target string) (Target, error)
} }

View File

@ -8,8 +8,25 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type dummyTarget struct{}
// String implements Target.
func (d dummyTarget) String() string {
return "dummy"
}
// Fields implements Target.
func (d dummyTarget) Fields() map[string]string {
return nil
}
type dummy struct{} type dummy struct{}
// Parse implements Builder.
func (d *dummy) Parse(string) (Target, error) {
return dummyTarget{}, nil
}
func (*dummy) WithDefaults(build config.Build) (config.Build, error) { func (*dummy) WithDefaults(build config.Build) (config.Build, error) {
return build, nil return build, nil
} }