github.com/szyn/goreleaser@v0.76.1-0.20180517112710-333da09a1297/pipeline/build/build.go (about) 1 // Package build provides a pipe that can build binaries for several 2 // languages. 3 package build 4 5 import ( 6 "bytes" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 "text/template" 12 "time" 13 14 "github.com/apex/log" 15 "github.com/pkg/errors" 16 "golang.org/x/sync/errgroup" 17 18 builders "github.com/goreleaser/goreleaser/build" 19 "github.com/goreleaser/goreleaser/config" 20 "github.com/goreleaser/goreleaser/context" 21 22 // langs to init 23 _ "github.com/goreleaser/goreleaser/internal/builders/golang" 24 ) 25 26 // Pipe for build 27 type Pipe struct{} 28 29 func (Pipe) String() string { 30 return "building binaries" 31 } 32 33 // Run the pipe 34 func (Pipe) Run(ctx *context.Context) error { 35 for _, build := range ctx.Config.Builds { 36 log.WithField("build", build).Debug("building") 37 if err := runPipeOnBuild(ctx, build); err != nil { 38 return err 39 } 40 } 41 return nil 42 } 43 44 // Default sets the pipe defaults 45 func (Pipe) Default(ctx *context.Context) error { 46 for i, build := range ctx.Config.Builds { 47 ctx.Config.Builds[i] = buildWithDefaults(ctx, build) 48 } 49 if len(ctx.Config.Builds) == 0 { 50 ctx.Config.Builds = []config.Build{ 51 buildWithDefaults(ctx, ctx.Config.SingleBuild), 52 } 53 } 54 return nil 55 } 56 57 func buildWithDefaults(ctx *context.Context, build config.Build) config.Build { 58 if build.Lang == "" { 59 build.Lang = "go" 60 } 61 if build.Binary == "" { 62 build.Binary = ctx.Config.Release.GitHub.Name 63 } 64 for k, v := range build.Env { 65 build.Env[k] = os.ExpandEnv(v) 66 } 67 return builders.For(build.Lang).WithDefaults(build) 68 } 69 70 func runPipeOnBuild(ctx *context.Context, build config.Build) error { 71 if err := runHook(ctx, build.Env, build.Hooks.Pre); err != nil { 72 return errors.Wrap(err, "pre hook failed") 73 } 74 sem := make(chan bool, ctx.Parallelism) 75 var g errgroup.Group 76 for _, target := range build.Targets { 77 sem <- true 78 target := target 79 build := build 80 g.Go(func() error { 81 defer func() { 82 <-sem 83 }() 84 return doBuild(ctx, build, target) 85 }) 86 } 87 if err := g.Wait(); err != nil { 88 return err 89 } 90 return errors.Wrap(runHook(ctx, build.Env, build.Hooks.Post), "post hook failed") 91 } 92 93 func runHook(ctx *context.Context, env []string, hook string) error { 94 if hook == "" { 95 return nil 96 } 97 log.WithField("hook", hook).Info("running hook") 98 cmd := strings.Fields(hook) 99 return run(ctx, cmd, env) 100 } 101 102 func doBuild(ctx *context.Context, build config.Build, target string) error { 103 var ext = extFor(target) 104 105 binary, err := binary(ctx, build) 106 if err != nil { 107 return err 108 } 109 110 build.Binary = binary 111 var name = build.Binary + ext 112 var path = filepath.Join(ctx.Config.Dist, target, name) 113 log.WithField("binary", path).Info("building") 114 return builders.For(build.Lang).Build(ctx, build, builders.Options{ 115 Target: target, 116 Name: name, 117 Path: path, 118 Ext: ext, 119 }) 120 } 121 122 func binary(ctx *context.Context, build config.Build) (string, error) { 123 var data = struct { 124 Commit string 125 Tag string 126 Version string 127 Date string 128 Env map[string]string 129 }{ 130 Commit: ctx.Git.Commit, 131 Tag: ctx.Git.CurrentTag, 132 Version: ctx.Version, 133 Date: time.Now().UTC().Format(time.RFC3339), 134 Env: ctx.Env, 135 } 136 var out bytes.Buffer 137 t, err := template.New("binary"). 138 Option("missingkey=error"). 139 Parse(build.Binary) 140 if err != nil { 141 return "", err 142 } 143 err = t.Execute(&out, data) 144 return out.String(), err 145 } 146 147 func extFor(target string) string { 148 if strings.Contains(target, "windows") { 149 return ".exe" 150 } 151 return "" 152 } 153 154 func run(ctx *context.Context, command, env []string) error { 155 /* #nosec */ 156 var cmd = exec.CommandContext(ctx, command[0], command[1:]...) 157 var log = log.WithField("env", env).WithField("cmd", command) 158 cmd.Env = append(cmd.Env, os.Environ()...) 159 cmd.Env = append(cmd.Env, env...) 160 log.WithField("cmd", command).WithField("env", env).Debug("running") 161 if out, err := cmd.CombinedOutput(); err != nil { 162 log.WithError(err).Debug("failed") 163 return errors.New(string(out)) 164 } 165 return nil 166 }