1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-10 03:47:03 +02:00
goreleaser/internal/builders/golang/build.go
2018-01-21 14:31:08 -02:00

133 lines
2.9 KiB
Go

package golang
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"text/template"
"time"
"github.com/goreleaser/goreleaser/build"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/pkg/errors"
)
var Default = &Builder{}
func init() {
build.Register("go", Default)
}
type Builder struct {
}
func (*Builder) Build(ctx *context.Context, cfg config.Build, options build.Options) error {
if err := checkMain(ctx, cfg); err != nil {
return err
}
cmd := []string{"go", "build"}
if cfg.Flags != "" {
cmd = append(cmd, strings.Fields(cfg.Flags)...)
}
flags, err := ldflags(ctx, cfg)
if err != nil {
return err
}
cmd = append(cmd, "-ldflags="+flags, "-o", options.Path, cfg.Main)
if err := build.Run(ctx, options.Target, cmd, cfg.Env); err != nil {
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,
Goos: options.Target.OS,
Goarch: options.Target.Arch,
Goarm: options.Target.Arm,
Extra: map[string]string{
"Binary": cfg.Binary,
"Ext": options.Ext,
},
})
return nil
}
type ldflagsData struct {
Date string
Tag string
Commit string
Version string
Env map[string]string
}
func ldflags(ctx *context.Context, build config.Build) (string, error) {
var data = ldflagsData{
Commit: ctx.Git.Commit,
Tag: ctx.Git.CurrentTag,
Version: ctx.Version,
Date: time.Now().UTC().Format(time.RFC3339),
Env: ctx.Env,
}
var out bytes.Buffer
t, err := template.New("ldflags").
Option("missingkey=error").
Parse(build.Ldflags)
if err != nil {
return "", err
}
err = t.Execute(&out, data)
return out.String(), err
}
func checkMain(ctx *context.Context, build config.Build) error {
var main = build.Main
if main == "" {
main = "."
}
stat, ferr := os.Stat(main)
if os.IsNotExist(ferr) {
return errors.Wrapf(ferr, "could not open %s", main)
}
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)
}
file, err := parser.ParseFile(token.NewFileSet(), build.Main, nil, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse file: %s", build.Main)
}
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
}