Home | History | Annotate | Download | only in build
      1 // Copyright 2017 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package build
     16 
     17 import (
     18 	"os/exec"
     19 )
     20 
     21 // Cmd is a wrapper of os/exec.Cmd that integrates with the build context for
     22 // logging, the config's Environment for simpler environment modification, and
     23 // implements hooks for sandboxing
     24 type Cmd struct {
     25 	*exec.Cmd
     26 
     27 	Environment *Environment
     28 	Sandbox     Sandbox
     29 
     30 	ctx    Context
     31 	config Config
     32 	name   string
     33 }
     34 
     35 func Command(ctx Context, config Config, name string, executable string, args ...string) *Cmd {
     36 	ret := &Cmd{
     37 		Cmd:         exec.CommandContext(ctx.Context, executable, args...),
     38 		Environment: config.Environment().Copy(),
     39 		Sandbox:     noSandbox,
     40 
     41 		ctx:    ctx,
     42 		config: config,
     43 		name:   name,
     44 	}
     45 
     46 	return ret
     47 }
     48 
     49 func (c *Cmd) prepare() {
     50 	if c.Env == nil {
     51 		c.Env = c.Environment.Environ()
     52 	}
     53 	if c.sandboxSupported() {
     54 		c.wrapSandbox()
     55 	}
     56 
     57 	c.ctx.Verboseln(c.Path, c.Args)
     58 }
     59 
     60 func (c *Cmd) Start() error {
     61 	c.prepare()
     62 	return c.Cmd.Start()
     63 }
     64 
     65 func (c *Cmd) Run() error {
     66 	c.prepare()
     67 	err := c.Cmd.Run()
     68 	return err
     69 }
     70 
     71 func (c *Cmd) Output() ([]byte, error) {
     72 	c.prepare()
     73 	bytes, err := c.Cmd.Output()
     74 	return bytes, err
     75 }
     76 
     77 func (c *Cmd) CombinedOutput() ([]byte, error) {
     78 	c.prepare()
     79 	bytes, err := c.Cmd.CombinedOutput()
     80 	return bytes, err
     81 }
     82 
     83 // StartOrFatal is equivalent to Start, but handles the error with a call to ctx.Fatal
     84 func (c *Cmd) StartOrFatal() {
     85 	if err := c.Start(); err != nil {
     86 		c.ctx.Fatalf("Failed to run %s: %v", c.name, err)
     87 	}
     88 }
     89 
     90 func (c *Cmd) reportError(err error) {
     91 	if err == nil {
     92 		return
     93 	}
     94 	if e, ok := err.(*exec.ExitError); ok {
     95 		c.ctx.Fatalf("%s failed with: %v", c.name, e.ProcessState.String())
     96 	} else {
     97 		c.ctx.Fatalf("Failed to run %s: %v", c.name, err)
     98 	}
     99 }
    100 
    101 // RunOrFatal is equivalent to Run, but handles the error with a call to ctx.Fatal
    102 func (c *Cmd) RunOrFatal() {
    103 	c.reportError(c.Run())
    104 }
    105 
    106 // WaitOrFatal is equivalent to Wait, but handles the error with a call to ctx.Fatal
    107 func (c *Cmd) WaitOrFatal() {
    108 	c.reportError(c.Wait())
    109 }
    110 
    111 // OutputOrFatal is equivalent to Output, but handles the error with a call to ctx.Fatal
    112 func (c *Cmd) OutputOrFatal() []byte {
    113 	ret, err := c.Output()
    114 	c.reportError(err)
    115 	return ret
    116 }
    117 
    118 // CombinedOutputOrFatal is equivalent to CombinedOutput, but handles the error with
    119 // a call to ctx.Fatal
    120 func (c *Cmd) CombinedOutputOrFatal() []byte {
    121 	ret, err := c.CombinedOutput()
    122 	c.reportError(err)
    123 	return ret
    124 }
    125