| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io" |
| "os" |
| "strings" |
| "syscall" |
| "time" |
| ) |
| |
| type env interface { |
| umask(int) int |
| getenv(key string) (string, bool) |
| environ() []string |
| getwd() string |
| stdin() io.Reader |
| stdout() io.Writer |
| stderr() io.Writer |
| run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error |
| runWithTimeout(cmd *command, duration time.Duration) error |
| exec(cmd *command) error |
| } |
| |
| type processEnv struct { |
| wd string |
| } |
| |
| func newProcessEnv() (env, error) { |
| wd, err := os.Getwd() |
| if err != nil { |
| return nil, wrapErrorwithSourceLocf(err, "failed to read working directory") |
| } |
| |
| // Note: On Linux, Getwd may resolve to /proc/self/cwd, since it checks the PWD environment |
| // variable. We need to read the link to get the actual working directory. We can't always |
| // do this as we are calculating the path to clang, since following a symlinked cwd first |
| // would make this calculation invalid. |
| // |
| // FIXME(gbiv): It's not clear why always Readlink()ing here an issue. crrev.com/c/1764624 |
| // might provide helpful context? |
| if wd == "/proc/self/cwd" { |
| wd, err = os.Readlink(wd) |
| if err != nil { |
| return nil, wrapErrorwithSourceLocf(err, "resolving /proc/self/cwd") |
| } |
| } |
| |
| return &processEnv{wd: wd}, nil |
| } |
| |
| var _ env = (*processEnv)(nil) |
| |
| func (env *processEnv) umask(newmask int) (oldmask int) { |
| return syscall.Umask(newmask) |
| } |
| |
| func (env *processEnv) getenv(key string) (string, bool) { |
| return os.LookupEnv(key) |
| } |
| |
| func (env *processEnv) environ() []string { |
| return os.Environ() |
| } |
| |
| func (env *processEnv) getwd() string { |
| return env.wd |
| } |
| |
| func (env *processEnv) stdin() io.Reader { |
| return os.Stdin |
| } |
| |
| 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 { |
| return execCmd(env, cmd) |
| } |
| |
| func (env *processEnv) runWithTimeout(cmd *command, duration time.Duration) error { |
| return runCmdWithTimeout(env, cmd, duration) |
| } |
| |
| func (env *processEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { |
| return runCmd(env, cmd, stdin, stdout, stderr) |
| } |
| |
| type commandRecordingEnv struct { |
| env |
| stdinReader io.Reader |
| cmdResults []*commandResult |
| } |
| type commandResult struct { |
| Cmd *command `json:"cmd"` |
| Stdout string `json:"stdout,omitempty"` |
| Stderr string `json:"stderr,omitempty"` |
| ExitCode int `json:"exitcode,omitempty"` |
| } |
| |
| var _ env = (*commandRecordingEnv)(nil) |
| |
| func (env *commandRecordingEnv) stdin() io.Reader { |
| return env.stdinReader |
| } |
| |
| func (env *commandRecordingEnv) exec(cmd *command) error { |
| // Note: We treat exec the same as run so that we can do work |
| // after the call. |
| return env.run(cmd, env.stdin(), env.stdout(), env.stderr()) |
| } |
| |
| func (env *commandRecordingEnv) runWithTimeout(cmd *command, duration time.Duration) error { |
| return env.runWithTimeout(cmd, duration) |
| } |
| |
| func (env *commandRecordingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { |
| stdoutBuffer := &bytes.Buffer{} |
| stderrBuffer := &bytes.Buffer{} |
| err := env.env.run(cmd, stdin, 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 |
| } |
| |
| type printingEnv struct { |
| env |
| } |
| |
| var _env = (*printingEnv)(nil) |
| |
| func (env *printingEnv) exec(cmd *command) error { |
| printCmd(env, cmd) |
| return env.env.exec(cmd) |
| } |
| |
| func (env *printingEnv) runWithTimeout(cmd *command, duration time.Duration) error { |
| printCmd(env, cmd) |
| return env.env.runWithTimeout(cmd, duration) |
| } |
| |
| func (env *printingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { |
| printCmd(env, cmd) |
| return env.env.run(cmd, stdin, stdout, stderr) |
| } |
| |
| func printCmd(env env, cmd *command) { |
| fmt.Fprintf(env.stderr(), "cd '%s' &&", env.getwd()) |
| if len(cmd.EnvUpdates) > 0 { |
| fmt.Fprintf(env.stderr(), " env '%s'", strings.Join(cmd.EnvUpdates, "' '")) |
| } |
| fmt.Fprintf(env.stderr(), " '%s'", getAbsCmdPath(env, cmd)) |
| if len(cmd.Args) > 0 { |
| fmt.Fprintf(env.stderr(), " '%s'", strings.Join(cmd.Args, "' '")) |
| } |
| io.WriteString(env.stderr(), "\n") |
| } |