...

Source file src/github.com/onsi/gomega/gexec/build.go

Documentation: github.com/onsi/gomega/gexec

     1  // untested sections: 5
     2  
     3  package gexec
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"go/build"
     9  	"os"
    10  	"os/exec"
    11  	"path"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  	"sync"
    16  
    17  	"github.com/onsi/gomega/internal/gutil"
    18  )
    19  
    20  var (
    21  	mu     sync.Mutex
    22  	tmpDir string
    23  )
    24  
    25  /*
    26  Build uses go build to compile the package at packagePath.  The resulting binary is saved off in a temporary directory.
    27  A path pointing to this binary is returned.
    28  
    29  Build uses the $GOPATH set in your environment. If $GOPATH is not set and you are using Go 1.8+,
    30  it will use the default GOPATH instead.  It passes the variadic args on to `go build`.
    31  */
    32  func Build(packagePath string, args ...string) (compiledPath string, err error) {
    33  	return doBuild(build.Default.GOPATH, packagePath, nil, args...)
    34  }
    35  
    36  /*
    37  BuildWithEnvironment is identical to Build but allows you to specify env vars to be set at build time.
    38  */
    39  func BuildWithEnvironment(packagePath string, env []string, args ...string) (compiledPath string, err error) {
    40  	return doBuild(build.Default.GOPATH, packagePath, env, args...)
    41  }
    42  
    43  /*
    44  BuildIn is identical to Build but allows you to specify a custom $GOPATH (the first argument).
    45  */
    46  func BuildIn(gopath string, packagePath string, args ...string) (compiledPath string, err error) {
    47  	return doBuild(gopath, packagePath, nil, args...)
    48  }
    49  
    50  func doBuild(gopath, packagePath string, env []string, args ...string) (compiledPath string, err error) {
    51  	executable, err := newExecutablePath(gopath, packagePath)
    52  	if err != nil {
    53  		return "", err
    54  	}
    55  
    56  	cmdArgs := append([]string{"build"}, args...)
    57  	cmdArgs = append(cmdArgs, "-o", executable, packagePath)
    58  
    59  	build := exec.Command("go", cmdArgs...)
    60  	build.Env = replaceGoPath(os.Environ(), gopath)
    61  	build.Env = append(build.Env, env...)
    62  
    63  	output, err := build.CombinedOutput()
    64  	if err != nil {
    65  		return "", fmt.Errorf("Failed to build %s:\n\nError:\n%s\n\nOutput:\n%s", packagePath, err, string(output))
    66  	}
    67  
    68  	return executable, nil
    69  }
    70  
    71  /*
    72  CompileTest uses go test to compile the test package at packagePath.  The resulting binary is saved off in a temporary directory.
    73  A path pointing to this binary is returned.
    74  
    75  CompileTest uses the $GOPATH set in your environment. If $GOPATH is not set and you are using Go 1.8+,
    76  it will use the default GOPATH instead.  It passes the variadic args on to `go test`.
    77  */
    78  func CompileTest(packagePath string, args ...string) (compiledPath string, err error) {
    79  	return doCompileTest(build.Default.GOPATH, packagePath, nil, args...)
    80  }
    81  
    82  /*
    83  GetAndCompileTest is identical to CompileTest but `go get` the package before compiling tests.
    84  */
    85  func GetAndCompileTest(packagePath string, args ...string) (compiledPath string, err error) {
    86  	if err := getForTest(build.Default.GOPATH, packagePath, []string{"GO111MODULE=off"}); err != nil {
    87  		return "", err
    88  	}
    89  
    90  	return doCompileTest(build.Default.GOPATH, packagePath, []string{"GO111MODULE=off"}, args...)
    91  }
    92  
    93  /*
    94  CompileTestWithEnvironment is identical to CompileTest but allows you to specify env vars to be set at build time.
    95  */
    96  func CompileTestWithEnvironment(packagePath string, env []string, args ...string) (compiledPath string, err error) {
    97  	return doCompileTest(build.Default.GOPATH, packagePath, env, args...)
    98  }
    99  
   100  /*
   101  GetAndCompileTestWithEnvironment is identical to GetAndCompileTest but allows you to specify env vars to be set at build time.
   102  */
   103  func GetAndCompileTestWithEnvironment(packagePath string, env []string, args ...string) (compiledPath string, err error) {
   104  	if err := getForTest(build.Default.GOPATH, packagePath, append(env, "GO111MODULE=off")); err != nil {
   105  		return "", err
   106  	}
   107  
   108  	return doCompileTest(build.Default.GOPATH, packagePath, append(env, "GO111MODULE=off"), args...)
   109  }
   110  
   111  /*
   112  CompileTestIn is identical to CompileTest but allows you to specify a custom $GOPATH (the first argument).
   113  */
   114  func CompileTestIn(gopath string, packagePath string, args ...string) (compiledPath string, err error) {
   115  	return doCompileTest(gopath, packagePath, nil, args...)
   116  }
   117  
   118  /*
   119  GetAndCompileTestIn is identical to GetAndCompileTest but allows you to specify a custom $GOPATH (the first argument).
   120  */
   121  func GetAndCompileTestIn(gopath string, packagePath string, args ...string) (compiledPath string, err error) {
   122  	if err := getForTest(gopath, packagePath, []string{"GO111MODULE=off"}); err != nil {
   123  		return "", err
   124  	}
   125  
   126  	return doCompileTest(gopath, packagePath, []string{"GO111MODULE=off"}, args...)
   127  }
   128  
   129  func isLocalPackage(packagePath string) bool {
   130  	return strings.HasPrefix(packagePath, ".")
   131  }
   132  
   133  func getForTest(gopath, packagePath string, env []string) error {
   134  	if isLocalPackage(packagePath) {
   135  		return nil
   136  	}
   137  
   138  	return doGet(gopath, packagePath, env, "-t")
   139  }
   140  
   141  func doGet(gopath, packagePath string, env []string, args ...string) error {
   142  	args = append(args, packagePath)
   143  	args = append([]string{"get"}, args...)
   144  
   145  	goGet := exec.Command("go", args...)
   146  	goGet.Dir = gopath
   147  	goGet.Env = replaceGoPath(os.Environ(), gopath)
   148  	goGet.Env = append(goGet.Env, env...)
   149  
   150  	output, err := goGet.CombinedOutput()
   151  	if err != nil {
   152  		return fmt.Errorf("Failed to get %s:\n\nError:\n%s\n\nOutput:\n%s", packagePath, err, string(output))
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  func doCompileTest(gopath, packagePath string, env []string, args ...string) (compiledPath string, err error) {
   159  	executable, err := newExecutablePath(gopath, packagePath, ".test")
   160  	if err != nil {
   161  		return "", err
   162  	}
   163  
   164  	cmdArgs := append([]string{"test", "-c"}, args...)
   165  	cmdArgs = append(cmdArgs, "-o", executable, packagePath)
   166  
   167  	build := exec.Command("go", cmdArgs...)
   168  	build.Env = replaceGoPath(os.Environ(), gopath)
   169  	build.Env = append(build.Env, env...)
   170  
   171  	output, err := build.CombinedOutput()
   172  	if err != nil {
   173  		return "", fmt.Errorf("Failed to build %s:\n\nError:\n%s\n\nOutput:\n%s", packagePath, err, string(output))
   174  	}
   175  
   176  	return executable, nil
   177  }
   178  
   179  func replaceGoPath(environ []string, newGoPath string) []string {
   180  	newEnviron := []string{}
   181  	for _, v := range environ {
   182  		if !strings.HasPrefix(v, "GOPATH=") {
   183  			newEnviron = append(newEnviron, v)
   184  		}
   185  	}
   186  	return append(newEnviron, "GOPATH="+newGoPath)
   187  }
   188  
   189  func newExecutablePath(gopath, packagePath string, suffixes ...string) (string, error) {
   190  	tmpDir, err := temporaryDirectory()
   191  	if err != nil {
   192  		return "", err
   193  	}
   194  
   195  	if len(gopath) == 0 {
   196  		return "", errors.New("$GOPATH not provided when building " + packagePath)
   197  	}
   198  
   199  	executable := filepath.Join(tmpDir, path.Base(packagePath))
   200  
   201  	if runtime.GOOS == "windows" {
   202  		executable += ".exe"
   203  	}
   204  
   205  	return executable, nil
   206  }
   207  
   208  /*
   209  You should call CleanupBuildArtifacts before your test ends to clean up any temporary artifacts generated by
   210  gexec. In Ginkgo this is typically done in an AfterSuite callback.
   211  */
   212  func CleanupBuildArtifacts() {
   213  	mu.Lock()
   214  	defer mu.Unlock()
   215  	if tmpDir != "" {
   216  		os.RemoveAll(tmpDir)
   217  		tmpDir = ""
   218  	}
   219  }
   220  
   221  func temporaryDirectory() (string, error) {
   222  	var err error
   223  	mu.Lock()
   224  	defer mu.Unlock()
   225  	if tmpDir == "" {
   226  		tmpDir, err = gutil.MkdirTemp("", "gexec_artifacts")
   227  		if err != nil {
   228  			return "", err
   229  		}
   230  	}
   231  
   232  	return gutil.MkdirTemp(tmpDir, "g")
   233  }
   234  

View as plain text