aboutsummaryrefslogtreecommitdiff
path: root/compiler_wrapper/clang_flags.go
diff options
context:
space:
mode:
Diffstat (limited to 'compiler_wrapper/clang_flags.go')
-rw-r--r--compiler_wrapper/clang_flags.go224
1 files changed, 224 insertions, 0 deletions
diff --git a/compiler_wrapper/clang_flags.go b/compiler_wrapper/clang_flags.go
new file mode 100644
index 00000000..ee041a2e
--- /dev/null
+++ b/compiler_wrapper/clang_flags.go
@@ -0,0 +1,224 @@
+// 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 {
+ if builder.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.
+ builder.path = filepath.Join(filepath.Dir(builder.path), filepath.Base(builder.absWrapperPath)+".real")
+ return nil
+ }
+
+ 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{
+ "-mno-movbe": true,
+ "-pass-exit-codes": true,
+ "-Wclobbered": true,
+ "-Wno-psabi": true,
+ "-Wlogical-op": true,
+ "-Wmissing-parameter-type": true,
+ "-Wold-style-declaration": true,
+ "-Woverride-init": true,
+ "-Wunsafe-loop-optimizations": 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)
+ }
+ return stdoutBuffer.String(), 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
+}