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

211 lines
4.7 KiB
Go
Raw Normal View History

2018-01-21 14:31:08 -02:00
package golang
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
2018-01-26 19:26:28 -02:00
"os/exec"
2018-01-21 14:31:08 -02:00
"strings"
2018-01-26 19:26:28 -02:00
"github.com/apex/log"
2018-01-21 14:31:08 -02:00
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/tmpl"
api "github.com/goreleaser/goreleaser/pkg/build"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
2018-01-21 14:31:08 -02:00
"github.com/pkg/errors"
)
2018-01-22 01:10:17 -02:00
// Default builder instance
2018-11-07 22:04:49 -02:00
// nolint: gochecknoglobals
2018-01-21 14:31:08 -02:00
var Default = &Builder{}
2018-11-07 22:04:49 -02:00
// nolint: gochecknoinits
2018-01-21 14:31:08 -02:00
func init() {
2018-01-22 01:10:17 -02:00
api.Register("go", Default)
2018-01-21 14:31:08 -02:00
}
2018-01-22 01:10:17 -02:00
// Builder is golang builder
type Builder struct{}
2018-01-21 14:31:08 -02:00
2018-01-26 21:54:08 -02:00
// WithDefaults sets the defaults for a golang build and returns it
2018-01-26 19:35:12 -02:00
func (*Builder) WithDefaults(build config.Build) config.Build {
if build.Main == "" {
build.Main = "."
}
if len(build.Goos) == 0 {
build.Goos = []string{"linux", "darwin"}
}
if len(build.Goarch) == 0 {
build.Goarch = []string{"amd64", "386"}
}
if len(build.Goarm) == 0 {
build.Goarm = []string{"6"}
}
if len(build.Ldflags) == 0 {
build.Ldflags = []string{"-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"}
}
2018-01-22 01:10:17 -02:00
if len(build.Targets) == 0 {
build.Targets = matrix(build)
}
return build
}
2018-01-22 01:10:17 -02:00
// Build builds a golang build
func (*Builder) Build(ctx *context.Context, build config.Build, options api.Options) error {
2018-07-04 00:42:24 -07:00
if err := checkMain(build); err != nil {
2018-01-21 14:31:08 -02:00
return err
}
target, err := newBuildTarget(options.Target)
if err != nil {
return err
}
var cmd = []string{"go", "build"}
var env = append(ctx.Env.Strings(), build.Env...)
env = append(env, target.Env()...)
flags, err := processFlags(ctx, env, build.Flags, "")
2019-01-17 10:50:00 -02:00
if err != nil {
return err
}
cmd = append(cmd, flags...)
asmflags, err := processFlags(ctx, env, build.Asmflags, "-asmflags=")
if err != nil {
return err
}
cmd = append(cmd, asmflags...)
gcflags, err := processFlags(ctx, env, build.Gcflags, "-gcflags=")
if err != nil {
return err
}
cmd = append(cmd, gcflags...)
ldflags, err := processFlags(ctx, env, build.Ldflags, "-ldflags=")
2018-01-21 14:31:08 -02:00
if err != nil {
return err
}
cmd = append(cmd, ldflags...)
cmd = append(cmd, "-o", options.Path, build.Main)
2018-01-26 19:26:28 -02:00
if err := run(ctx, cmd, env); err != nil {
2018-01-21 14:31:08 -02:00
return errors.Wrapf(err, "failed to build for %s", options.Target)
}
ctx.Artifacts.Add(artifact.Artifact{
Type: artifact.Binary,
Path: options.Path,
Name: options.Name,
2018-01-21 22:44:06 -02:00
Goos: target.os,
Goarch: target.arch,
Goarm: target.arm,
Extra: map[string]interface{}{
2018-01-22 01:10:17 -02:00
"Binary": build.Binary,
2018-01-21 14:31:08 -02:00
"Ext": options.Ext,
"ID": build.ID,
2018-01-21 14:31:08 -02:00
},
})
return nil
}
func processFlags(ctx *context.Context, env, flags []string, flagPrefix string) ([]string, error) {
processed := make([]string, 0, len(flags))
for _, rawFlag := range flags {
flag, err := tmpl.New(ctx).WithEnvS(env).Apply(rawFlag)
if err != nil {
return nil, err
}
processed = append(processed, flagPrefix+flag)
}
return processed, nil
}
2018-01-26 19:26:28 -02:00
func run(ctx *context.Context, command, env []string) error {
/* #nosec */
var cmd = exec.CommandContext(ctx, command[0], command[1:]...)
var log = log.WithField("env", env).WithField("cmd", command)
cmd.Env = env
log.Debug("running")
2018-01-26 19:26:28 -02:00
if out, err := cmd.CombinedOutput(); err != nil {
log.WithError(err).Debug("failed")
return errors.New(string(out))
}
return nil
}
2018-01-21 22:44:06 -02:00
type buildTarget struct {
os, arch, arm string
}
2018-01-26 21:54:08 -02:00
func newBuildTarget(s string) (buildTarget, error) {
2018-01-21 22:44:06 -02:00
var t = buildTarget{}
parts := strings.Split(s, "_")
2018-01-26 21:54:08 -02:00
if len(parts) < 2 {
return t, fmt.Errorf("%s is not a valid build target", s)
}
2018-01-21 22:44:06 -02:00
t.os = parts[0]
t.arch = parts[1]
if len(parts) == 3 {
t.arm = parts[2]
}
2018-01-26 21:54:08 -02:00
return t, nil
2018-01-21 22:44:06 -02:00
}
func (b buildTarget) Env() []string {
return []string{
"GOOS=" + b.os,
"GOARCH=" + b.arch,
"GOARM=" + b.arm,
}
}
2018-07-04 00:42:24 -07:00
func checkMain(build config.Build) error {
2018-01-21 14:31:08 -02:00
var main = build.Main
if main == "" {
main = "."
}
stat, ferr := os.Stat(main)
2018-02-01 13:11:46 +01:00
if ferr != nil {
return ferr
2018-01-21 14:31:08 -02:00
}
if stat.IsDir() {
packs, err := parser.ParseDir(token.NewFileSet(), main, nil, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse dir: %s", main)
}
for _, pack := range packs {
for _, file := range pack.Files {
if hasMain(file) {
return nil
}
}
}
return fmt.Errorf("build for %s does not contain a main function", build.Binary)
}
2018-01-26 21:54:08 -02:00
file, err := parser.ParseFile(token.NewFileSet(), main, nil, 0)
2018-01-21 14:31:08 -02:00
if err != nil {
2018-01-26 21:54:08 -02:00
return errors.Wrapf(err, "failed to parse file: %s", main)
2018-01-21 14:31:08 -02:00
}
if hasMain(file) {
return nil
}
return fmt.Errorf("build for %s does not contain a main function", build.Binary)
}
func hasMain(file *ast.File) bool {
for _, decl := range file.Decls {
fn, isFn := decl.(*ast.FuncDecl)
if !isFn {
continue
}
if fn.Name.Name == "main" && fn.Recv == nil {
return true
}
}
return false
}