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 "bufio" 9 "bytes" 10 "fmt" 11 "io" 12 "os" 13 "path/filepath" 14 "strings" 15 "syscall" 16 "time" 17) 18 19const prebuiltCompilerPathKey = "ANDROID_LLVM_PREBUILT_COMPILER_PATH" 20 21func shouldCompileWithFallback(env env) bool { 22 value, _ := env.getenv(prebuiltCompilerPathKey) 23 return value != "" 24} 25 26// FIXME: Deduplicate this logic with the logic for FORCE_DISABLE_WERROR 27// (the logic here is from Android, the logic for FORCE_DISABLE_WERROR is from ChromeOS) 28func compileWithFallback(env env, cfg *config, originalCmd *command, absWrapperPath string) (exitCode int, err error) { 29 firstCmd := &command{ 30 Path: originalCmd.Path, 31 Args: originalCmd.Args, 32 EnvUpdates: originalCmd.EnvUpdates, 33 } 34 // We only want to pass extra flags to clang and clang++. 35 if base := filepath.Base(originalCmd.Path); base == "clang.real" || base == "clang++.real" { 36 // We may introduce some new warnings after rebasing and we need to 37 // disable them before we fix those warnings. 38 extraArgs, _ := env.getenv("ANDROID_LLVM_FALLBACK_DISABLED_WARNINGS") 39 firstCmd.Args = append( 40 append(firstCmd.Args, "-fno-color-diagnostics"), 41 strings.Split(extraArgs, " ")..., 42 ) 43 } 44 45 getStdin, err := prebufferStdinIfNeeded(env, firstCmd) 46 if err != nil { 47 return 0, wrapErrorwithSourceLocf(err, "prebuffering stdin: %v", err) 48 } 49 50 firstCmdStderrBuffer := &bytes.Buffer{} 51 firstCmdExitCode, err := wrapSubprocessErrorWithSourceLoc(firstCmd, 52 env.run(firstCmd, getStdin(), env.stdout(), io.MultiWriter(env.stderr(), firstCmdStderrBuffer))) 53 if err != nil { 54 return 0, err 55 } 56 57 if firstCmdExitCode == 0 { 58 return 0, nil 59 } 60 stderrRedirectPath, _ := env.getenv("ANDROID_LLVM_STDERR_REDIRECT") 61 f, err := os.OpenFile(stderrRedirectPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) 62 if err != nil { 63 return 0, wrapErrorwithSourceLocf(err, "error opening stderr file %s", stderrRedirectPath) 64 } 65 lockSuccess := false 66 for i := 0; i < 30; i++ { 67 err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) 68 if err == nil { 69 lockSuccess = true 70 break 71 } 72 if errno, ok := err.(syscall.Errno); ok { 73 if errno == syscall.EAGAIN || errno == syscall.EACCES { 74 time.Sleep(500 * time.Millisecond) 75 err = nil 76 } 77 } 78 if err != nil { 79 return 0, wrapErrorwithSourceLocf(err, "error waiting to lock file %s", stderrRedirectPath) 80 } 81 } 82 if !lockSuccess { 83 return 0, wrapErrorwithSourceLocf(err, "timeout waiting to lock file %s", stderrRedirectPath) 84 } 85 w := bufio.NewWriter(f) 86 w.WriteString("==================COMMAND:====================\n") 87 fmt.Fprintf(w, "%s %s\n\n", firstCmd.Path, strings.Join(firstCmd.Args, " ")) 88 firstCmdStderrBuffer.WriteTo(w) 89 w.WriteString("==============================================\n\n") 90 if err := w.Flush(); err != nil { 91 return 0, wrapErrorwithSourceLocf(err, "unable to write to file %s", stderrRedirectPath) 92 } 93 if err := f.Close(); err != nil { 94 return 0, wrapErrorwithSourceLocf(err, "error closing file %s", stderrRedirectPath) 95 } 96 97 prebuiltCompilerPath, _ := env.getenv(prebuiltCompilerPathKey) 98 fallbackCmd := &command{ 99 Path: filepath.Join(prebuiltCompilerPath, filepath.Base(absWrapperPath)), 100 // Don't use extra args added (from ANDROID_LLVM_FALLBACK_DISABLED_WARNINGS) for clang and 101 // clang++ above. They may not be recognized by the fallback clang. 102 Args: originalCmd.Args, 103 // Delete prebuiltCompilerPathKey so the fallback doesn't keep 104 // calling itself in case of an error. 105 EnvUpdates: append(originalCmd.EnvUpdates, prebuiltCompilerPathKey+"="), 106 } 107 return wrapSubprocessErrorWithSourceLoc(fallbackCmd, 108 env.run(fallbackCmd, getStdin(), env.stdout(), env.stderr())) 109} 110