1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-21 21:07:19 +02:00

229 lines
4.7 KiB
Go
Raw Permalink Normal View History

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
}