github.com/mergetb/u-root@v4.0.1-0.20190719191109-b70b86b73e5b+incompatible/pkg/golang/build.go (about)

     1  // Copyright 2015-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package golang
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"go/build"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strings"
    15  )
    16  
    17  type Environ struct {
    18  	build.Context
    19  }
    20  
    21  // Default is the default build environment comprised of the default GOPATH,
    22  // GOROOT, GOOS, GOARCH, and CGO_ENABLED values.
    23  func Default() Environ {
    24  	return Environ{Context: build.Default}
    25  }
    26  
    27  // PackageByPath retrieves information about a package by its file system path.
    28  //
    29  // `path` is assumed to be the directory containing the package.
    30  func (c Environ) PackageByPath(path string) (*build.Package, error) {
    31  	abs, err := filepath.Abs(path)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	return c.Context.ImportDir(abs, 0)
    36  }
    37  
    38  // Package retrieves information about a package by its Go import path.
    39  func (c Environ) Package(importPath string) (*build.Package, error) {
    40  	return c.Context.Import(importPath, "", 0)
    41  }
    42  
    43  // ListPackage matches a subset of the JSON output of the `go list -json`
    44  // command.
    45  //
    46  // See `go help list` for the full structure.
    47  //
    48  // This currently contains an incomplete list of dependencies.
    49  type ListPackage struct {
    50  	Dir        string
    51  	Deps       []string
    52  	GoFiles    []string
    53  	SFiles     []string
    54  	HFiles     []string
    55  	Goroot     bool
    56  	Root       string
    57  	ImportPath string
    58  }
    59  
    60  func (c Environ) goCmd(args ...string) *exec.Cmd {
    61  	cmd := exec.Command(filepath.Join(c.GOROOT, "bin", "go"), args...)
    62  	cmd.Env = append(os.Environ(), c.Env()...)
    63  	return cmd
    64  }
    65  
    66  // Deps lists all dependencies of the package given by `importPath`.
    67  func (c Environ) Deps(importPath string) (*ListPackage, error) {
    68  	// The output of this is almost the same as build.Import, except for
    69  	// the dependencies.
    70  	cmd := c.goCmd("list", "-json", importPath)
    71  	out, err := cmd.CombinedOutput()
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	var p ListPackage
    77  	if err := json.Unmarshal(out, &p); err != nil {
    78  		return nil, err
    79  	}
    80  	return &p, nil
    81  }
    82  
    83  func (c Environ) Env() []string {
    84  	var env []string
    85  	if c.GOARCH != "" {
    86  		env = append(env, fmt.Sprintf("GOARCH=%s", c.GOARCH))
    87  	}
    88  	if c.GOOS != "" {
    89  		env = append(env, fmt.Sprintf("GOOS=%s", c.GOOS))
    90  	}
    91  	if c.GOROOT != "" {
    92  		env = append(env, fmt.Sprintf("GOROOT=%s", c.GOROOT))
    93  	}
    94  	if c.GOPATH != "" {
    95  		env = append(env, fmt.Sprintf("GOPATH=%s", c.GOPATH))
    96  	}
    97  	var cgo int8
    98  	if c.CgoEnabled {
    99  		cgo = 1
   100  	}
   101  	env = append(env, fmt.Sprintf("CGO_ENABLED=%d", cgo))
   102  	return env
   103  }
   104  
   105  func (c Environ) String() string {
   106  	return strings.Join(c.Env(), " ")
   107  }
   108  
   109  // Optional arguments to Environ.Build.
   110  type BuildOpts struct {
   111  	// ExtraArgs to `go build`.
   112  	ExtraArgs []string
   113  }
   114  
   115  // Build compiles the package given by `importPath`, writing the build object
   116  // to `binaryPath`.
   117  func (c Environ) Build(importPath string, binaryPath string, opts BuildOpts) error {
   118  	p, err := c.Package(importPath)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	return c.BuildDir(p.Dir, binaryPath, opts)
   124  }
   125  
   126  // BuildDir compiles the package in the directory `dirPath`, writing the build
   127  // object to `binaryPath`.
   128  func (c Environ) BuildDir(dirPath string, binaryPath string, opts BuildOpts) error {
   129  	args := []string{
   130  		"build",
   131  		"-a", // Force rebuilding of packages.
   132  		"-o", binaryPath,
   133  		"-installsuffix", "uroot",
   134  		"-ldflags", "-s -w", // Strip all symbols.
   135  	}
   136  	if len(c.BuildTags) > 0 {
   137  		args = append(args, []string{"-tags", strings.Join(c.BuildTags, " ")}...)
   138  	}
   139  	if opts.ExtraArgs != nil {
   140  		args = append(args, opts.ExtraArgs...)
   141  	}
   142  	// We always set the working directory, so this is always '.'.
   143  	args = append(args, ".")
   144  
   145  	cmd := c.goCmd(args...)
   146  	cmd.Dir = dirPath
   147  
   148  	if o, err := cmd.CombinedOutput(); err != nil {
   149  		return fmt.Errorf("error building go package in %q: %v, %v", dirPath, string(o), err)
   150  	}
   151  	return nil
   152  }