github.phpd.cn/goreleaser/goreleaser@v0.92.0/internal/pipe/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 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 11 "github.com/apex/log" 12 "github.com/pkg/errors" 13 14 "github.com/goreleaser/goreleaser/internal/semerrgroup" 15 "github.com/goreleaser/goreleaser/internal/tmpl" 16 builders "github.com/goreleaser/goreleaser/pkg/build" 17 "github.com/goreleaser/goreleaser/pkg/config" 18 "github.com/goreleaser/goreleaser/pkg/context" 19 20 // langs to init 21 _ "github.com/goreleaser/goreleaser/internal/builders/golang" 22 ) 23 24 // Pipe for build 25 type Pipe struct{} 26 27 func (Pipe) String() string { 28 return "building binaries" 29 } 30 31 // Run the pipe 32 func (Pipe) Run(ctx *context.Context) error { 33 for _, build := range ctx.Config.Builds { 34 log.WithField("build", build).Debug("building") 35 if err := runPipeOnBuild(ctx, build); err != nil { 36 return err 37 } 38 } 39 return nil 40 } 41 42 // Default sets the pipe defaults 43 func (Pipe) Default(ctx *context.Context) error { 44 for i, build := range ctx.Config.Builds { 45 ctx.Config.Builds[i] = buildWithDefaults(ctx, build) 46 } 47 if len(ctx.Config.Builds) == 0 { 48 ctx.Config.Builds = []config.Build{ 49 buildWithDefaults(ctx, ctx.Config.SingleBuild), 50 } 51 } 52 if len(ctx.Config.Builds) > 1 { 53 log.Warn("you have more than 1 build setup: please make sure it is a not a typo on your config") 54 } 55 return nil 56 } 57 58 func buildWithDefaults(ctx *context.Context, build config.Build) config.Build { 59 if build.Lang == "" { 60 build.Lang = "go" 61 } 62 if build.Binary == "" { 63 build.Binary = ctx.Config.ProjectName 64 } 65 for k, v := range build.Env { 66 build.Env[k] = os.ExpandEnv(v) 67 } 68 return builders.For(build.Lang).WithDefaults(build) 69 } 70 71 func runPipeOnBuild(ctx *context.Context, build config.Build) error { 72 if err := runHook(ctx, build.Env, build.Hooks.Pre); err != nil { 73 return errors.Wrap(err, "pre hook failed") 74 } 75 var g = semerrgroup.New(ctx.Parallelism) 76 for _, target := range build.Targets { 77 target := target 78 build := build 79 g.Go(func() error { 80 return doBuild(ctx, build, target) 81 }) 82 } 83 if err := g.Wait(); err != nil { 84 return err 85 } 86 return errors.Wrap(runHook(ctx, build.Env, build.Hooks.Post), "post hook failed") 87 } 88 89 func runHook(ctx *context.Context, env []string, hook string) error { 90 if hook == "" { 91 return nil 92 } 93 log.WithField("hook", hook).Info("running hook") 94 cmd := strings.Fields(hook) 95 return run(ctx, cmd, env) 96 } 97 98 func doBuild(ctx *context.Context, build config.Build, target string) error { 99 var ext = extFor(target) 100 101 binary, err := tmpl.New(ctx).Apply(build.Binary) 102 if err != nil { 103 return err 104 } 105 106 build.Binary = binary 107 var name = build.Binary + ext 108 var path = filepath.Join(ctx.Config.Dist, target, name) 109 log.WithField("binary", path).Info("building") 110 return builders.For(build.Lang).Build(ctx, build, builders.Options{ 111 Target: target, 112 Name: name, 113 Path: path, 114 Ext: ext, 115 }) 116 } 117 118 func extFor(target string) string { 119 if strings.Contains(target, "windows") { 120 return ".exe" 121 } 122 return "" 123 } 124 125 func run(ctx *context.Context, command, env []string) error { 126 /* #nosec */ 127 var cmd = exec.CommandContext(ctx, command[0], command[1:]...) 128 var log = log.WithField("env", env).WithField("cmd", command) 129 cmd.Env = append(cmd.Env, os.Environ()...) 130 cmd.Env = append(cmd.Env, env...) 131 log.WithField("cmd", command).WithField("env", env).Debug("running") 132 if out, err := cmd.CombinedOutput(); err != nil { 133 log.WithError(err).Debug("failed") 134 return errors.New(string(out)) 135 } 136 return nil 137 }