mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-25 21:29:14 +02:00
Continuing on #5308 and #5307, this finally adds Zig support to GoReleaser! Things are very raw still, plenty of use cases to test (please do test on your project if you can), but it does work at least for simple projects! A release done by this: https://github.com/goreleaser/example-zig/releases/tag/v0.1.0 --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
232 lines
5.4 KiB
Go
232 lines
5.4 KiB
Go
// Package build provides a pipe that can build binaries for several
|
|
// languages.
|
|
package build
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/caarlos0/go-shellwords"
|
|
"github.com/caarlos0/log"
|
|
"github.com/goreleaser/goreleaser/v2/internal/ids"
|
|
"github.com/goreleaser/goreleaser/v2/internal/semerrgroup"
|
|
"github.com/goreleaser/goreleaser/v2/internal/shell"
|
|
"github.com/goreleaser/goreleaser/v2/internal/skips"
|
|
"github.com/goreleaser/goreleaser/v2/internal/tmpl"
|
|
builders "github.com/goreleaser/goreleaser/v2/pkg/build"
|
|
"github.com/goreleaser/goreleaser/v2/pkg/config"
|
|
"github.com/goreleaser/goreleaser/v2/pkg/context"
|
|
|
|
// langs to init.
|
|
_ "github.com/goreleaser/goreleaser/v2/internal/builders/golang"
|
|
_ "github.com/goreleaser/goreleaser/v2/internal/builders/zig"
|
|
)
|
|
|
|
// Pipe for build.
|
|
type Pipe struct{}
|
|
|
|
func (Pipe) String() string {
|
|
return "building binaries"
|
|
}
|
|
|
|
// Run the pipe.
|
|
func (Pipe) Run(ctx *context.Context) error {
|
|
g := semerrgroup.New(ctx.Parallelism)
|
|
for _, build := range ctx.Config.Builds {
|
|
skip, err := tmpl.New(ctx).Bool(build.Skip)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if skip {
|
|
log.WithField("id", build.ID).Info("skip is set")
|
|
continue
|
|
}
|
|
log.WithField("build", build).Debug("building")
|
|
runPipeOnBuild(ctx, g, build)
|
|
}
|
|
return g.Wait()
|
|
}
|
|
|
|
// Default sets the pipe defaults.
|
|
func (Pipe) Default(ctx *context.Context) error {
|
|
ids := ids.New("builds")
|
|
for i, build := range ctx.Config.Builds {
|
|
build, err := buildWithDefaults(ctx, build)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctx.Config.Builds[i] = build
|
|
ids.Inc(ctx.Config.Builds[i].ID)
|
|
}
|
|
if len(ctx.Config.Builds) == 0 {
|
|
build, err := buildWithDefaults(ctx, config.Build{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctx.Config.Builds = []config.Build{build}
|
|
}
|
|
return ids.Validate()
|
|
}
|
|
|
|
func buildWithDefaults(ctx *context.Context, build config.Build) (config.Build, error) {
|
|
if build.Builder == "" {
|
|
build.Builder = "go"
|
|
}
|
|
if build.Binary == "" {
|
|
build.Binary = ctx.Config.ProjectName
|
|
}
|
|
if build.ID == "" {
|
|
build.ID = ctx.Config.ProjectName
|
|
}
|
|
for k, v := range build.Env {
|
|
build.Env[k] = os.ExpandEnv(v)
|
|
}
|
|
return builders.For(build.Builder).WithDefaults(build)
|
|
}
|
|
|
|
func runPipeOnBuild(ctx *context.Context, g semerrgroup.Group, build config.Build) {
|
|
for _, target := range filter(ctx, build.Targets) {
|
|
g.Go(func() error {
|
|
opts, err := buildOptionsForTarget(ctx, build, target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !skips.Any(ctx, skips.PreBuildHooks) {
|
|
if err := runHook(ctx, *opts, build.Env, build.Hooks.Pre); err != nil {
|
|
return fmt.Errorf("pre hook failed: %w", err)
|
|
}
|
|
}
|
|
if err := doBuild(ctx, build, *opts); err != nil {
|
|
return err
|
|
}
|
|
if !skips.Any(ctx, skips.PostBuildHooks) {
|
|
if err := runHook(ctx, *opts, build.Env, build.Hooks.Post); err != nil {
|
|
return fmt.Errorf("post hook failed: %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
|
|
func runHook(ctx *context.Context, opts builders.Options, buildEnv []string, hooks config.Hooks) error {
|
|
if len(hooks) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, hook := range hooks {
|
|
var env []string
|
|
|
|
env = append(env, ctx.Env.Strings()...)
|
|
for _, rawEnv := range append(buildEnv, hook.Env...) {
|
|
e, err := tmpl.New(ctx).WithBuildOptions(opts).Apply(rawEnv)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
env = append(env, e)
|
|
}
|
|
|
|
dir, err := tmpl.New(ctx).WithBuildOptions(opts).Apply(hook.Dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sh, err := tmpl.New(ctx).WithBuildOptions(opts).
|
|
WithEnvS(env).
|
|
Apply(hook.Cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.WithField("hook", sh).Info("running hook")
|
|
cmd, err := shellwords.Parse(sh)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := shell.Run(ctx, dir, cmd, env, hook.Output); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func doBuild(ctx *context.Context, build config.Build, opts builders.Options) error {
|
|
return builders.For(build.Builder).Build(ctx, build, opts)
|
|
}
|
|
|
|
func buildOptionsForTarget(ctx *context.Context, build config.Build, target string) (*builders.Options, error) {
|
|
ext := extFor(target, build.BuildDetails)
|
|
buildOpts := builders.Options{
|
|
Ext: ext,
|
|
}
|
|
|
|
t, err := builders.For(build.Builder).Parse(target)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buildOpts.Target = t
|
|
|
|
bin, err := tmpl.New(ctx).WithBuildOptions(buildOpts).Apply(build.Binary)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
name := bin + ext
|
|
dir := fmt.Sprintf("%s_%s", build.ID, t)
|
|
noUnique, err := tmpl.New(ctx).Bool(build.NoUniqueDistDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if noUnique {
|
|
dir = ""
|
|
}
|
|
relpath := filepath.Join(ctx.Config.Dist, dir, name)
|
|
path, err := filepath.Abs(relpath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buildOpts.Path = path
|
|
buildOpts.Name = name
|
|
|
|
log.WithField("binary", relpath).Info("building")
|
|
return &buildOpts, nil
|
|
}
|
|
|
|
func extFor(target string, build config.BuildDetails) string {
|
|
// Configure the extensions for shared and static libraries - by default .so and .a respectively -
|
|
// with overrides for Windows (.dll for shared and .lib for static) and .dylib for macOS.
|
|
switch build.Buildmode {
|
|
case "c-shared":
|
|
if strings.Contains(target, "darwin") {
|
|
return ".dylib"
|
|
}
|
|
if strings.Contains(target, "windows") {
|
|
return ".dll"
|
|
}
|
|
if strings.Contains(target, "wasm") {
|
|
return ".wasm"
|
|
}
|
|
return ".so"
|
|
case "c-archive":
|
|
if strings.Contains(target, "windows") {
|
|
return ".lib"
|
|
}
|
|
return ".a"
|
|
}
|
|
|
|
if strings.Contains(target, "wasm") {
|
|
return ".wasm"
|
|
}
|
|
|
|
if strings.Contains(target, "windows") {
|
|
return ".exe"
|
|
}
|
|
|
|
return ""
|
|
}
|