247 lines
7.3 KiB
Go
247 lines
7.3 KiB
Go
// 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 (
|
|
"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
|
|
}
|
|
|
|
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 {
|
|
// 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.
|
|
mainBuilder.path = filepath.Join(filepath.Dir(mainBuilder.path), filepath.Base(mainBuilder.absWrapperPath)+".real")
|
|
|
|
switch mainBuilder.target.compilerType {
|
|
case clangType:
|
|
mainBuilder.addPreUserArgs(mainBuilder.cfg.clangFlags...)
|
|
mainBuilder.addPreUserArgs(mainBuilder.cfg.commonFlags...)
|
|
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 if mainBuilder.target.compilerType == clangType {
|
|
cSrcFile, useClangTidy := processClangTidyFlags(mainBuilder)
|
|
sysroot, err := prepareClangCommand(mainBuilder)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
allowCCache := true
|
|
if useClangTidy {
|
|
allowCCache = false
|
|
clangCmdWithoutGomaAndCCache := mainBuilder.build()
|
|
if err := runClangTidy(env, clangCmdWithoutGomaAndCCache, cSrcFile); err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
if err := processGomaCCacheFlags(sysroot, 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) {
|
|
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) (sysroot string, err error) {
|
|
sysroot = ""
|
|
if !builder.cfg.isHostWrapper {
|
|
sysroot = processSysrootFlag(builder)
|
|
}
|
|
builder.addPreUserArgs(builder.cfg.clangFlags...)
|
|
builder.addPostUserArgs(builder.cfg.clangPostFlags...)
|
|
calcCommonPreUserArgs(builder)
|
|
if err := processClangFlags(builder); err != nil {
|
|
return "", err
|
|
}
|
|
return sysroot, nil
|
|
}
|
|
|
|
func calcClangCommand(allowCCache bool, builder *commandBuilder) (*command, error) {
|
|
sysroot, err := prepareClangCommand(builder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := processGomaCCacheFlags(sysroot, allowCCache, builder); err != nil {
|
|
return nil, err
|
|
}
|
|
return builder.build(), nil
|
|
}
|
|
|
|
func calcGccCommand(builder *commandBuilder) (*command, error) {
|
|
sysroot := ""
|
|
if !builder.cfg.isHostWrapper {
|
|
sysroot = processSysrootFlag(builder)
|
|
}
|
|
builder.addPreUserArgs(builder.cfg.gccFlags...)
|
|
if !builder.cfg.isHostWrapper {
|
|
calcCommonPreUserArgs(builder)
|
|
}
|
|
processGccFlags(builder)
|
|
if !builder.cfg.isHostWrapper {
|
|
allowCCache := true
|
|
if err := processGomaCCacheFlags(sysroot, 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(sysroot string, 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(sysroot, 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 {
|
|
fmt.Fprintf(writer,
|
|
"Internal error. Please report to chromeos-toolchain@google.com.\n%s\n",
|
|
compilerErr)
|
|
}
|
|
}
|
|
|
|
func teeStdinIfNeeded(env env, inputCmd *command, dest io.Writer) io.Reader {
|
|
// We can't use io.TeeReader unconditionally, as that would block
|
|
// calls to exec.Cmd.Run(), even if the underlying process has already
|
|
// terminated. See https://github.com/golang/go/issues/7990 for more details.
|
|
lastArg := ""
|
|
for _, arg := range inputCmd.Args {
|
|
if arg == "-" && lastArg != "-o" {
|
|
return io.TeeReader(env.stdin(), dest)
|
|
}
|
|
lastArg = arg
|
|
}
|
|
return env.stdin()
|
|
}
|