xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/compiler_wrapper.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li// Copyright 2019 The ChromiumOS Authors
2*760c253cSXin Li// Use of this source code is governed by a BSD-style license that can be
3*760c253cSXin Li// found in the LICENSE file.
4*760c253cSXin Li
5*760c253cSXin Lipackage main
6*760c253cSXin Li
7*760c253cSXin Liimport (
8*760c253cSXin Li	"bytes"
9*760c253cSXin Li	"context"
10*760c253cSXin Li	"errors"
11*760c253cSXin Li	"fmt"
12*760c253cSXin Li	"io"
13*760c253cSXin Li	"path/filepath"
14*760c253cSXin Li	"strconv"
15*760c253cSXin Li	"strings"
16*760c253cSXin Li	"time"
17*760c253cSXin Li)
18*760c253cSXin Li
19*760c253cSXin Liconst (
20*760c253cSXin Li	clangCrashArtifactsSubdir = "toolchain/clang_crash_diagnostics"
21*760c253cSXin Li	crosArtifactsEnvVar       = "CROS_ARTIFACTS_TMP_DIR"
22*760c253cSXin Li)
23*760c253cSXin Li
24*760c253cSXin Lifunc callCompiler(env env, cfg *config, inputCmd *command) int {
25*760c253cSXin Li	var compilerErr error
26*760c253cSXin Li
27*760c253cSXin Li	if !filepath.IsAbs(inputCmd.Path) && !strings.HasPrefix(inputCmd.Path, ".") &&
28*760c253cSXin Li		!strings.ContainsRune(inputCmd.Path, filepath.Separator) {
29*760c253cSXin Li		if resolvedPath, err := resolveAgainstPathEnv(env, inputCmd.Path); err == nil {
30*760c253cSXin Li			inputCmd = &command{
31*760c253cSXin Li				Path:       resolvedPath,
32*760c253cSXin Li				Args:       inputCmd.Args,
33*760c253cSXin Li				EnvUpdates: inputCmd.EnvUpdates,
34*760c253cSXin Li			}
35*760c253cSXin Li		} else {
36*760c253cSXin Li			compilerErr = err
37*760c253cSXin Li		}
38*760c253cSXin Li	}
39*760c253cSXin Li	exitCode := 0
40*760c253cSXin Li	if compilerErr == nil {
41*760c253cSXin Li		exitCode, compilerErr = callCompilerInternal(env, cfg, inputCmd)
42*760c253cSXin Li	}
43*760c253cSXin Li	if compilerErr != nil {
44*760c253cSXin Li		printCompilerError(env.stderr(), compilerErr)
45*760c253cSXin Li		exitCode = 1
46*760c253cSXin Li	}
47*760c253cSXin Li	return exitCode
48*760c253cSXin Li}
49*760c253cSXin Li
50*760c253cSXin Li// Given the main builder path and the absolute path to our wrapper, returns the path to the
51*760c253cSXin Li// 'real' compiler we should invoke.
52*760c253cSXin Lifunc calculateAndroidWrapperPath(mainBuilderPath string, absWrapperPath string) string {
53*760c253cSXin Li	// FIXME: This combination of using the directory of the symlink but the basename of the
54*760c253cSXin Li	// link target is strange but is the logic that old android wrapper uses. Change this to use
55*760c253cSXin Li	// directory and basename either from the absWrapperPath or from the builder.path, but don't
56*760c253cSXin Li	// mix anymore.
57*760c253cSXin Li
58*760c253cSXin Li	// We need to be careful here: path.Join Clean()s its result, so `./foo` will get
59*760c253cSXin Li	// transformed to `foo`, which isn't good since we're passing this path to exec.
60*760c253cSXin Li	basePart := filepath.Base(absWrapperPath) + ".real"
61*760c253cSXin Li	if !strings.ContainsRune(mainBuilderPath, filepath.Separator) {
62*760c253cSXin Li		return basePart
63*760c253cSXin Li	}
64*760c253cSXin Li
65*760c253cSXin Li	dirPart := filepath.Dir(mainBuilderPath)
66*760c253cSXin Li	if cleanResult := filepath.Join(dirPart, basePart); strings.ContainsRune(cleanResult, filepath.Separator) {
67*760c253cSXin Li		return cleanResult
68*760c253cSXin Li	}
69*760c253cSXin Li
70*760c253cSXin Li	return "." + string(filepath.Separator) + basePart
71*760c253cSXin Li}
72*760c253cSXin Li
73*760c253cSXin Lifunc runAndroidClangTidy(env env, cmd *command) error {
74*760c253cSXin Li	timeout, found := env.getenv("TIDY_TIMEOUT")
75*760c253cSXin Li	if !found {
76*760c253cSXin Li		return env.exec(cmd)
77*760c253cSXin Li	}
78*760c253cSXin Li	seconds, err := strconv.Atoi(timeout)
79*760c253cSXin Li	if err != nil || seconds == 0 {
80*760c253cSXin Li		return env.exec(cmd)
81*760c253cSXin Li	}
82*760c253cSXin Li	getSourceFile := func() string {
83*760c253cSXin Li		// Note: This depends on Android build system's clang-tidy command line format.
84*760c253cSXin Li		// Last non-flag before "--" in cmd.Args is used as the source file name.
85*760c253cSXin Li		sourceFile := "unknown_file"
86*760c253cSXin Li		for _, arg := range cmd.Args {
87*760c253cSXin Li			if arg == "--" {
88*760c253cSXin Li				break
89*760c253cSXin Li			}
90*760c253cSXin Li			if strings.HasPrefix(arg, "-") {
91*760c253cSXin Li				continue
92*760c253cSXin Li			}
93*760c253cSXin Li			sourceFile = arg
94*760c253cSXin Li		}
95*760c253cSXin Li		return sourceFile
96*760c253cSXin Li	}
97*760c253cSXin Li	startTime := time.Now()
98*760c253cSXin Li	err = env.runWithTimeout(cmd, time.Duration(seconds)*time.Second)
99*760c253cSXin Li	if !errors.Is(err, context.DeadlineExceeded) {
100*760c253cSXin Li		// When used time is over half of TIDY_TIMEOUT, give a warning.
101*760c253cSXin Li		// These warnings allow users to fix slow jobs before they get worse.
102*760c253cSXin Li		usedSeconds := int(time.Since(startTime) / time.Second)
103*760c253cSXin Li		if usedSeconds > seconds/2 {
104*760c253cSXin Li			warning := "%s:1:1: warning: clang-tidy used %d seconds.\n"
105*760c253cSXin Li			fmt.Fprintf(env.stdout(), warning, getSourceFile(), usedSeconds)
106*760c253cSXin Li		}
107*760c253cSXin Li		return err
108*760c253cSXin Li	}
109*760c253cSXin Li	// When DeadllineExceeded, print warning messages.
110*760c253cSXin Li	warning := "%s:1:1: warning: clang-tidy aborted after %d seconds.\n"
111*760c253cSXin Li	fmt.Fprintf(env.stdout(), warning, getSourceFile(), seconds)
112*760c253cSXin Li	fmt.Fprintf(env.stdout(), "TIMEOUT: %s %s\n", cmd.Path, strings.Join(cmd.Args, " "))
113*760c253cSXin Li	// Do not stop Android build. Just give a warning and return no error.
114*760c253cSXin Li	return nil
115*760c253cSXin Li}
116*760c253cSXin Li
117*760c253cSXin Lifunc detectCrashArtifactsDir(env env, cfg *config) string {
118*760c253cSXin Li	if cfg.isAndroidWrapper {
119*760c253cSXin Li		return ""
120*760c253cSXin Li	}
121*760c253cSXin Li
122*760c253cSXin Li	tmpdir, ok := env.getenv(crosArtifactsEnvVar)
123*760c253cSXin Li	if !ok {
124*760c253cSXin Li		return ""
125*760c253cSXin Li	}
126*760c253cSXin Li	return filepath.Join(tmpdir, clangCrashArtifactsSubdir)
127*760c253cSXin Li}
128*760c253cSXin Li
129*760c253cSXin Lifunc callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
130*760c253cSXin Li	if err := checkUnsupportedFlags(inputCmd); err != nil {
131*760c253cSXin Li		return 0, err
132*760c253cSXin Li	}
133*760c253cSXin Li	mainBuilder, err := newCommandBuilder(env, cfg, inputCmd)
134*760c253cSXin Li	if err != nil {
135*760c253cSXin Li		return 0, err
136*760c253cSXin Li	}
137*760c253cSXin Li	processPrintConfigFlag(mainBuilder)
138*760c253cSXin Li	processPrintCmdlineFlag(mainBuilder)
139*760c253cSXin Li	env = mainBuilder.env
140*760c253cSXin Li	var compilerCmd *command
141*760c253cSXin Li	disableWerrorConfig := processForceDisableWerrorFlag(env, cfg, mainBuilder)
142*760c253cSXin Li	clangSyntax := processClangSyntaxFlag(mainBuilder)
143*760c253cSXin Li
144*760c253cSXin Li	rusageEnabled := isRusageEnabled(env)
145*760c253cSXin Li
146*760c253cSXin Li	// Disable CCache for rusage logs
147*760c253cSXin Li	// Note: Disabling Goma causes timeout related INFRA_FAILUREs in builders
148*760c253cSXin Li	allowCCache := !rusageEnabled
149*760c253cSXin Li	remoteBuildUsed := false
150*760c253cSXin Li
151*760c253cSXin Li	workAroundKernelBugWithRetries := false
152*760c253cSXin Li	if cfg.isAndroidWrapper {
153*760c253cSXin Li		mainBuilder.path = calculateAndroidWrapperPath(mainBuilder.path, mainBuilder.absWrapperPath)
154*760c253cSXin Li		switch mainBuilder.target.compilerType {
155*760c253cSXin Li		case clangType:
156*760c253cSXin Li			mainBuilder.addPreUserArgs(mainBuilder.cfg.clangFlags...)
157*760c253cSXin Li			mainBuilder.addPreUserArgs(mainBuilder.cfg.commonFlags...)
158*760c253cSXin Li			mainBuilder.addPostUserArgs(mainBuilder.cfg.clangPostFlags...)
159*760c253cSXin Li			inheritGomaFromEnv := true
160*760c253cSXin Li			// Android doesn't support rewrapper; don't try to use it.
161*760c253cSXin Li			if remoteBuildUsed, err = processGomaCccFlags(mainBuilder, inheritGomaFromEnv); err != nil {
162*760c253cSXin Li				return 0, err
163*760c253cSXin Li			}
164*760c253cSXin Li			compilerCmd = mainBuilder.build()
165*760c253cSXin Li		case clangTidyType:
166*760c253cSXin Li			compilerCmd = mainBuilder.build()
167*760c253cSXin Li		default:
168*760c253cSXin Li			return 0, newErrorwithSourceLocf("unsupported compiler: %s", mainBuilder.target.compiler)
169*760c253cSXin Li		}
170*760c253cSXin Li	} else {
171*760c253cSXin Li		cSrcFile, tidyFlags, tidyMode := processClangTidyFlags(mainBuilder)
172*760c253cSXin Li		crashArtifactsDir := detectCrashArtifactsDir(env, cfg)
173*760c253cSXin Li		if mainBuilder.target.compilerType == clangType {
174*760c253cSXin Li			err := prepareClangCommand(crashArtifactsDir, mainBuilder)
175*760c253cSXin Li			if err != nil {
176*760c253cSXin Li				return 0, err
177*760c253cSXin Li			}
178*760c253cSXin Li			if tidyMode != tidyModeNone {
179*760c253cSXin Li				allowCCache = false
180*760c253cSXin Li				// Remove and ignore goma flags.
181*760c253cSXin Li				_, err := removeOneUserCmdlineFlagWithValue(mainBuilder, "--gomacc-path")
182*760c253cSXin Li				if err != nil && err != errNoSuchCmdlineArg {
183*760c253cSXin Li					return 0, err
184*760c253cSXin Li				}
185*760c253cSXin Li
186*760c253cSXin Li				clangCmdWithoutRemoteBuildAndCCache := mainBuilder.build()
187*760c253cSXin Li
188*760c253cSXin Li				switch tidyMode {
189*760c253cSXin Li				case tidyModeTricium:
190*760c253cSXin Li					err = runClangTidyForTricium(env, clangCmdWithoutRemoteBuildAndCCache, cSrcFile, tidyFlags, crashArtifactsDir)
191*760c253cSXin Li				case tidyModeAll:
192*760c253cSXin Li					err = runClangTidy(env, clangCmdWithoutRemoteBuildAndCCache, cSrcFile, tidyFlags)
193*760c253cSXin Li				default:
194*760c253cSXin Li					panic(fmt.Sprintf("Unknown tidy mode: %v", tidyMode))
195*760c253cSXin Li				}
196*760c253cSXin Li
197*760c253cSXin Li				if err != nil {
198*760c253cSXin Li					return 0, err
199*760c253cSXin Li				}
200*760c253cSXin Li			}
201*760c253cSXin Li
202*760c253cSXin Li			if remoteBuildUsed, err = processRemoteBuildAndCCacheFlags(allowCCache, mainBuilder); err != nil {
203*760c253cSXin Li				return 0, err
204*760c253cSXin Li			}
205*760c253cSXin Li			compilerCmd = mainBuilder.build()
206*760c253cSXin Li		} else {
207*760c253cSXin Li			if clangSyntax {
208*760c253cSXin Li				allowCCache = false
209*760c253cSXin Li				_, clangCmd, err := calcClangCommand(crashArtifactsDir, allowCCache, mainBuilder.clone())
210*760c253cSXin Li				if err != nil {
211*760c253cSXin Li					return 0, err
212*760c253cSXin Li				}
213*760c253cSXin Li				_, gccCmd, err := calcGccCommand(rusageEnabled, mainBuilder)
214*760c253cSXin Li				if err != nil {
215*760c253cSXin Li					return 0, err
216*760c253cSXin Li				}
217*760c253cSXin Li				return checkClangSyntax(env, clangCmd, gccCmd)
218*760c253cSXin Li			}
219*760c253cSXin Li			remoteBuildUsed, compilerCmd, err = calcGccCommand(rusageEnabled, mainBuilder)
220*760c253cSXin Li			if err != nil {
221*760c253cSXin Li				return 0, err
222*760c253cSXin Li			}
223*760c253cSXin Li			workAroundKernelBugWithRetries = true
224*760c253cSXin Li		}
225*760c253cSXin Li	}
226*760c253cSXin Li
227*760c253cSXin Li	// If builds matching some heuristic should crash, crash them. Since this is purely a
228*760c253cSXin Li	// debugging tool, don't offer any nice features with it (e.g., rusage, ...).
229*760c253cSXin Li	if shouldUseCrashBuildsHeuristic && mainBuilder.target.compilerType == clangType {
230*760c253cSXin Li		return buildWithAutocrash(env, cfg, compilerCmd)
231*760c253cSXin Li	}
232*760c253cSXin Li
233*760c253cSXin Li	bisectStage := getBisectStage(env)
234*760c253cSXin Li
235*760c253cSXin Li	if rusageEnabled {
236*760c253cSXin Li		compilerCmd = removeRusageFromCommand(compilerCmd)
237*760c253cSXin Li	}
238*760c253cSXin Li
239*760c253cSXin Li	if disableWerrorConfig.enabled {
240*760c253cSXin Li		if bisectStage != "" {
241*760c253cSXin Li			return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
242*760c253cSXin Li		}
243*760c253cSXin Li		return doubleBuildWithWNoError(env, cfg, compilerCmd, disableWerrorConfig)
244*760c253cSXin Li	}
245*760c253cSXin Li	if shouldCompileWithFallback(env) {
246*760c253cSXin Li		if rusageEnabled {
247*760c253cSXin Li			return 0, newUserErrorf("TOOLCHAIN_RUSAGE_OUTPUT is meaningless with ANDROID_LLVM_PREBUILT_COMPILER_PATH")
248*760c253cSXin Li		}
249*760c253cSXin Li		if bisectStage != "" {
250*760c253cSXin Li			return 0, newUserErrorf("BISECT_STAGE is meaningless with ANDROID_LLVM_PREBUILT_COMPILER_PATH")
251*760c253cSXin Li		}
252*760c253cSXin Li		return compileWithFallback(env, cfg, compilerCmd, mainBuilder.absWrapperPath)
253*760c253cSXin Li	}
254*760c253cSXin Li	if bisectStage != "" {
255*760c253cSXin Li		if rusageEnabled {
256*760c253cSXin Li			return 0, newUserErrorf("TOOLCHAIN_RUSAGE_OUTPUT is meaningless with BISECT_STAGE")
257*760c253cSXin Li		}
258*760c253cSXin Li		compilerCmd, err = calcBisectCommand(env, cfg, bisectStage, compilerCmd)
259*760c253cSXin Li		if err != nil {
260*760c253cSXin Li			return 0, err
261*760c253cSXin Li		}
262*760c253cSXin Li	}
263*760c253cSXin Li
264*760c253cSXin Li	errRetryCompilation := errors.New("compilation retry requested")
265*760c253cSXin Li	var runCompiler func(willLogRusage bool) (int, error)
266*760c253cSXin Li	if !workAroundKernelBugWithRetries {
267*760c253cSXin Li		runCompiler = func(willLogRusage bool) (int, error) {
268*760c253cSXin Li			var err error
269*760c253cSXin Li			if willLogRusage {
270*760c253cSXin Li				err = env.run(compilerCmd, env.stdin(), env.stdout(), env.stderr())
271*760c253cSXin Li			} else if cfg.isAndroidWrapper && mainBuilder.target.compilerType == clangTidyType {
272*760c253cSXin Li				// Only clang-tidy has timeout feature now.
273*760c253cSXin Li				err = runAndroidClangTidy(env, compilerCmd)
274*760c253cSXin Li			} else {
275*760c253cSXin Li				// Note: We return from this in non-fatal circumstances only if the
276*760c253cSXin Li				// underlying env is not really doing an exec, e.g. commandRecordingEnv.
277*760c253cSXin Li				err = env.exec(compilerCmd)
278*760c253cSXin Li			}
279*760c253cSXin Li			return wrapSubprocessErrorWithSourceLoc(compilerCmd, err)
280*760c253cSXin Li		}
281*760c253cSXin Li	} else {
282*760c253cSXin Li		getStdin, err := prebufferStdinIfNeeded(env, compilerCmd)
283*760c253cSXin Li		if err != nil {
284*760c253cSXin Li			return 0, wrapErrorwithSourceLocf(err, "prebuffering stdin: %v", err)
285*760c253cSXin Li		}
286*760c253cSXin Li
287*760c253cSXin Li		stdoutBuffer := &bytes.Buffer{}
288*760c253cSXin Li		stderrBuffer := &bytes.Buffer{}
289*760c253cSXin Li		retryAttempt := 0
290*760c253cSXin Li		runCompiler = func(willLogRusage bool) (int, error) {
291*760c253cSXin Li			retryAttempt++
292*760c253cSXin Li			stdoutBuffer.Reset()
293*760c253cSXin Li			stderrBuffer.Reset()
294*760c253cSXin Li
295*760c253cSXin Li			exitCode, compilerErr := wrapSubprocessErrorWithSourceLoc(compilerCmd,
296*760c253cSXin Li				env.run(compilerCmd, getStdin(), stdoutBuffer, stderrBuffer))
297*760c253cSXin Li
298*760c253cSXin Li			if compilerErr != nil || exitCode != 0 {
299*760c253cSXin Li				if retryAttempt < kernelBugRetryLimit && (errorContainsTracesOfKernelBug(compilerErr) || containsTracesOfKernelBug(stdoutBuffer.Bytes()) || containsTracesOfKernelBug(stderrBuffer.Bytes())) {
300*760c253cSXin Li					return exitCode, errRetryCompilation
301*760c253cSXin Li				}
302*760c253cSXin Li			}
303*760c253cSXin Li			_, stdoutErr := stdoutBuffer.WriteTo(env.stdout())
304*760c253cSXin Li			_, stderrErr := stderrBuffer.WriteTo(env.stderr())
305*760c253cSXin Li			if stdoutErr != nil {
306*760c253cSXin Li				return exitCode, wrapErrorwithSourceLocf(err, "writing stdout: %v", stdoutErr)
307*760c253cSXin Li			}
308*760c253cSXin Li			if stderrErr != nil {
309*760c253cSXin Li				return exitCode, wrapErrorwithSourceLocf(err, "writing stderr: %v", stderrErr)
310*760c253cSXin Li			}
311*760c253cSXin Li			return exitCode, compilerErr
312*760c253cSXin Li		}
313*760c253cSXin Li	}
314*760c253cSXin Li
315*760c253cSXin Li	for {
316*760c253cSXin Li		var exitCode int
317*760c253cSXin Li		commitRusage, err := maybeCaptureRusage(env, compilerCmd, func(willLogRusage bool) error {
318*760c253cSXin Li			var err error
319*760c253cSXin Li			exitCode, err = runCompiler(willLogRusage)
320*760c253cSXin Li			return err
321*760c253cSXin Li		})
322*760c253cSXin Li
323*760c253cSXin Li		switch {
324*760c253cSXin Li		case err == errRetryCompilation:
325*760c253cSXin Li			// Loop around again.
326*760c253cSXin Li		case err != nil:
327*760c253cSXin Li			return exitCode, err
328*760c253cSXin Li		default:
329*760c253cSXin Li			if !remoteBuildUsed {
330*760c253cSXin Li				if err := commitRusage(exitCode); err != nil {
331*760c253cSXin Li					return exitCode, fmt.Errorf("commiting rusage: %v", err)
332*760c253cSXin Li				}
333*760c253cSXin Li			}
334*760c253cSXin Li			return exitCode, err
335*760c253cSXin Li		}
336*760c253cSXin Li	}
337*760c253cSXin Li}
338*760c253cSXin Li
339*760c253cSXin Lifunc hasUserArg(argName string, builder *commandBuilder) bool {
340*760c253cSXin Li	for _, argValue := range builder.args {
341*760c253cSXin Li		if strings.Contains(argValue.value, argName) && argValue.fromUser {
342*760c253cSXin Li			return true
343*760c253cSXin Li		}
344*760c253cSXin Li	}
345*760c253cSXin Li	return false
346*760c253cSXin Li}
347*760c253cSXin Li
348*760c253cSXin Lifunc prepareClangCommand(crashArtifactsDir string, builder *commandBuilder) (err error) {
349*760c253cSXin Li	if !builder.cfg.isHostWrapper {
350*760c253cSXin Li		processSysrootFlag(builder)
351*760c253cSXin Li	}
352*760c253cSXin Li	builder.addPreUserArgs(builder.cfg.clangFlags...)
353*760c253cSXin Li
354*760c253cSXin Li	var crashDiagFlagName = "-fcrash-diagnostics-dir"
355*760c253cSXin Li	if crashArtifactsDir != "" &&
356*760c253cSXin Li		!hasUserArg(crashDiagFlagName, builder) {
357*760c253cSXin Li		builder.addPreUserArgs(crashDiagFlagName + "=" + crashArtifactsDir)
358*760c253cSXin Li	}
359*760c253cSXin Li
360*760c253cSXin Li	builder.addPostUserArgs(builder.cfg.clangPostFlags...)
361*760c253cSXin Li	calcCommonPreUserArgs(builder)
362*760c253cSXin Li	return processClangFlags(builder)
363*760c253cSXin Li}
364*760c253cSXin Li
365*760c253cSXin Lifunc calcClangCommand(crashArtifactsDir string, allowCCache bool, builder *commandBuilder) (bool, *command, error) {
366*760c253cSXin Li	err := prepareClangCommand(crashArtifactsDir, builder)
367*760c253cSXin Li	if err != nil {
368*760c253cSXin Li		return false, nil, err
369*760c253cSXin Li	}
370*760c253cSXin Li	remoteBuildUsed, err := processRemoteBuildAndCCacheFlags(allowCCache, builder)
371*760c253cSXin Li	if err != nil {
372*760c253cSXin Li		return remoteBuildUsed, nil, err
373*760c253cSXin Li	}
374*760c253cSXin Li	return remoteBuildUsed, builder.build(), nil
375*760c253cSXin Li}
376*760c253cSXin Li
377*760c253cSXin Lifunc calcGccCommand(enableRusage bool, builder *commandBuilder) (bool, *command, error) {
378*760c253cSXin Li	if !builder.cfg.isHostWrapper {
379*760c253cSXin Li		processSysrootFlag(builder)
380*760c253cSXin Li	}
381*760c253cSXin Li	builder.addPreUserArgs(builder.cfg.gccFlags...)
382*760c253cSXin Li	calcCommonPreUserArgs(builder)
383*760c253cSXin Li	processGccFlags(builder)
384*760c253cSXin Li
385*760c253cSXin Li	remoteBuildUsed, err := processRemoteBuildAndCCacheFlags(!enableRusage, builder)
386*760c253cSXin Li	if err != nil {
387*760c253cSXin Li		return remoteBuildUsed, nil, err
388*760c253cSXin Li	}
389*760c253cSXin Li	return remoteBuildUsed, builder.build(), nil
390*760c253cSXin Li}
391*760c253cSXin Li
392*760c253cSXin Lifunc calcCommonPreUserArgs(builder *commandBuilder) {
393*760c253cSXin Li	builder.addPreUserArgs(builder.cfg.commonFlags...)
394*760c253cSXin Li	if !builder.cfg.isHostWrapper {
395*760c253cSXin Li		processLibGCCFlags(builder)
396*760c253cSXin Li		processThumbCodeFlags(builder)
397*760c253cSXin Li		processStackProtectorFlags(builder)
398*760c253cSXin Li		processX86Flags(builder)
399*760c253cSXin Li	}
400*760c253cSXin Li	processSanitizerFlags(builder)
401*760c253cSXin Li}
402*760c253cSXin Li
403*760c253cSXin Lifunc processRemoteBuildAndCCacheFlags(allowCCache bool, builder *commandBuilder) (remoteBuildUsed bool, err error) {
404*760c253cSXin Li	remoteBuildUsed, err = processRemoteBuildFlags(builder)
405*760c253cSXin Li	if err != nil {
406*760c253cSXin Li		return remoteBuildUsed, err
407*760c253cSXin Li	}
408*760c253cSXin Li	if !remoteBuildUsed && allowCCache {
409*760c253cSXin Li		processCCacheFlag(builder)
410*760c253cSXin Li	}
411*760c253cSXin Li	return remoteBuildUsed, nil
412*760c253cSXin Li}
413*760c253cSXin Li
414*760c253cSXin Lifunc getAbsWrapperPath(env env, wrapperCmd *command) (string, error) {
415*760c253cSXin Li	wrapperPath := getAbsCmdPath(env, wrapperCmd)
416*760c253cSXin Li	evaledCmdPath, err := filepath.EvalSymlinks(wrapperPath)
417*760c253cSXin Li	if err != nil {
418*760c253cSXin Li		return "", wrapErrorwithSourceLocf(err, "failed to evaluate symlinks for %s", wrapperPath)
419*760c253cSXin Li	}
420*760c253cSXin Li	return evaledCmdPath, nil
421*760c253cSXin Li}
422*760c253cSXin Li
423*760c253cSXin Lifunc printCompilerError(writer io.Writer, compilerErr error) {
424*760c253cSXin Li	if _, ok := compilerErr.(userError); ok {
425*760c253cSXin Li		fmt.Fprintf(writer, "%s\n", compilerErr)
426*760c253cSXin Li	} else {
427*760c253cSXin Li		emailAccount := "chromeos-toolchain"
428*760c253cSXin Li		if isAndroidConfig() {
429*760c253cSXin Li			emailAccount = "android-llvm"
430*760c253cSXin Li		}
431*760c253cSXin Li		fmt.Fprintf(writer,
432*760c253cSXin Li			"Internal error. Please report to %[email protected].\n%s\n",
433*760c253cSXin Li			emailAccount, compilerErr)
434*760c253cSXin Li	}
435*760c253cSXin Li}
436*760c253cSXin Li
437*760c253cSXin Lifunc needStdinTee(inputCmd *command) bool {
438*760c253cSXin Li	lastArg := ""
439*760c253cSXin Li	for _, arg := range inputCmd.Args {
440*760c253cSXin Li		if arg == "-" && lastArg != "-o" {
441*760c253cSXin Li			return true
442*760c253cSXin Li		}
443*760c253cSXin Li		lastArg = arg
444*760c253cSXin Li	}
445*760c253cSXin Li	return false
446*760c253cSXin Li}
447*760c253cSXin Li
448*760c253cSXin Lifunc prebufferStdinIfNeeded(env env, inputCmd *command) (getStdin func() io.Reader, err error) {
449*760c253cSXin Li	// We pre-buffer the entirety of stdin, since the compiler may exit mid-invocation with an
450*760c253cSXin Li	// error, which may leave stdin partially read.
451*760c253cSXin Li	if !needStdinTee(inputCmd) {
452*760c253cSXin Li		// This won't produce deterministic input to the compiler, but stdin shouldn't
453*760c253cSXin Li		// matter in this case, so...
454*760c253cSXin Li		return env.stdin, nil
455*760c253cSXin Li	}
456*760c253cSXin Li
457*760c253cSXin Li	stdinBuffer := &bytes.Buffer{}
458*760c253cSXin Li	if _, err := stdinBuffer.ReadFrom(env.stdin()); err != nil {
459*760c253cSXin Li		return nil, wrapErrorwithSourceLocf(err, "prebuffering stdin")
460*760c253cSXin Li	}
461*760c253cSXin Li
462*760c253cSXin Li	return func() io.Reader { return bytes.NewReader(stdinBuffer.Bytes()) }, nil
463*760c253cSXin Li}
464