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  }