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.
209 lines
6.8 KiB
209 lines
6.8 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"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func processClangFlags(builder *commandBuilder) error {
|
|
env := builder.env
|
|
clangDir, _ := env.getenv("CLANG")
|
|
|
|
if clangDir == "" {
|
|
if builder.cfg.isHostWrapper {
|
|
clangDir = filepath.Dir(builder.absWrapperPath)
|
|
} else {
|
|
clangDir = filepath.Join(builder.rootPath, "usr/bin/")
|
|
if !filepath.IsAbs(builder.path) {
|
|
// If sysroot_wrapper is invoked by relative path, call actual compiler in
|
|
// relative form. This is neccesary to remove absolute path from compile
|
|
// outputs.
|
|
var err error
|
|
clangDir, err = filepath.Rel(env.getwd(), clangDir)
|
|
if err != nil {
|
|
return wrapErrorwithSourceLocf(err, "failed to make clangDir %s relative to %s.", clangDir, env.getwd())
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
clangDir = filepath.Dir(clangDir)
|
|
}
|
|
|
|
clangBasename := "clang"
|
|
if strings.HasSuffix(builder.target.compiler, "++") {
|
|
clangBasename = "clang++"
|
|
}
|
|
|
|
// GCC flags to remove from the clang command line.
|
|
// TODO: Once clang supports GCC compatibility mode, remove
|
|
// these checks.
|
|
//
|
|
// Use of -Qunused-arguments allows this set to be small, just those
|
|
// that clang still warns about.
|
|
unsupported := map[string]bool{
|
|
"-pass-exit-codes": true,
|
|
}
|
|
|
|
unsupportedPrefixes := []string{"-Wstrict-aliasing=", "-finline-limit="}
|
|
|
|
// clang with '-ftrapv' generates 'call __mulodi4', which is only implemented
|
|
// in compiler-rt library. However compiler-rt library only has i386/x86_64
|
|
// backends (see '/usr/lib/clang/3.7.0/lib/linux/libclang_rt.*'). GCC, on the
|
|
// other hand, generate 'call __mulvdi3', which is implemented in libgcc. See
|
|
// bug chromium:503229.
|
|
armUnsupported := map[string]bool{"-ftrapv": true}
|
|
if builder.cfg.isHostWrapper {
|
|
unsupported["-ftrapv"] = true
|
|
}
|
|
|
|
// Clang may use different options for the same or similar functionality.
|
|
gccToClang := map[string]string{
|
|
"-Wno-error=cpp": "-Wno-#warnings",
|
|
"-Wno-error=maybe-uninitialized": "-Wno-error=uninitialized",
|
|
"-Wno-error=unused-but-set-variable": "-Wno-error=unused-variable",
|
|
"-Wno-unused-but-set-variable": "-Wno-unused-variable",
|
|
"-Wunused-but-set-variable": "-Wunused-variable",
|
|
}
|
|
|
|
// Note: not using builder.transformArgs as we need to add multiple arguments
|
|
// based on a single input argument, and also be able to return errors.
|
|
newArgs := []builderArg{}
|
|
|
|
for _, arg := range builder.args {
|
|
// Adds an argument with the given value, preserving the
|
|
// fromUser value of the original argument.
|
|
addNewArg := func(value string) {
|
|
newArgs = append(newArgs, builderArg{
|
|
fromUser: arg.fromUser,
|
|
value: value,
|
|
})
|
|
}
|
|
|
|
if mapped, ok := gccToClang[arg.value]; ok {
|
|
addNewArg(mapped)
|
|
continue
|
|
}
|
|
|
|
if unsupported[arg.value] {
|
|
continue
|
|
}
|
|
|
|
if hasAtLeastOnePrefix(arg.value, unsupportedPrefixes) {
|
|
continue
|
|
}
|
|
|
|
if builder.target.arch == "armv7a" && builder.target.sys == "linux" {
|
|
if armUnsupported[arg.value] {
|
|
continue
|
|
}
|
|
}
|
|
|
|
if clangOnly := "-Xclang-only="; strings.HasPrefix(arg.value, clangOnly) {
|
|
addNewArg(arg.value[len(clangOnly):])
|
|
continue
|
|
}
|
|
|
|
if clangPath := "-Xclang-path="; strings.HasPrefix(arg.value, clangPath) {
|
|
clangPathValue := arg.value[len(clangPath):]
|
|
resourceDir, err := getClangResourceDir(env, filepath.Join(clangDir, clangBasename))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clangDir = clangPathValue
|
|
|
|
addNewArg("-resource-dir=" + resourceDir)
|
|
addNewArg("--gcc-toolchain=/usr")
|
|
continue
|
|
}
|
|
|
|
addNewArg(arg.value)
|
|
}
|
|
builder.args = newArgs
|
|
|
|
builder.path = filepath.Join(clangDir, clangBasename)
|
|
|
|
// Specify the target for clang.
|
|
if !builder.cfg.isHostWrapper {
|
|
linkerPath := getLinkerPath(env, builder.target.target+"-ld", builder.rootPath)
|
|
relLinkerPath, err := filepath.Rel(env.getwd(), linkerPath)
|
|
if err != nil {
|
|
return wrapErrorwithSourceLocf(err, "failed to make linker path %s relative to %s",
|
|
linkerPath, env.getwd())
|
|
}
|
|
builder.addPostUserArgs("-B" + relLinkerPath)
|
|
if startswithI86(builder.target.arch) {
|
|
// TODO: -target i686-pc-linux-gnu causes clang to search for
|
|
// libclang_rt.asan-i686.a which doesn't exist because it's packaged
|
|
// as libclang_rt.asan-i386.a. We can't use -target i386-pc-linux-gnu
|
|
// because then it would try to run i386-pc-linux-gnu-ld which doesn't
|
|
// exist. Consider renaming the runtime library to use i686 in its name.
|
|
builder.addPostUserArgs("-m32")
|
|
// clang does not support -mno-movbe. This is the alternate way to do it.
|
|
builder.addPostUserArgs("-Xclang", "-target-feature", "-Xclang", "-movbe")
|
|
} else {
|
|
builder.addPostUserArgs("-target", builder.target.target)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getClangResourceDir(env env, clangPath string) (string, error) {
|
|
readResourceCmd := &command{
|
|
Path: clangPath,
|
|
Args: []string{"--print-resource-dir"},
|
|
}
|
|
stdoutBuffer := bytes.Buffer{}
|
|
if err := env.run(readResourceCmd, nil, &stdoutBuffer, env.stderr()); err != nil {
|
|
return "", wrapErrorwithSourceLocf(err,
|
|
"failed to call clang to read the resouce-dir: %#v",
|
|
readResourceCmd)
|
|
}
|
|
resourceDir := strings.TrimRight(stdoutBuffer.String(), "\n")
|
|
return resourceDir, nil
|
|
}
|
|
|
|
// Return the a directory which contains an 'ld' that gcc is using.
|
|
func getLinkerPath(env env, linkerCmd string, rootPath string) string {
|
|
// We did not pass the tuple i686-pc-linux-gnu to x86-32 clang. Instead,
|
|
// we passed '-m32' to clang. As a result, clang does not want to use the
|
|
// i686-pc-linux-gnu-ld, so we need to add this to help clang find the right
|
|
// linker.
|
|
if linkerPath, err := resolveAgainstPathEnv(env, linkerCmd); err == nil {
|
|
// FIXME: We are not using filepath.EvalSymlinks to only unpack
|
|
// one layer of symlinks to match the old wrapper. Investigate
|
|
// why this is important or simplify to filepath.EvalSymlinks.
|
|
if fi, err := os.Lstat(linkerPath); err == nil {
|
|
if fi.Mode()&os.ModeSymlink != 0 {
|
|
if linkPath, err := os.Readlink(linkerPath); err == nil {
|
|
linkerPath = linkPath
|
|
}
|
|
}
|
|
return filepath.Dir(linkerPath)
|
|
}
|
|
}
|
|
|
|
// When using the sdk outside chroot, we need to provide the cross linker path
|
|
// to the compiler via -B ${linker_path}. This is because for gcc, it can
|
|
// find the right linker via searching its internal paths. Clang does not have
|
|
// such feature, and it falls back to $PATH search only. However, the path of
|
|
// ${SDK_LOCATION}/bin is not necessarily in the ${PATH}. To fix this, we
|
|
// provide the directory that contains the cross linker wrapper to clang.
|
|
// Outside chroot, it is the top bin directory form the sdk tarball.
|
|
return filepath.Join(rootPath, "bin")
|
|
}
|
|
|
|
func hasAtLeastOnePrefix(s string, prefixes []string) bool {
|
|
for _, prefix := range prefixes {
|
|
if strings.HasPrefix(s, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|