blob: 1eacb578b868c94018a2c6b1b9edd0cd27a8e384 [file] [log] [blame]
package main
import (
"fmt"
"io"
"path/filepath"
"strings"
"syscall"
)
func callCompiler(env env, cfg *config, inputCmd *command) int {
exitCode := 0
var compilerErr error
if shouldForwardToOldWrapper(env, inputCmd) {
// TODO: Once this is only checking for bisect, create a command
// that directly calls the bisect driver in calcCompilerCommand.
exitCode, compilerErr = forwardToOldWrapper(env, cfg, inputCmd)
} else if cfg.oldWrapperPath != "" {
exitCode, compilerErr = callCompilerWithRunAndCompareToOldWrapper(env, cfg, inputCmd)
} else {
exitCode, compilerErr = callCompilerWithExec(env, cfg, inputCmd)
}
if compilerErr != nil {
printCompilerError(env.stderr(), compilerErr)
exitCode = 1
}
return exitCode
}
func callCompilerWithRunAndCompareToOldWrapper(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
recordingEnv := &commandRecordingEnv{
env: env,
}
compilerCmd, exitCode, err := calcCompilerCommand(recordingEnv, cfg, inputCmd)
if err != nil || exitCode != 0 {
return exitCode, err
}
exitCode = 0
// Note: we are not using env.exec here so that we can compare the exit code
// against the old wrapper too.
if err := recordingEnv.run(compilerCmd, env.stdout(), env.stderr()); err != nil {
if userErr, ok := getCCacheError(compilerCmd, err); ok {
return exitCode, userErr
}
var ok bool
if exitCode, ok = getExitCode(err); !ok {
return exitCode, wrapErrorwithSourceLocf(err, "failed to execute %#v", compilerCmd)
}
}
if err := compareToOldWrapper(env, cfg, inputCmd, recordingEnv.cmdResults); err != nil {
return exitCode, err
}
return exitCode, nil
}
func callCompilerWithExec(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
compilerCmd, exitCode, err := calcCompilerCommand(env, cfg, inputCmd)
if err != nil || exitCode != 0 {
return exitCode, err
}
if err := env.exec(compilerCmd); err != nil {
// Note: No need to check for exit code error as exec will
// stop this control flow once the command started executing.
if userErr, ok := getCCacheError(compilerCmd, err); ok {
return exitCode, userErr
}
return exitCode, wrapErrorwithSourceLocf(err, "failed to execute %#v", compilerCmd)
}
return exitCode, nil
}
func calcCompilerCommand(env env, cfg *config, inputCmd *command) (compilerCmd *command, exitCode int, err error) {
if err := checkUnsupportedFlags(inputCmd); err != nil {
return nil, exitCode, err
}
mainBuilder, err := newCommandBuilder(env, cfg, inputCmd)
if err != nil {
return nil, exitCode, err
}
clangSyntax := processClangSyntaxFlag(mainBuilder)
if mainBuilder.target.compilerType == clangType {
cSrcFile, useClangTidy := processClangTidyFlags(mainBuilder)
compilerCmd, err = calcClangCommand(useClangTidy, mainBuilder)
if err != nil {
return nil, exitCode, err
}
if useClangTidy {
if err := runClangTidy(env, compilerCmd, cSrcFile); err != nil {
return nil, exitCode, err
}
}
} else {
if clangSyntax {
forceLocal := false
clangCmd, err := calcClangCommand(forceLocal, mainBuilder.clone())
if err != nil {
return nil, 0, err
}
exitCode, err = checkClangSyntax(env, clangCmd)
if err != nil || exitCode != 0 {
return nil, exitCode, err
}
}
compilerCmd = calcGccCommand(mainBuilder)
}
return compilerCmd, exitCode, nil
}
func calcClangCommand(forceLocal bool, builder *commandBuilder) (*command, error) {
sysroot := processSysrootFlag(builder)
builder.addPreUserArgs(builder.cfg.clangFlags...)
calcCommonPreUserArgs(builder)
if err := processClangFlags(builder); err != nil {
return nil, err
}
if !forceLocal {
processGomaCCacheFlags(sysroot, builder)
}
return builder.build(), nil
}
func calcGccCommand(builder *commandBuilder) *command {
sysroot := processSysrootFlag(builder)
builder.addPreUserArgs(builder.cfg.gccFlags...)
calcCommonPreUserArgs(builder)
processGccFlags(builder)
processGomaCCacheFlags(sysroot, builder)
return builder.build()
}
func calcCommonPreUserArgs(builder *commandBuilder) {
builder.addPreUserArgs(builder.cfg.commonFlags...)
processPieFlags(builder)
processStackProtectorFlags(builder)
processThumbCodeFlags(builder)
processX86Flags(builder)
processSanitizerFlags(builder)
}
func processGomaCCacheFlags(sysroot string, builder *commandBuilder) {
gomaccUsed := processGomaCccFlags(builder)
if !gomaccUsed {
processCCacheFlag(sysroot, builder)
}
}
func getAbsWrapperDir(env env, wrapperPath string) (string, error) {
if !filepath.IsAbs(wrapperPath) {
wrapperPath = filepath.Join(env.getwd(), wrapperPath)
}
evaledCmdPath, err := filepath.EvalSymlinks(wrapperPath)
if err != nil {
return "", wrapErrorwithSourceLocf(err, "failed to evaluate symlinks for %s", wrapperPath)
}
return filepath.Dir(evaledCmdPath), nil
}
func getCCacheError(compilerCmd *command, compilerCmdErr error) (ccacheErr userError, ok bool) {
if en, ok := compilerCmdErr.(syscall.Errno); ok && en == syscall.ENOENT &&
strings.Contains(compilerCmd.path, "ccache") {
ccacheErr =
newUserErrorf("ccache not found under %s. Please install it",
compilerCmd.path)
return ccacheErr, ok
}
return ccacheErr, false
}
func printCompilerError(writer io.Writer, compilerErr error) {
if _, ok := compilerErr.(userError); ok {
fmt.Fprintf(writer, "%s\n", compilerErr)
} else {
fmt.Fprintf(writer,
"Internal error. Please report to chromeos-toolchain@google.com.\n%s\n",
compilerErr)
}
}