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