xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/env.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package main
6
7import (
8	"bytes"
9	"fmt"
10	"io"
11	"os"
12	"strings"
13	"syscall"
14	"time"
15)
16
17const artifactsTmpDirEnvName = "CROS_ARTIFACTS_TMP_DIR"
18
19type env interface {
20	umask(int) int
21	getenv(key string) (string, bool)
22	environ() []string
23	getwd() string
24	stdin() io.Reader
25	stdout() io.Writer
26	stderr() io.Writer
27	run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error
28	runWithTimeout(cmd *command, duration time.Duration) error
29	exec(cmd *command) error
30}
31
32type processEnv struct {
33	wd string
34}
35
36func newProcessEnv() (env, error) {
37	wd, err := os.Getwd()
38	if err != nil {
39		return nil, wrapErrorwithSourceLocf(err, "failed to read working directory")
40	}
41
42	// Note: On Linux, Getwd may resolve to /proc/self/cwd, since it checks the PWD environment
43	// variable. We need to read the link to get the actual working directory. We can't always
44	// do this as we are calculating the path to clang, since following a symlinked cwd first
45	// would make this calculation invalid.
46	//
47	// FIXME(gbiv): It's not clear why always Readlink()ing here an issue. crrev.com/c/1764624
48	// might provide helpful context?
49	if wd == "/proc/self/cwd" {
50		wd, err = os.Readlink(wd)
51		if err != nil {
52			return nil, wrapErrorwithSourceLocf(err, "resolving /proc/self/cwd")
53		}
54	}
55
56	return &processEnv{wd: wd}, nil
57}
58
59var _ env = (*processEnv)(nil)
60
61func (env *processEnv) umask(newmask int) (oldmask int) {
62	return syscall.Umask(newmask)
63}
64
65func (env *processEnv) getenv(key string) (string, bool) {
66	return os.LookupEnv(key)
67}
68
69func (env *processEnv) environ() []string {
70	return os.Environ()
71}
72
73func (env *processEnv) getwd() string {
74	return env.wd
75}
76
77func (env *processEnv) stdin() io.Reader {
78	return os.Stdin
79}
80
81func (env *processEnv) stdout() io.Writer {
82	return os.Stdout
83}
84
85func (env *processEnv) stderr() io.Writer {
86	return os.Stderr
87}
88
89func (env *processEnv) exec(cmd *command) error {
90	return execCmd(env, cmd)
91}
92
93func (env *processEnv) runWithTimeout(cmd *command, duration time.Duration) error {
94	return runCmdWithTimeout(env, cmd, duration)
95}
96
97func (env *processEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
98	return runCmd(env, cmd, stdin, stdout, stderr)
99}
100
101type commandRecordingEnv struct {
102	env
103	stdinReader io.Reader
104	cmdResults  []*commandResult
105}
106type commandResult struct {
107	Cmd      *command `json:"cmd"`
108	Stdout   string   `json:"stdout,omitempty"`
109	Stderr   string   `json:"stderr,omitempty"`
110	ExitCode int      `json:"exitcode,omitempty"`
111}
112
113var _ env = (*commandRecordingEnv)(nil)
114
115func (env *commandRecordingEnv) stdin() io.Reader {
116	return env.stdinReader
117}
118
119func (env *commandRecordingEnv) exec(cmd *command) error {
120	// Note: We treat exec the same as run so that we can do work
121	// after the call.
122	return env.run(cmd, env.stdin(), env.stdout(), env.stderr())
123}
124
125func (env *commandRecordingEnv) runWithTimeout(cmd *command, duration time.Duration) error {
126	return runCmdWithTimeout(env, cmd, duration)
127}
128
129func (env *commandRecordingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
130	stdoutBuffer := &bytes.Buffer{}
131	stderrBuffer := &bytes.Buffer{}
132	err := env.env.run(cmd, stdin, io.MultiWriter(stdout, stdoutBuffer), io.MultiWriter(stderr, stderrBuffer))
133	if exitCode, ok := getExitCode(err); ok {
134		env.cmdResults = append(env.cmdResults, &commandResult{
135			Cmd:      cmd,
136			Stdout:   stdoutBuffer.String(),
137			Stderr:   stderrBuffer.String(),
138			ExitCode: exitCode,
139		})
140	}
141	return err
142}
143
144type printingEnv struct {
145	env
146}
147
148var _ env = (*printingEnv)(nil)
149
150func (env *printingEnv) exec(cmd *command) error {
151	printCmd(env, cmd)
152	return env.env.exec(cmd)
153}
154
155func (env *printingEnv) runWithTimeout(cmd *command, duration time.Duration) error {
156	printCmd(env, cmd)
157	return env.env.runWithTimeout(cmd, duration)
158}
159
160func (env *printingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
161	printCmd(env, cmd)
162	return env.env.run(cmd, stdin, stdout, stderr)
163}
164
165func printCmd(env env, cmd *command) {
166	fmt.Fprintf(env.stderr(), "cd '%s' &&", env.getwd())
167	if len(cmd.EnvUpdates) > 0 {
168		fmt.Fprintf(env.stderr(), " env '%s'", strings.Join(cmd.EnvUpdates, "' '"))
169	}
170	fmt.Fprintf(env.stderr(), " '%s'", getAbsCmdPath(env, cmd))
171	if len(cmd.Args) > 0 {
172		fmt.Fprintf(env.stderr(), " '%s'", strings.Join(cmd.Args, "' '"))
173	}
174	io.WriteString(env.stderr(), "\n")
175}
176
177func getCompilerArtifactsDir(env env) string {
178	const defaultArtifactDir = "/tmp"
179	value, _ := env.getenv(artifactsTmpDirEnvName)
180	if value == "" {
181		fmt.Fprintf(env.stdout(), "$%s is not set, artifacts will be written to %s", artifactsTmpDirEnvName, defaultArtifactDir)
182		return defaultArtifactDir
183	}
184	return value
185
186}
187