mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-19 20:57:53 +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>
229 lines
4.7 KiB
Go
229 lines
4.7 KiB
Go
package zig
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/caarlos0/log"
|
|
"github.com/goreleaser/goreleaser/v2/internal/artifact"
|
|
"github.com/goreleaser/goreleaser/v2/internal/gio"
|
|
"github.com/goreleaser/goreleaser/v2/internal/tmpl"
|
|
api "github.com/goreleaser/goreleaser/v2/pkg/build"
|
|
"github.com/goreleaser/goreleaser/v2/pkg/config"
|
|
"github.com/goreleaser/goreleaser/v2/pkg/context"
|
|
)
|
|
|
|
// Default builder instance.
|
|
//
|
|
//nolint:gochecknoglobals
|
|
var Default = &Builder{}
|
|
|
|
//nolint:gochecknoinits
|
|
func init() {
|
|
api.Register("zig", Default)
|
|
}
|
|
|
|
// Builder is golang builder.
|
|
type Builder struct{}
|
|
|
|
// Parse implements build.Builder.
|
|
func (b *Builder) Parse(target string) (api.Target, error) {
|
|
parts := strings.Split(target, "-")
|
|
if len(parts) < 2 {
|
|
return nil, fmt.Errorf("%s is not a valid build target", target)
|
|
}
|
|
|
|
t := Target{
|
|
Target: target,
|
|
Os: convertToGoos(parts[1]),
|
|
Arch: convertToGoarch(parts[0]),
|
|
}
|
|
|
|
if len(parts) > 2 {
|
|
t.Abi = parts[2]
|
|
}
|
|
|
|
return t, nil
|
|
}
|
|
|
|
// WithDefaults implements build.Builder.
|
|
func (b *Builder) WithDefaults(build config.Build) (config.Build, error) {
|
|
log.Warn("you are using the experimental Zig builder")
|
|
|
|
if len(build.Targets) == 0 {
|
|
build.Targets = defaultTargets()
|
|
}
|
|
|
|
if build.GoBinary == "" {
|
|
build.GoBinary = "zig"
|
|
}
|
|
|
|
if build.Command == "" {
|
|
build.Command = "build"
|
|
}
|
|
|
|
if build.Dir == "" {
|
|
build.Dir = "."
|
|
}
|
|
|
|
if build.Main != "" {
|
|
return build, errors.New("main is not used for zig")
|
|
}
|
|
|
|
if len(build.Ldflags) > 0 {
|
|
return build, errors.New("ldflags is not used for zig")
|
|
}
|
|
|
|
if len(slices.Concat(
|
|
build.Goos,
|
|
build.Goarch,
|
|
build.Goamd64,
|
|
build.Go386,
|
|
build.Goarm,
|
|
build.Goarm64,
|
|
build.Gomips,
|
|
build.Goppc64,
|
|
build.Goriscv64,
|
|
)) > 0 {
|
|
return build, errors.New("all go* fields are not used for zig, set targets instead")
|
|
}
|
|
|
|
if len(build.Ignore) > 0 {
|
|
return build, errors.New("ignore is not used for zig, set targets instead")
|
|
}
|
|
|
|
if build.Buildmode != "" {
|
|
return build, errors.New("buildmode is not used for zig")
|
|
}
|
|
|
|
if len(build.Tags) > 0 {
|
|
return build, errors.New("tags is not used for zig")
|
|
}
|
|
|
|
if len(build.Asmflags) > 0 {
|
|
return build, errors.New("asmtags is not used for zig")
|
|
}
|
|
|
|
if len(build.BuildDetailsOverrides) > 0 {
|
|
return build, errors.New("overrides is not used for zig")
|
|
}
|
|
|
|
for _, t := range build.Targets {
|
|
switch checkTarget(t) {
|
|
case targetValid:
|
|
// lfg
|
|
case targetBroken:
|
|
log.Warnf("target might not be supported: %s", t)
|
|
case targetInvalid:
|
|
return build, fmt.Errorf("invalid target: %s", t)
|
|
}
|
|
}
|
|
|
|
return build, nil
|
|
}
|
|
|
|
// Build implements build.Builder.
|
|
func (b *Builder) Build(ctx *context.Context, build config.Build, options api.Options) error {
|
|
prefix := filepath.Dir(options.Path)
|
|
options.Path = filepath.Join(prefix, "bin", options.Name)
|
|
|
|
t := options.Target.(Target)
|
|
a := &artifact.Artifact{
|
|
Type: artifact.Binary,
|
|
Path: options.Path,
|
|
Name: options.Name,
|
|
Goos: convertToGoos(t.Os),
|
|
Goarch: convertToGoarch(t.Arch),
|
|
Target: t.Target,
|
|
Extra: map[string]interface{}{
|
|
artifact.ExtraBinary: strings.TrimSuffix(filepath.Base(options.Path), options.Ext),
|
|
artifact.ExtraExt: options.Ext,
|
|
artifact.ExtraID: build.ID,
|
|
artifact.ExtraBuilder: "zig",
|
|
},
|
|
}
|
|
|
|
env := []string{}
|
|
env = append(env, ctx.Env.Strings()...)
|
|
|
|
tpl := tmpl.New(ctx).
|
|
WithBuildOptions(options).
|
|
WithEnvS(env).
|
|
WithArtifact(a)
|
|
|
|
zigbin, err := tpl.Apply(build.GoBinary)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
command := []string{
|
|
zigbin,
|
|
build.Command,
|
|
"-Dtarget=" + t.Target,
|
|
"-p", prefix,
|
|
}
|
|
|
|
for _, e := range build.Env {
|
|
ee, err := tpl.Apply(e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Debugf("env %q evaluated to %q", e, ee)
|
|
if ee != "" {
|
|
env = append(env, ee)
|
|
}
|
|
}
|
|
|
|
tpl = tpl.WithEnvS(env)
|
|
|
|
flags, err := processFlags(tpl, build.Flags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
command = append(command, flags...)
|
|
|
|
/* #nosec */
|
|
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
|
|
cmd.Env = env
|
|
cmd.Dir = build.Dir
|
|
log.Debug("running")
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("%w: %s", err, string(out))
|
|
}
|
|
if s := string(out); s != "" {
|
|
log.WithField("cmd", command).Info(s)
|
|
}
|
|
|
|
// TODO: move this to outside builder for both go and zig
|
|
modTimestamp, err := tpl.Apply(build.ModTimestamp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := gio.Chtimes(a.Path, modTimestamp); err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx.Artifacts.Add(a)
|
|
return nil
|
|
}
|
|
|
|
func processFlags(tpl *tmpl.Template, flags []string) ([]string, error) {
|
|
var processed []string
|
|
for _, rawFlag := range flags {
|
|
flag, err := tpl.Apply(rawFlag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if flag == "" {
|
|
continue
|
|
}
|
|
processed = append(processed, flag)
|
|
}
|
|
return processed, nil
|
|
}
|