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