blob: 53d70d507e92c3e2b91de024a609bcca6760d390 [file] [log] [blame]
// 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 (
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(, "++") {
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 := make(map[string]bool)
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 {
if unsupported[arg.value] {
if hasAtLeastOnePrefix(arg.value, unsupportedPrefixes) {
if == "armv7a" && == "linux" {
if armUnsupported[arg.value] {
if clangOnly := "-Xclang-only="; strings.HasPrefix(arg.value, clangOnly) {
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)
builder.args = newArgs
builder.path = filepath.Join(clangDir, clangBasename)
// Specify the target for clang.
if !builder.cfg.isHostWrapper {
linkerPath := getLinkerPath(env,"-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( {
// 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.
// clang does not support -mno-movbe. This is the alternate way to do it.
builder.addPostUserArgs("-Xclang", "-target-feature", "-Xclang", "-movbe")
} else {
return nil
func getClangResourceDir(env env, clangPath string) (string, error) {
readResourceCmd := &command{
Path: clangPath,
Args: []string{"--print-resource-dir"},
stdoutBuffer := bytes.Buffer{}
if err :=, nil, &stdoutBuffer, env.stderr()); err != nil {
return "", wrapErrorwithSourceLocf(err,
"failed to call clang to read the resouce-dir: %#v",
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