1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-30 04:50:45 +02:00

feat(build): rust support

This commit is contained in:
Carlos Alexandro Becker 2024-12-01 15:06:42 -03:00
parent b623d53583
commit 003e41cb77
No known key found for this signature in database
9 changed files with 676 additions and 2 deletions

View File

@ -39,6 +39,11 @@ func newInitCmd() *initCmd {
log.Info("project contains a 'build.zig', using default zig configuration")
return
}
if _, err := os.Stat("Cargo.toml"); err == nil {
root.lang = "rust"
log.Info("project contains a 'Cargo.toml', using default rust configuration")
return
}
},
RunE: func(_ *cobra.Command, _ []string) error {
if _, err := os.Stat(root.config); err == nil {
@ -56,6 +61,8 @@ func newInitCmd() *initCmd {
switch root.lang {
case "zig":
example = static.ZigExampleConfig
case "rust":
example = static.RustExampleConfig
case "go":
example = static.GoExampleConfig
default:
@ -88,7 +95,7 @@ func newInitCmd() *initCmd {
_ = cmd.RegisterFlagCompletionFunc(
"language",
cobra.FixedCompletions(
[]string{"go", "zig"},
[]string{"go", "rust", "zig"},
cobra.ShellCompDirectiveDefault,
),
)

View File

@ -0,0 +1,262 @@
aarch64-apple-darwin
aarch64-apple-ios
aarch64-apple-ios-macabi
aarch64-apple-ios-sim
aarch64-apple-tvos
aarch64-apple-tvos-sim
aarch64-apple-visionos
aarch64-apple-visionos-sim
aarch64-apple-watchos
aarch64-apple-watchos-sim
aarch64-fuchsia
aarch64-kmc-solid_asp3
aarch64-linux-android
aarch64-nintendo-switch-freestanding
aarch64-pc-windows-gnullvm
aarch64-pc-windows-msvc
aarch64-unknown-freebsd
aarch64-unknown-fuchsia
aarch64-unknown-hermit
aarch64-unknown-illumos
aarch64-unknown-linux-gnu
aarch64-unknown-linux-gnu_ilp32
aarch64-unknown-linux-musl
aarch64-unknown-linux-ohos
aarch64-unknown-netbsd
aarch64-unknown-none
aarch64-unknown-none-softfloat
aarch64-unknown-nto-qnx710
aarch64-unknown-openbsd
aarch64-unknown-redox
aarch64-unknown-teeos
aarch64-unknown-trusty
aarch64-unknown-uefi
aarch64-uwp-windows-msvc
aarch64-wrs-vxworks
aarch64_be-unknown-linux-gnu
aarch64_be-unknown-linux-gnu_ilp32
aarch64_be-unknown-netbsd
arm-linux-androideabi
arm-unknown-linux-gnueabi
arm-unknown-linux-gnueabihf
arm-unknown-linux-musleabi
arm-unknown-linux-musleabihf
arm64_32-apple-watchos
arm64e-apple-darwin
arm64e-apple-ios
arm64ec-pc-windows-msvc
armeb-unknown-linux-gnueabi
armebv7r-none-eabi
armebv7r-none-eabihf
armv4t-none-eabi
armv4t-unknown-linux-gnueabi
armv5te-none-eabi
armv5te-unknown-linux-gnueabi
armv5te-unknown-linux-musleabi
armv5te-unknown-linux-uclibceabi
armv6-unknown-freebsd
armv6-unknown-netbsd-eabihf
armv6k-nintendo-3ds
armv7-linux-androideabi
armv7-sony-vita-newlibeabihf
armv7-unknown-freebsd
armv7-unknown-linux-gnueabi
armv7-unknown-linux-gnueabihf
armv7-unknown-linux-musleabi
armv7-unknown-linux-musleabihf
armv7-unknown-linux-ohos
armv7-unknown-linux-uclibceabi
armv7-unknown-linux-uclibceabihf
armv7-unknown-netbsd-eabihf
armv7-unknown-trusty
armv7-wrs-vxworks-eabihf
armv7a-kmc-solid_asp3-eabi
armv7a-kmc-solid_asp3-eabihf
armv7a-none-eabi
armv7a-none-eabihf
armv7k-apple-watchos
armv7r-none-eabi
armv7r-none-eabihf
armv7s-apple-ios
armv8r-none-eabihf
avr-unknown-gnu-atmega328
bpfeb-unknown-none
bpfel-unknown-none
csky-unknown-linux-gnuabiv2
csky-unknown-linux-gnuabiv2hf
hexagon-unknown-linux-musl
hexagon-unknown-none-elf
i386-apple-ios
i586-pc-nto-qnx700
i586-pc-windows-msvc
i586-unknown-linux-gnu
i586-unknown-linux-musl
i586-unknown-netbsd
i686-apple-darwin
i686-linux-android
i686-pc-windows-gnu
i686-pc-windows-gnullvm
i686-pc-windows-msvc
i686-unknown-freebsd
i686-unknown-haiku
i686-unknown-hurd-gnu
i686-unknown-linux-gnu
i686-unknown-linux-musl
i686-unknown-netbsd
i686-unknown-openbsd
i686-unknown-redox
i686-unknown-uefi
i686-uwp-windows-gnu
i686-uwp-windows-msvc
i686-win7-windows-msvc
i686-wrs-vxworks
loongarch64-unknown-linux-gnu
loongarch64-unknown-linux-musl
loongarch64-unknown-none
loongarch64-unknown-none-softfloat
m68k-unknown-linux-gnu
mips-unknown-linux-gnu
mips-unknown-linux-musl
mips-unknown-linux-uclibc
mips64-openwrt-linux-musl
mips64-unknown-linux-gnuabi64
mips64-unknown-linux-muslabi64
mips64el-unknown-linux-gnuabi64
mips64el-unknown-linux-muslabi64
mipsel-sony-psp
mipsel-sony-psx
mipsel-unknown-linux-gnu
mipsel-unknown-linux-musl
mipsel-unknown-linux-uclibc
mipsel-unknown-netbsd
mipsel-unknown-none
mipsisa32r6-unknown-linux-gnu
mipsisa32r6el-unknown-linux-gnu
mipsisa64r6-unknown-linux-gnuabi64
mipsisa64r6el-unknown-linux-gnuabi64
msp430-none-elf
nvptx64-nvidia-cuda
powerpc-unknown-freebsd
powerpc-unknown-linux-gnu
powerpc-unknown-linux-gnuspe
powerpc-unknown-linux-musl
powerpc-unknown-linux-muslspe
powerpc-unknown-netbsd
powerpc-unknown-openbsd
powerpc-wrs-vxworks
powerpc-wrs-vxworks-spe
powerpc64-ibm-aix
powerpc64-unknown-freebsd
powerpc64-unknown-linux-gnu
powerpc64-unknown-linux-musl
powerpc64-unknown-openbsd
powerpc64-wrs-vxworks
powerpc64le-unknown-freebsd
powerpc64le-unknown-linux-gnu
powerpc64le-unknown-linux-musl
riscv32gc-unknown-linux-gnu
riscv32gc-unknown-linux-musl
riscv32i-unknown-none-elf
riscv32im-risc0-zkvm-elf
riscv32im-unknown-none-elf
riscv32ima-unknown-none-elf
riscv32imac-esp-espidf
riscv32imac-unknown-none-elf
riscv32imac-unknown-nuttx-elf
riscv32imac-unknown-xous-elf
riscv32imafc-esp-espidf
riscv32imafc-unknown-none-elf
riscv32imafc-unknown-nuttx-elf
riscv32imc-esp-espidf
riscv32imc-unknown-none-elf
riscv32imc-unknown-nuttx-elf
riscv64-linux-android
riscv64gc-unknown-freebsd
riscv64gc-unknown-fuchsia
riscv64gc-unknown-hermit
riscv64gc-unknown-linux-gnu
riscv64gc-unknown-linux-musl
riscv64gc-unknown-netbsd
riscv64gc-unknown-none-elf
riscv64gc-unknown-nuttx-elf
riscv64gc-unknown-openbsd
riscv64imac-unknown-none-elf
riscv64imac-unknown-nuttx-elf
s390x-unknown-linux-gnu
s390x-unknown-linux-musl
sparc-unknown-linux-gnu
sparc-unknown-none-elf
sparc64-unknown-linux-gnu
sparc64-unknown-netbsd
sparc64-unknown-openbsd
sparcv9-sun-solaris
thumbv4t-none-eabi
thumbv5te-none-eabi
thumbv6m-none-eabi
thumbv6m-nuttx-eabi
thumbv7a-pc-windows-msvc
thumbv7a-uwp-windows-msvc
thumbv7em-none-eabi
thumbv7em-none-eabihf
thumbv7em-nuttx-eabi
thumbv7em-nuttx-eabihf
thumbv7m-none-eabi
thumbv7m-nuttx-eabi
thumbv7neon-linux-androideabi
thumbv7neon-unknown-linux-gnueabihf
thumbv7neon-unknown-linux-musleabihf
thumbv8m.base-none-eabi
thumbv8m.base-nuttx-eabi
thumbv8m.main-none-eabi
thumbv8m.main-none-eabihf
thumbv8m.main-nuttx-eabi
thumbv8m.main-nuttx-eabihf
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
wasm32-wasip1
wasm32-wasip1-threads
wasm32-wasip2
wasm64-unknown-unknown
x86_64-apple-darwin
x86_64-apple-ios
x86_64-apple-ios-macabi
x86_64-apple-tvos
x86_64-apple-watchos-sim
x86_64-fortanix-unknown-sgx
x86_64-fuchsia
x86_64-linux-android
x86_64-pc-nto-qnx710
x86_64-pc-solaris
x86_64-pc-windows-gnu
x86_64-pc-windows-gnullvm
x86_64-pc-windows-msvc
x86_64-unikraft-linux-musl
x86_64-unknown-dragonfly
x86_64-unknown-freebsd
x86_64-unknown-fuchsia
x86_64-unknown-haiku
x86_64-unknown-hermit
x86_64-unknown-illumos
x86_64-unknown-l4re-uclibc
x86_64-unknown-linux-gnu
x86_64-unknown-linux-gnux32
x86_64-unknown-linux-musl
x86_64-unknown-linux-none
x86_64-unknown-linux-ohos
x86_64-unknown-netbsd
x86_64-unknown-none
x86_64-unknown-openbsd
x86_64-unknown-redox
x86_64-unknown-uefi
x86_64-uwp-windows-gnu
x86_64-uwp-windows-msvc
x86_64-win7-windows-msvc
x86_64-wrs-vxworks
x86_64h-apple-darwin
xtensa-esp32-espidf
xtensa-esp32-none-elf
xtensa-esp32s2-espidf
xtensa-esp32s2-none-elf
xtensa-esp32s3-espidf
xtensa-esp32s3-none-elf

View File

@ -0,0 +1,238 @@
package rust
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{}
var _ api.PreparedBuilder = &Builder{}
//nolint:gochecknoinits
func init() {
api.Register("rust", Default)
}
// Builder is golang builder.
type Builder struct{}
// Prepare implements build.PreparedBuilder.
func (b *Builder) Prepare(ctx *context.Context, build config.Build) error {
for _, target := range build.Targets {
out, err := exec.CommandContext(ctx, "rustup", "target", "add", target).CombinedOutput()
if err != nil {
return fmt.Errorf("could not add target %s: %w: %s", target, err, string(out))
}
}
return nil
}
// Parse implements build.Builder.
func (b *Builder) Parse(target string) (api.Target, error) {
parts := strings.Split(target, "-")
if len(parts) < 3 {
return nil, fmt.Errorf("%s is not a valid build target", target)
}
t := Target{
Target: target,
Os: parts[2],
Vendor: parts[1],
Arch: convertToGoarch(parts[0]),
}
if len(parts) > 3 {
t.Environment = parts[3]
}
return t, nil
}
// WithDefaults implements build.Builder.
func (b *Builder) WithDefaults(build config.Build) (config.Build, error) {
log.Warn("you are using the experimental Rust builder")
if len(build.Targets) == 0 {
build.Targets = defaultTargets()
}
if build.GoBinary == "" {
build.GoBinary = "cargo"
}
if build.Command == "" {
build.Command = "zigbuild"
}
if build.Dir == "" {
build.Dir = "."
}
if build.Main != "" {
return build, errors.New("main is not used for rust")
}
if len(build.Ldflags) > 0 {
return build, errors.New("ldflags is not used for rust")
}
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 rust, set targets instead")
}
if len(build.Ignore) > 0 {
return build, errors.New("ignore is not used for rust, set targets instead")
}
if build.Buildmode != "" {
return build, errors.New("buildmode is not used for rust")
}
if len(build.Tags) > 0 {
return build, errors.New("tags is not used for rust")
}
if len(build.Asmflags) > 0 {
return build, errors.New("asmtags is not used for rust")
}
if len(build.BuildDetailsOverrides) > 0 {
return build, errors.New("overrides is not used for rust")
}
for _, t := range build.Targets {
if !isValid(t) {
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 {
dir := filepath.Dir(options.Path)
t := options.Target.(Target)
a := &artifact.Artifact{
Type: artifact.Binary,
Path: options.Path,
Name: options.Name,
Goos: 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: "rust",
},
}
env := []string{}
env = append(env, ctx.Env.Strings()...)
tpl := tmpl.New(ctx).
WithBuildOptions(options).
WithEnvS(env).
WithArtifact(a)
cargo, err := tpl.Apply(build.GoBinary)
if err != nil {
return err
}
command := []string{
cargo,
build.Command,
"--target=" + t.Target,
"--out-dir=" + dir,
"-Z=unstable-options",
"-r",
}
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, rust, 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
}

View File

@ -0,0 +1,90 @@
package rust
import (
"slices"
"strings"
"sync"
_ "embed"
"github.com/goreleaser/goreleaser/v2/internal/tmpl"
)
var (
//go:embed all_targets.txt
allTargetsBts []byte
allTargets []string
targetsOnce sync.Once
)
const (
keyVendor = "Vendor"
keyEnvironment = "Environment"
)
// Target is a Zig build target.
type Target struct {
// The zig formatted target (arch-os-abi).
Target string
Os string
Arch string
Arm string
Vendor string
Environment string
}
// Fields implements build.Target.
func (t Target) Fields() map[string]string {
return map[string]string{
tmpl.KeyOS: t.Os,
tmpl.KeyArch: t.Arch,
keyEnvironment: t.Environment,
keyVendor: t.Vendor,
}
}
// String implements fmt.Stringer.
func (t Target) String() string {
return t.Target
}
func convertToGoarch(s string) string {
ss, ok := map[string]string{
"aarch64": "arm64",
"x86_64": "amd64",
"i686": "386",
"i586": "386",
"i386": "386",
"powerpc": "ppc",
"powerpc64": "ppc64",
"powerpc64le": "ppc64le",
"riscv64": "riscv64",
"s390x": "s390x",
"arm": "arm",
"armv7": "arm",
"wasm32": "wasm",
}[s]
if ok {
return ss
}
return s
}
func isValid(target string) bool {
targetsOnce.Do(func() {
allTargets = strings.Split(string(allTargetsBts), "\n")
})
return slices.Contains(allTargets, target)
}
func defaultTargets() []string {
return []string{
"x86_64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-gnu",
"aarch64-unknown-linux-gnu",
"aarch64-apple-darwin",
}
}

View File

@ -21,6 +21,7 @@ import (
// langs to init.
_ "github.com/goreleaser/goreleaser/v2/internal/builders/golang"
_ "github.com/goreleaser/goreleaser/v2/internal/builders/rust"
_ "github.com/goreleaser/goreleaser/v2/internal/builders/zig"
)
@ -44,11 +45,23 @@ func (Pipe) Run(ctx *context.Context) error {
continue
}
log.WithField("build", build).Debug("building")
if err := prepare(ctx, build); err != nil {
return err
}
runPipeOnBuild(ctx, g, build)
}
return g.Wait()
}
func prepare(ctx *context.Context, build config.Build) error {
prep, ok := builders.For(build.Builder).(builders.PreparedBuilder)
if !ok {
// nothing to do
return nil
}
return prep.Prepare(ctx, build)
}
// Default sets the pipe defaults.
func (Pipe) Default(ctx *context.Context) error {
ids := ids.New("builds")

View File

@ -12,3 +12,8 @@ var GoExampleConfig []byte
//
//go:embed config.zig.yaml
var ZigExampleConfig []byte
// RustExampleConfig is the config used within goreleaser init --lang rust.
//
//go:embed config.rust.yaml
var RustExampleConfig []byte

View File

@ -0,0 +1,53 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
before:
hooks:
# if you don't do these things before calling goreleaser, it might be a
# good idea to do them here:
- rustup default stable
- cargo install --locked cargo-zigbuild
builds:
- builder: rust
targets:
- x86_64-unknown-linux-gnu
- x86_64-apple-darwin
- x86_64-pc-windows-gnu
- aarch64-unknown-linux-gnu
- aarch64-apple-darwin
archives:
- format: tar.gz
# this name template makes the OS and Arch compatible with the results of `uname`.
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
release:
footer: >-
---
Released by [GoReleaser](https://github.com/goreleaser/goreleaser).

View File

@ -52,3 +52,9 @@ type Builder interface {
Build(ctx *context.Context, build config.Build, options Options) error
Parse(target string) (Target, error)
}
// PreparedBuilder can be implemented to run something before all the actual
// builds happen.
type PreparedBuilder interface {
Prepare(ctx *context.Context, build config.Build) error
}

View File

@ -511,7 +511,7 @@ type Build struct {
Main string `yaml:"main,omitempty" json:"main,omitempty"`
Binary string `yaml:"binary,omitempty" json:"binary,omitempty"`
Hooks BuildHookConfig `yaml:"hooks,omitempty" json:"hooks,omitempty"`
Builder string `yaml:"builder,omitempty" json:"builder,omitempty" jsonschema:"enum=,enum=go,enum=zig"`
Builder string `yaml:"builder,omitempty" json:"builder,omitempty" jsonschema:"enum=,enum=go,enum=rust,enum=zig"`
ModTimestamp string `yaml:"mod_timestamp,omitempty" json:"mod_timestamp,omitempty"`
Skip string `yaml:"skip,omitempty" json:"skip,omitempty" jsonschema:"oneof_type=string;boolean"`
GoBinary string `yaml:"gobinary,omitempty" json:"gobinary,omitempty"`