github.com/kekek/gb@v0.4.5-0.20170222120241-d4ba64b0b297/cmd/gb/build.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"go/build"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  
    10  	"github.com/constabulary/gb"
    11  	"github.com/constabulary/gb/cmd"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  func init() {
    16  	registerCommand(buildCmd)
    17  }
    18  
    19  var (
    20  	// build flags
    21  
    22  	// should we perform a release build +release tag ?
    23  	// defaults to false, +debug.
    24  	R bool
    25  
    26  	// force rebuild of packages
    27  	F bool
    28  
    29  	// skip caching of packages
    30  	FF bool
    31  
    32  	// enable race runtime
    33  	race bool
    34  
    35  	ldflags, gcflags []string
    36  
    37  	P int // number of executors to run in parallel
    38  
    39  	dotfile string // path to dot output file
    40  
    41  	buildtags []string
    42  )
    43  
    44  func addBuildFlags(fs *flag.FlagSet) {
    45  	// TODO(dfc) this should accept a *gb.Context
    46  	fs.BoolVar(&R, "r", false, "perform a release build")
    47  	fs.BoolVar(&F, "f", false, "rebuild up-to-date packages")
    48  	fs.BoolVar(&FF, "F", false, "do not cache built packages")
    49  	fs.BoolVar(&race, "race", false, "enable race detector")
    50  	fs.IntVar(&P, "P", runtime.NumCPU(), "number of parallel jobs")
    51  	fs.Var((*stringsFlag)(&ldflags), "ldflags", "flags passed to the linker")
    52  	fs.Var((*stringsFlag)(&gcflags), "gcflags", "flags passed to the compiler")
    53  	fs.StringVar(&dotfile, "dotfile", "", "path to dot output file")
    54  	fs.Var((*stringsFlag)(&buildtags), "tags", "")
    55  }
    56  
    57  var buildCmd = &cmd.Command{
    58  	Name:      "build",
    59  	Short:     "build a package",
    60  	UsageLine: "build [build flags] [packages]",
    61  	Long: `
    62  Build compiles the packages named by the import paths, along with their
    63  dependencies.
    64  
    65  Flags:
    66  
    67  	-f
    68  		ignore cached packages if present, new packages built will overwrite
    69  		any cached packages. This effectively disables incremental
    70  		compilation.
    71  	-F
    72  		do not cache packages, cached packages will still be used for
    73  		incremental compilation. -f -F is advised to disable the package
    74  		caching system.
    75  	-P
    76  		The number of build jobs to run in parallel, including test execution.
    77  		By default this is the number of CPUs visible to gb.
    78  	-R
    79  		sets the base of the project root search path from the current working
    80  		directory to the value supplied. Effectively gb changes working
    81  		directory to this path before searching for the project root.
    82  	-dotfile
    83  		if provided, gb will output a dot formatted file of the build steps to
    84  		be performed.
    85  	-ldflags 'flag list'
    86  		arguments to pass on each linker invocation.
    87  	-gcflags 'arg list'
    88  		arguments to pass on each compile invocation.
    89          -race
    90                  enable data race detection.
    91                  Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
    92  	-tags 'tag list'
    93  		additional build tags.
    94  
    95  The list flags accept a space-separated list of strings. To embed spaces in an
    96  element in the list, surround it with either single or double quotes.
    97  
    98  For more about where packages and binaries are installed, run 'gb help project'.
    99  `,
   100  	Run: func(ctx *gb.Context, args []string) error {
   101  		// TODO(dfc) run should take a *gb.Context not a *gb.Project
   102  		ctx.Force = F
   103  		ctx.Install = !FF
   104  
   105  		pkgs, err := resolveRootPackages(ctx, args...)
   106  		if err != nil {
   107  			return err
   108  		}
   109  
   110  		build, err := gb.BuildPackages(pkgs...)
   111  		if err != nil {
   112  			return err
   113  		}
   114  
   115  		if dotfile != "" {
   116  			f, err := os.Create(dotfile)
   117  			if err != nil {
   118  				return err
   119  			}
   120  			defer f.Close()
   121  			printActions(f, build)
   122  		}
   123  
   124  		startSigHandlers()
   125  		return gb.ExecuteConcurrent(build, P, interrupted)
   126  	},
   127  	AddFlags: addBuildFlags,
   128  }
   129  
   130  // Resolver resolves packages.
   131  type Resolver interface {
   132  	ResolvePackage(path string) (*gb.Package, error)
   133  }
   134  
   135  // resolveRootPackages resolves import paths into packages.
   136  // Only packages which exist inside $PROJECT/src are elegable to be
   137  // roots to build or test. Other import paths are discarded.
   138  func resolveRootPackages(r Resolver, paths ...string) ([]*gb.Package, error) {
   139  	var pkgs []*gb.Package
   140  	for _, path := range paths {
   141  		pkg, err := r.ResolvePackage(path)
   142  		if _, nogo := errors.Cause(err).(*build.NoGoError); nogo {
   143  			// if the package is empty, to no Go files are in scope
   144  			// ignore it.
   145  			// TODO(dfc) ResolvePackage should return an empty *Package
   146  			// and build/test should ignore them.
   147  			continue
   148  		}
   149  		if err != nil {
   150  			return pkgs, errors.Wrapf(err, "failed to resolve import path %q", path)
   151  		}
   152  		if pkg.SrcRoot == filepath.Join(runtime.GOROOT(), "src") {
   153  			// skip package roots that are not part of this project.
   154  			// TODO(dfc) should gb return an error here?
   155  			continue
   156  		}
   157  		pkgs = append(pkgs, pkg)
   158  	}
   159  	return pkgs, nil
   160  }