| // 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 ( |
| "bufio" |
| "bytes" |
| "fmt" |
| "io" |
| "os" |
| "path/filepath" |
| "strings" |
| "syscall" |
| "time" |
| ) |
| |
| const prebuiltCompilerPathKey = "ANDROID_LLVM_PREBUILT_COMPILER_PATH" |
| |
| func shouldCompileWithFallback(env env) bool { |
| value, _ := env.getenv(prebuiltCompilerPathKey) |
| return value != "" |
| } |
| |
| // FIXME: Deduplicate this logic with the logic for FORCE_DISABLE_WERROR |
| // (the logic here is from Android, the logic for FORCE_DISABLE_WERROR is from ChromeOS) |
| func compileWithFallback(env env, cfg *config, originalCmd *command, absWrapperPath string) (exitCode int, err error) { |
| firstCmd := &command{ |
| Path: originalCmd.Path, |
| Args: originalCmd.Args, |
| EnvUpdates: originalCmd.EnvUpdates, |
| } |
| // We only want to pass extra flags to clang and clang++. |
| if base := filepath.Base(originalCmd.Path); base == "clang.real" || base == "clang++.real" { |
| // We may introduce some new warnings after rebasing and we need to |
| // disable them before we fix those warnings. |
| extraArgs, _ := env.getenv("ANDROID_LLVM_FALLBACK_DISABLED_WARNINGS") |
| firstCmd.Args = append( |
| append(firstCmd.Args, "-fno-color-diagnostics"), |
| strings.Split(extraArgs, " ")..., |
| ) |
| } |
| |
| getStdin, err := prebufferStdinIfNeeded(env, firstCmd) |
| if err != nil { |
| return 0, wrapErrorwithSourceLocf(err, "prebuffering stdin: %v", err) |
| } |
| |
| firstCmdStderrBuffer := &bytes.Buffer{} |
| firstCmdExitCode, err := wrapSubprocessErrorWithSourceLoc(firstCmd, |
| env.run(firstCmd, getStdin(), env.stdout(), io.MultiWriter(env.stderr(), firstCmdStderrBuffer))) |
| if err != nil { |
| return 0, err |
| } |
| |
| if firstCmdExitCode == 0 { |
| return 0, nil |
| } |
| stderrRedirectPath, _ := env.getenv("ANDROID_LLVM_STDERR_REDIRECT") |
| f, err := os.OpenFile(stderrRedirectPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) |
| if err != nil { |
| return 0, wrapErrorwithSourceLocf(err, "error opening stderr file %s", stderrRedirectPath) |
| } |
| lockSuccess := false |
| for i := 0; i < 30; i++ { |
| err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) |
| if err == nil { |
| lockSuccess = true |
| break |
| } |
| if errno, ok := err.(syscall.Errno); ok { |
| if errno == syscall.EAGAIN || errno == syscall.EACCES { |
| time.Sleep(500 * time.Millisecond) |
| err = nil |
| } |
| } |
| if err != nil { |
| return 0, wrapErrorwithSourceLocf(err, "error waiting to lock file %s", stderrRedirectPath) |
| } |
| } |
| if !lockSuccess { |
| return 0, wrapErrorwithSourceLocf(err, "timeout waiting to lock file %s", stderrRedirectPath) |
| } |
| w := bufio.NewWriter(f) |
| w.WriteString("==================COMMAND:====================\n") |
| fmt.Fprintf(w, "%s %s\n\n", firstCmd.Path, strings.Join(firstCmd.Args, " ")) |
| firstCmdStderrBuffer.WriteTo(w) |
| w.WriteString("==============================================\n\n") |
| if err := w.Flush(); err != nil { |
| return 0, wrapErrorwithSourceLocf(err, "unable to write to file %s", stderrRedirectPath) |
| } |
| if err := f.Close(); err != nil { |
| return 0, wrapErrorwithSourceLocf(err, "error closing file %s", stderrRedirectPath) |
| } |
| |
| prebuiltCompilerPath, _ := env.getenv(prebuiltCompilerPathKey) |
| fallbackCmd := &command{ |
| Path: filepath.Join(prebuiltCompilerPath, filepath.Base(absWrapperPath)), |
| // Don't use extra args added (from ANDROID_LLVM_FALLBACK_DISABLED_WARNINGS) for clang and |
| // clang++ above. They may not be recognized by the fallback clang. |
| Args: originalCmd.Args, |
| // Delete prebuiltCompilerPathKey so the fallback doesn't keep |
| // calling itself in case of an error. |
| EnvUpdates: append(originalCmd.EnvUpdates, prebuiltCompilerPathKey+"="), |
| } |
| return wrapSubprocessErrorWithSourceLoc(fallbackCmd, |
| env.run(fallbackCmd, getStdin(), env.stdout(), env.stderr())) |
| } |