github.com/tomsquest/goreleaser@v0.34.3-0.20171008022654-7d6ef4d338b3/pipeline/build/build.go (about) 1 // Package build implements Pipe and can build Go projects for 2 // several platforms, with pre and post hook support. 3 package build 4 5 import ( 6 "fmt" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 12 "github.com/apex/log" 13 "github.com/goreleaser/goreleaser/config" 14 "github.com/goreleaser/goreleaser/context" 15 "github.com/goreleaser/goreleaser/internal/buildtarget" 16 "github.com/goreleaser/goreleaser/internal/ext" 17 "github.com/goreleaser/goreleaser/internal/name" 18 "golang.org/x/sync/errgroup" 19 ) 20 21 // Pipe for build 22 type Pipe struct{} 23 24 // Description of the pipe 25 func (Pipe) Description() string { 26 return "Building binaries" 27 } 28 29 // Run the pipe 30 func (Pipe) Run(ctx *context.Context) error { 31 for _, build := range ctx.Config.Builds { 32 log.WithField("build", build).Debug("building") 33 if err := runPipeOnBuild(ctx, build); err != nil { 34 return err 35 } 36 } 37 return nil 38 } 39 40 func runPipeOnBuild(ctx *context.Context, build config.Build) error { 41 if err := runHook(build.Env, build.Hooks.Pre); err != nil { 42 return err 43 } 44 sem := make(chan bool, ctx.Parallelism) 45 var g errgroup.Group 46 for _, target := range buildtarget.All(build) { 47 sem <- true 48 target := target 49 build := build 50 g.Go(func() error { 51 defer func() { 52 <-sem 53 }() 54 return doBuild(ctx, build, target) 55 }) 56 } 57 if err := g.Wait(); err != nil { 58 return err 59 } 60 return runHook(build.Env, build.Hooks.Post) 61 } 62 63 func runHook(env []string, hook string) error { 64 if hook == "" { 65 return nil 66 } 67 log.WithField("hook", hook).Info("running hook") 68 cmd := strings.Fields(hook) 69 return run(buildtarget.Runtime, cmd, env) 70 } 71 72 func doBuild(ctx *context.Context, build config.Build, target buildtarget.Target) error { 73 folder, err := name.For(ctx, target) 74 if err != nil { 75 return err 76 } 77 var binaryName = build.Binary + ext.For(target) 78 var prettyName = binaryName 79 if ctx.Config.Archive.Format == "binary" { 80 binaryName, err = name.ForBuild(ctx, build, target) 81 if err != nil { 82 return err 83 } 84 binaryName = binaryName + ext.For(target) 85 } 86 var binary = filepath.Join(ctx.Config.Dist, folder, binaryName) 87 ctx.AddBinary(target.String(), folder, prettyName, binary) 88 log.WithField("binary", binary).Info("building") 89 cmd := []string{"go", "build"} 90 if build.Flags != "" { 91 cmd = append(cmd, strings.Fields(build.Flags)...) 92 } 93 flags, err := ldflags(ctx, build) 94 if err != nil { 95 return err 96 } 97 cmd = append(cmd, "-ldflags="+flags, "-o", binary, build.Main) 98 return run(target, cmd, build.Env) 99 } 100 101 func run(target buildtarget.Target, command, env []string) error { 102 var cmd = exec.Command(command[0], command[1:]...) 103 env = append(env, target.Env()...) 104 var log = log.WithField("target", target.PrettyString()). 105 WithField("env", env). 106 WithField("cmd", command) 107 cmd.Env = append(cmd.Env, os.Environ()...) 108 cmd.Env = append(cmd.Env, env...) 109 log.Debug("running") 110 if out, err := cmd.CombinedOutput(); err != nil { 111 log.WithError(err).Debug("failed") 112 return fmt.Errorf("build failed for %s:\n%v", target.PrettyString(), string(out)) 113 } 114 return nil 115 }