diff options
author | Tobias Bosch <tbosch@google.com> | 2019-06-24 09:31:39 -0700 |
---|---|---|
committer | Tobias Bosch <tbosch@google.com> | 2019-06-28 19:00:23 +0000 |
commit | 900dbc92800d8fc927905db29cb302461054cf97 (patch) | |
tree | c74ecbfb01ee4ee679090139596aef3546f19d9b /compiler_wrapper/env.go | |
parent | 739e6abb2cd03b60e579df31ad55870a4a00260a (diff) | |
download | toolchain-utils-900dbc92800d8fc927905db29cb302461054cf97.tar.gz |
Introduce infrastructure for calling and testing nested
commands, error messages and exit codes.
Also:
- implements the -Xclang-path= flag as use case of calling
a nested command.
- adds tests for forwarding errors, comparing against the
old wrapper, and exit codes.
- captures the source locations of errors in error messages.
- compares exit codes of new wrapper and old wrapper.
BUG=chromium:773875
TEST=unit test
Change-Id: I919e58091d093d68939809f676f799a68ec7a34e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1676833
Reviewed-by: George Burgess <gbiv@chromium.org>
Tested-by: Tobias Bosch <tbosch@google.com>
Diffstat (limited to 'compiler_wrapper/env.go')
-rw-r--r-- | compiler_wrapper/env.go | 64 |
1 files changed, 63 insertions, 1 deletions
diff --git a/compiler_wrapper/env.go b/compiler_wrapper/env.go index 3c106d4a..6cf1a1b9 100644 --- a/compiler_wrapper/env.go +++ b/compiler_wrapper/env.go @@ -1,13 +1,20 @@ package main import ( + "bytes" + "io" "os" + "syscall" ) type env interface { getenv(key string) string environ() []string getwd() string + stdout() io.Writer + stderr() io.Writer + run(cmd *command, stdout io.Writer, stderr io.Writer) error + exec(cmd *command) error } type processEnv struct { @@ -17,7 +24,7 @@ type processEnv struct { func newProcessEnv() (env, error) { wd, err := os.Getwd() if err != nil { - return nil, err + return nil, wrapErrorwithSourceLocf(err, "failed to read working directory") } return &processEnv{wd: wd}, nil } @@ -35,3 +42,58 @@ func (env *processEnv) environ() []string { func (env *processEnv) getwd() string { return env.wd } + +func (env *processEnv) stdout() io.Writer { + return os.Stdout +} + +func (env *processEnv) stderr() io.Writer { + return os.Stderr +} + +func (env *processEnv) exec(cmd *command) error { + execCmd := newExecCmd(env, cmd) + return syscall.Exec(execCmd.Path, execCmd.Args, execCmd.Env) +} + +func (env *processEnv) run(cmd *command, stdout io.Writer, stderr io.Writer) error { + execCmd := newExecCmd(env, cmd) + execCmd.Stdout = stdout + execCmd.Stderr = stderr + return execCmd.Run() +} + +type commandRecordingEnv struct { + env + cmdResults []*commandResult +} +type commandResult struct { + cmd *command + stdout string + stderr string + exitCode int +} + +var _ env = (*commandRecordingEnv)(nil) + +func (env *commandRecordingEnv) exec(cmd *command) error { + // Note: We will only get here if the exec failed, + // e.g. when the underlying command could not be called. + // In this case, we don't compare commands, so nothing to record. + return env.exec(cmd) +} + +func (env *commandRecordingEnv) run(cmd *command, stdout io.Writer, stderr io.Writer) error { + stdoutBuffer := &bytes.Buffer{} + stderrBuffer := &bytes.Buffer{} + err := env.env.run(cmd, io.MultiWriter(stdout, stdoutBuffer), io.MultiWriter(stderr, stderrBuffer)) + if exitCode, ok := getExitCode(err); ok { + env.cmdResults = append(env.cmdResults, &commandResult{ + cmd: cmd, + stdout: stdoutBuffer.String(), + stderr: stderrBuffer.String(), + exitCode: exitCode, + }) + } + return err +} |