You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
9.0 KiB
298 lines
9.0 KiB
// 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 (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func callCompiler(env env, cfg *config, inputCmd *command) int {
|
|
var compilerErr error
|
|
|
|
if !filepath.IsAbs(inputCmd.Path) && !strings.HasPrefix(inputCmd.Path, ".") &&
|
|
!strings.ContainsRune(inputCmd.Path, filepath.Separator) {
|
|
if resolvedPath, err := resolveAgainstPathEnv(env, inputCmd.Path); err == nil {
|
|
inputCmd = &command{
|
|
Path: resolvedPath,
|
|
Args: inputCmd.Args,
|
|
EnvUpdates: inputCmd.EnvUpdates,
|
|
}
|
|
} else {
|
|
compilerErr = err
|
|
}
|
|
}
|
|
exitCode := 0
|
|
if compilerErr == nil {
|
|
exitCode, compilerErr = callCompilerInternal(env, cfg, inputCmd)
|
|
}
|
|
if compilerErr != nil {
|
|
printCompilerError(env.stderr(), compilerErr)
|
|
exitCode = 1
|
|
}
|
|
return exitCode
|
|
}
|
|
|
|
// Given the main builder path and the absolute path to our wrapper, returns the path to the
|
|
// 'real' compiler we should invoke.
|
|
func calculateAndroidWrapperPath(mainBuilderPath string, absWrapperPath string) string {
|
|
// FIXME: This combination of using the directory of the symlink but the basename of the
|
|
// link target is strange but is the logic that old android wrapper uses. Change this to use
|
|
// directory and basename either from the absWrapperPath or from the builder.path, but don't
|
|
// mix anymore.
|
|
|
|
// We need to be careful here: path.Join Clean()s its result, so `./foo` will get
|
|
// transformed to `foo`, which isn't good since we're passing this path to exec.
|
|
basePart := filepath.Base(absWrapperPath) + ".real"
|
|
if !strings.ContainsRune(mainBuilderPath, filepath.Separator) {
|
|
return basePart
|
|
}
|
|
|
|
dirPart := filepath.Dir(mainBuilderPath)
|
|
if cleanResult := filepath.Join(dirPart, basePart); strings.ContainsRune(cleanResult, filepath.Separator) {
|
|
return cleanResult
|
|
}
|
|
|
|
return "." + string(filepath.Separator) + basePart
|
|
}
|
|
|
|
func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
|
|
if err := checkUnsupportedFlags(inputCmd); err != nil {
|
|
return 0, err
|
|
}
|
|
mainBuilder, err := newCommandBuilder(env, cfg, inputCmd)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
processPrintConfigFlag(mainBuilder)
|
|
processPrintCmdlineFlag(mainBuilder)
|
|
env = mainBuilder.env
|
|
var compilerCmd *command
|
|
clangSyntax := processClangSyntaxFlag(mainBuilder)
|
|
if cfg.isAndroidWrapper {
|
|
mainBuilder.path = calculateAndroidWrapperPath(mainBuilder.path, mainBuilder.absWrapperPath)
|
|
switch mainBuilder.target.compilerType {
|
|
case clangType:
|
|
mainBuilder.addPreUserArgs(mainBuilder.cfg.clangFlags...)
|
|
mainBuilder.addPreUserArgs(mainBuilder.cfg.commonFlags...)
|
|
mainBuilder.addPostUserArgs(mainBuilder.cfg.clangPostFlags...)
|
|
if _, err := processGomaCccFlags(mainBuilder); err != nil {
|
|
return 0, err
|
|
}
|
|
compilerCmd = mainBuilder.build()
|
|
case clangTidyType:
|
|
compilerCmd = mainBuilder.build()
|
|
default:
|
|
return 0, newErrorwithSourceLocf("unsupported compiler: %s", mainBuilder.target.compiler)
|
|
}
|
|
} else {
|
|
cSrcFile, tidyFlags, tidyMode := processClangTidyFlags(mainBuilder)
|
|
if mainBuilder.target.compilerType == clangType {
|
|
err := prepareClangCommand(mainBuilder)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
allowCCache := true
|
|
if tidyMode != tidyModeNone {
|
|
allowCCache = false
|
|
clangCmdWithoutGomaAndCCache := mainBuilder.build()
|
|
var err error
|
|
switch tidyMode {
|
|
case tidyModeTricium:
|
|
if cfg.triciumNitsDir == "" {
|
|
return 0, newErrorwithSourceLocf("tricium linting was requested, but no nits directory is configured")
|
|
}
|
|
err = runClangTidyForTricium(env, clangCmdWithoutGomaAndCCache, cSrcFile, cfg.triciumNitsDir, tidyFlags, cfg.crashArtifactsDir)
|
|
case tidyModeAll:
|
|
err = runClangTidy(env, clangCmdWithoutGomaAndCCache, cSrcFile, tidyFlags)
|
|
default:
|
|
panic(fmt.Sprintf("Unknown tidy mode: %v", tidyMode))
|
|
}
|
|
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
if err := processGomaCCacheFlags(allowCCache, mainBuilder); err != nil {
|
|
return 0, err
|
|
}
|
|
compilerCmd = mainBuilder.build()
|
|
} else {
|
|
if clangSyntax {
|
|
allowCCache := false
|
|
clangCmd, err := calcClangCommand(allowCCache, mainBuilder.clone())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
gccCmd, err := calcGccCommand(mainBuilder)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return checkClangSyntax(env, clangCmd, gccCmd)
|
|
}
|
|
compilerCmd, err = calcGccCommand(mainBuilder)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
}
|
|
rusageLogfileName := getRusageLogFilename(env)
|
|
bisectStage := getBisectStage(env)
|
|
if shouldForceDisableWerror(env, cfg) {
|
|
if rusageLogfileName != "" {
|
|
return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
|
|
}
|
|
if bisectStage != "" {
|
|
return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
|
|
}
|
|
return doubleBuildWithWNoError(env, cfg, compilerCmd)
|
|
}
|
|
if shouldCompileWithFallback(env) {
|
|
if rusageLogfileName != "" {
|
|
return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
|
|
}
|
|
if bisectStage != "" {
|
|
return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
|
|
}
|
|
return compileWithFallback(env, cfg, compilerCmd, mainBuilder.absWrapperPath)
|
|
}
|
|
if rusageLogfileName != "" {
|
|
if bisectStage != "" {
|
|
return 0, newUserErrorf("BISECT_STAGE is meaningless with GETRUSAGE")
|
|
}
|
|
return logRusage(env, rusageLogfileName, compilerCmd)
|
|
}
|
|
if bisectStage != "" {
|
|
compilerCmd, err = calcBisectCommand(env, cfg, bisectStage, compilerCmd)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
// Note: We return an exit code only if the underlying env is not
|
|
// really doing an exec, e.g. commandRecordingEnv.
|
|
return wrapSubprocessErrorWithSourceLoc(compilerCmd, env.exec(compilerCmd))
|
|
}
|
|
|
|
func prepareClangCommand(builder *commandBuilder) (err error) {
|
|
if !builder.cfg.isHostWrapper {
|
|
processSysrootFlag(builder)
|
|
}
|
|
builder.addPreUserArgs(builder.cfg.clangFlags...)
|
|
if builder.cfg.crashArtifactsDir != "" {
|
|
builder.addPreUserArgs("-fcrash-diagnostics-dir=" + builder.cfg.crashArtifactsDir)
|
|
}
|
|
builder.addPostUserArgs(builder.cfg.clangPostFlags...)
|
|
calcCommonPreUserArgs(builder)
|
|
return processClangFlags(builder)
|
|
}
|
|
|
|
func calcClangCommand(allowCCache bool, builder *commandBuilder) (*command, error) {
|
|
err := prepareClangCommand(builder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := processGomaCCacheFlags(allowCCache, builder); err != nil {
|
|
return nil, err
|
|
}
|
|
return builder.build(), nil
|
|
}
|
|
|
|
func calcGccCommand(builder *commandBuilder) (*command, error) {
|
|
if !builder.cfg.isHostWrapper {
|
|
processSysrootFlag(builder)
|
|
}
|
|
builder.addPreUserArgs(builder.cfg.gccFlags...)
|
|
if !builder.cfg.isHostWrapper {
|
|
calcCommonPreUserArgs(builder)
|
|
}
|
|
processGccFlags(builder)
|
|
if !builder.cfg.isHostWrapper {
|
|
allowCCache := true
|
|
if err := processGomaCCacheFlags(allowCCache, builder); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return builder.build(), nil
|
|
}
|
|
|
|
func calcCommonPreUserArgs(builder *commandBuilder) {
|
|
builder.addPreUserArgs(builder.cfg.commonFlags...)
|
|
if !builder.cfg.isHostWrapper {
|
|
processPieFlags(builder)
|
|
processThumbCodeFlags(builder)
|
|
processStackProtectorFlags(builder)
|
|
processX86Flags(builder)
|
|
}
|
|
processSanitizerFlags(builder)
|
|
}
|
|
|
|
func processGomaCCacheFlags(allowCCache bool, builder *commandBuilder) (err error) {
|
|
gomaccUsed := false
|
|
if !builder.cfg.isHostWrapper {
|
|
gomaccUsed, err = processGomaCccFlags(builder)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if !gomaccUsed && allowCCache {
|
|
processCCacheFlag(builder)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getAbsWrapperPath(env env, wrapperCmd *command) (string, error) {
|
|
wrapperPath := getAbsCmdPath(env, wrapperCmd)
|
|
evaledCmdPath, err := filepath.EvalSymlinks(wrapperPath)
|
|
if err != nil {
|
|
return "", wrapErrorwithSourceLocf(err, "failed to evaluate symlinks for %s", wrapperPath)
|
|
}
|
|
return evaledCmdPath, nil
|
|
}
|
|
|
|
func printCompilerError(writer io.Writer, compilerErr error) {
|
|
if _, ok := compilerErr.(userError); ok {
|
|
fmt.Fprintf(writer, "%s\n", compilerErr)
|
|
} else {
|
|
emailAccount := "chromeos-toolchain"
|
|
if isAndroidConfig() {
|
|
emailAccount = "android-llvm"
|
|
}
|
|
fmt.Fprintf(writer,
|
|
"Internal error. Please report to %s@google.com.\n%s\n",
|
|
emailAccount, compilerErr)
|
|
}
|
|
}
|
|
|
|
func needStdinTee(inputCmd *command) bool {
|
|
lastArg := ""
|
|
for _, arg := range inputCmd.Args {
|
|
if arg == "-" && lastArg != "-o" {
|
|
return true
|
|
}
|
|
lastArg = arg
|
|
}
|
|
return false
|
|
}
|
|
|
|
func prebufferStdinIfNeeded(env env, inputCmd *command) (getStdin func() io.Reader, err error) {
|
|
// We pre-buffer the entirety of stdin, since the compiler may exit mid-invocation with an
|
|
// error, which may leave stdin partially read.
|
|
if !needStdinTee(inputCmd) {
|
|
// This won't produce deterministic input to the compiler, but stdin shouldn't
|
|
// matter in this case, so...
|
|
return env.stdin, nil
|
|
}
|
|
|
|
stdinBuffer := &bytes.Buffer{}
|
|
if _, err := stdinBuffer.ReadFrom(env.stdin()); err != nil {
|
|
return nil, wrapErrorwithSourceLocf(err, "prebuffering stdin")
|
|
}
|
|
|
|
return func() io.Reader { return bytes.NewReader(stdinBuffer.Bytes()) }, nil
|
|
}
|