1*9bb1b549SSpandan Das// Copyright 2021 The Bazel Authors. All rights reserved. 2*9bb1b549SSpandan Das// 3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License"); 4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License. 5*9bb1b549SSpandan Das// You may obtain a copy of the License at 6*9bb1b549SSpandan Das// 7*9bb1b549SSpandan Das// http://www.apache.org/licenses/LICENSE-2.0 8*9bb1b549SSpandan Das// 9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software 10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS, 11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and 13*9bb1b549SSpandan Das// limitations under the License. 14*9bb1b549SSpandan Das 15*9bb1b549SSpandan Daspackage main 16*9bb1b549SSpandan Das 17*9bb1b549SSpandan Dasimport ( 18*9bb1b549SSpandan Das "bytes" 19*9bb1b549SSpandan Das "context" 20*9bb1b549SSpandan Das "fmt" 21*9bb1b549SSpandan Das "os" 22*9bb1b549SSpandan Das "os/exec" 23*9bb1b549SSpandan Das "strings" 24*9bb1b549SSpandan Das) 25*9bb1b549SSpandan Das 26*9bb1b549SSpandan Das// runForError runs a command without showing its output. If the command fails, 27*9bb1b549SSpandan Das// runForError returns an error containing its stderr. 28*9bb1b549SSpandan Dasfunc runForError(ctx context.Context, dir string, name string, args ...string) error { 29*9bb1b549SSpandan Das stderr := &bytes.Buffer{} 30*9bb1b549SSpandan Das cmd := exec.CommandContext(ctx, name, args...) 31*9bb1b549SSpandan Das cmd.Env = envWithoutBazel() 32*9bb1b549SSpandan Das cmd.Dir = dir 33*9bb1b549SSpandan Das cmd.Stdout = nil 34*9bb1b549SSpandan Das cmd.Stderr = stderr 35*9bb1b549SSpandan Das err := cmd.Run() 36*9bb1b549SSpandan Das return cleanCmdError(err, name, args, stderr.Bytes()) 37*9bb1b549SSpandan Das} 38*9bb1b549SSpandan Das 39*9bb1b549SSpandan Das// runForOutput runs a command and returns its output. If the command fails, 40*9bb1b549SSpandan Das// runForOutput returns an error containing its stderr. The command's output 41*9bb1b549SSpandan Das// is returned whether it failed or not. 42*9bb1b549SSpandan Dasfunc runForOutput(ctx context.Context, dir string, name string, args ...string) ([]byte, error) { 43*9bb1b549SSpandan Das stdout := &bytes.Buffer{} 44*9bb1b549SSpandan Das stderr := &bytes.Buffer{} 45*9bb1b549SSpandan Das cmd := exec.CommandContext(ctx, name, args...) 46*9bb1b549SSpandan Das cmd.Env = envWithoutBazel() 47*9bb1b549SSpandan Das cmd.Dir = dir 48*9bb1b549SSpandan Das cmd.Stdout = stdout 49*9bb1b549SSpandan Das cmd.Stderr = stderr 50*9bb1b549SSpandan Das err := cmd.Run() 51*9bb1b549SSpandan Das return stdout.Bytes(), cleanCmdError(err, name, args, stderr.Bytes()) 52*9bb1b549SSpandan Das} 53*9bb1b549SSpandan Das 54*9bb1b549SSpandan Das// envWithoutBazel runs the current process's environment without variables 55*9bb1b549SSpandan Das// starting with "BUILD_" added by 'bazel run'. These can confuse subprocesses. 56*9bb1b549SSpandan Dasfunc envWithoutBazel() []string { 57*9bb1b549SSpandan Das env := os.Environ() 58*9bb1b549SSpandan Das filtered := make([]string, 0, len(env)) 59*9bb1b549SSpandan Das for _, e := range env { 60*9bb1b549SSpandan Das if strings.HasPrefix(e, "BUILD_") { 61*9bb1b549SSpandan Das continue 62*9bb1b549SSpandan Das } 63*9bb1b549SSpandan Das filtered = append(filtered, e) 64*9bb1b549SSpandan Das } 65*9bb1b549SSpandan Das return filtered 66*9bb1b549SSpandan Das} 67*9bb1b549SSpandan Das 68*9bb1b549SSpandan Das// cleanCmdError wraps an error returned by exec.Cmd.Run with the command that 69*9bb1b549SSpandan Das// was run and its stderr output. 70*9bb1b549SSpandan Dasfunc cleanCmdError(err error, name string, args []string, stderr []byte) error { 71*9bb1b549SSpandan Das if err == nil { 72*9bb1b549SSpandan Das return nil 73*9bb1b549SSpandan Das } 74*9bb1b549SSpandan Das return &commandError{ 75*9bb1b549SSpandan Das argv: append([]string{name}, args...), 76*9bb1b549SSpandan Das err: err, 77*9bb1b549SSpandan Das } 78*9bb1b549SSpandan Das} 79*9bb1b549SSpandan Das 80*9bb1b549SSpandan Dastype commandError struct { 81*9bb1b549SSpandan Das argv []string 82*9bb1b549SSpandan Das stderr []byte 83*9bb1b549SSpandan Das err error 84*9bb1b549SSpandan Das} 85*9bb1b549SSpandan Das 86*9bb1b549SSpandan Dasfunc (e *commandError) Error() string { 87*9bb1b549SSpandan Das return fmt.Sprintf("running %s: %v\n%s", strings.Join(e.argv, " "), e.err, bytes.TrimSpace(e.stderr)) 88*9bb1b549SSpandan Das} 89*9bb1b549SSpandan Das 90*9bb1b549SSpandan Dasfunc (e *commandError) Unwrap() error { 91*9bb1b549SSpandan Das return e.err 92*9bb1b549SSpandan Das} 93