github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/testutil/exec.go (about) 1 /* 2 * Copyright 2019 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package testutil 18 19 import ( 20 "fmt" 21 "io" 22 "os" 23 "os/exec" 24 "path" 25 26 "github.com/dgraph-io/dgraph/x" 27 ) 28 29 // These are exported so they can also be set directly from outside this package. 30 var ( 31 ShowOutput = os.Getenv("DEBUG_SHOW_OUTPUT") != "" 32 ShowError = os.Getenv("DEBUG_SHOW_ERROR") != "" 33 ShowCommand = os.Getenv("DEBUG_SHOW_COMMAND") != "" 34 ) 35 36 // CmdOpts sets the options to run a single command. 37 type CmdOpts struct { 38 Dir string 39 } 40 41 // RepoPath converts a path relative to the root of the repo into an absolute path. 42 func RepoPath(p string) string { 43 return path.Join(os.ExpandEnv("$GOPATH/src/github.com/dgraph-io/dgraph"), p) 44 } 45 46 // Exec runs a single external command. 47 func Exec(argv ...string) error { 48 return Pipeline([][]string{argv}) 49 } 50 51 // ExecWithOpts runs a single external command with the given options. 52 func ExecWithOpts(argv []string, opts CmdOpts) error { 53 return pipelineInternal([][]string{argv}, []CmdOpts{opts}) 54 } 55 56 // Pipeline runs several commands such that the output of one command becomes the input of the next. 57 // The first argument should be an two-dimensional array containing the commands. 58 // TODO: allow capturing output, sending to terminal, etc 59 func Pipeline(cmds [][]string) error { 60 return pipelineInternal(cmds, nil) 61 } 62 63 // piplineInternal takes a list of commands and a list of options (one for each). 64 // If opts is nil, all commands should be run with the default options. 65 func pipelineInternal(cmds [][]string, opts []CmdOpts) error { 66 x.AssertTrue(opts == nil || len(cmds) == len(opts)) 67 68 var p io.ReadCloser 69 var numCmds = len(cmds) 70 71 cmd := make([]*exec.Cmd, numCmds) 72 73 // Run all commands in parallel, connecting stdin of each to the stdout of the previous. 74 for i, c := range cmds { 75 lastCmd := i == numCmds-1 76 if ShowCommand { 77 fmt.Fprintf(os.Stderr, "%+v", c) 78 } 79 80 cmd[i] = exec.Command(c[0], c[1:]...) 81 cmd[i].Stdin = p 82 83 if opts != nil { 84 cmd[i].Dir = opts[i].Dir 85 } 86 87 if !lastCmd { 88 p, _ = cmd[i].StdoutPipe() 89 } 90 91 if ShowOutput { 92 cmd[i].Stderr = os.Stderr 93 if lastCmd { 94 cmd[i].Stdout = os.Stdout 95 } 96 } else if ShowError { 97 cmd[i].Stderr = os.Stderr 98 } 99 100 if ShowCommand { 101 if lastCmd { 102 fmt.Fprintf(os.Stderr, "\n") 103 } else { 104 fmt.Fprintf(os.Stderr, "\n| ") 105 } 106 } 107 108 err := cmd[i].Start() 109 x.Check(err) 110 } 111 112 // Make sure to properly reap all spawned processes, but only save the error from the 113 // earliest stage of the pipeline. 114 var err error 115 for i := range cmds { 116 e := cmd[i].Wait() 117 if e != nil && err == nil { 118 err = e 119 } 120 } 121 122 return err 123 }