1// Copyright 2019 The ChromiumOS Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package main 6 7import ( 8 "bytes" 9 "os" 10 "path" 11 "path/filepath" 12 "strings" 13) 14 15func processClangFlags(builder *commandBuilder) error { 16 env := builder.env 17 clangDir, _ := env.getenv("CLANG") 18 19 if clangDir == "" { 20 if builder.cfg.isHostWrapper { 21 clangDir = filepath.Dir(builder.absWrapperPath) 22 } else { 23 clangDir = filepath.Join(builder.rootPath, "usr/bin/") 24 if !filepath.IsAbs(builder.path) { 25 // If sysroot_wrapper is invoked by relative path, call actual compiler in 26 // relative form. This is neccesary to remove absolute path from compile 27 // outputs. 28 var err error 29 clangDir, err = filepath.Rel(env.getwd(), clangDir) 30 if err != nil { 31 return wrapErrorwithSourceLocf(err, "failed to make clangDir %s relative to %s.", clangDir, env.getwd()) 32 } 33 } 34 } 35 } else { 36 clangDir = filepath.Dir(clangDir) 37 } 38 39 clangBasename := "clang" 40 if strings.HasSuffix(builder.target.compiler, "++") { 41 clangBasename = "clang++" 42 } 43 44 // Unsupported flags to remove from the clang command line. 45 // Use of -Qunused-arguments allows this set to be small, just those 46 // that clang still warns about. 47 unsupported := map[string]bool{ 48 "-Xcompiler": true, 49 "-avoid-version": true, 50 } 51 52 unsupportedPrefixes := []string{"-Wstrict-aliasing=", "-finline-limit="} 53 54 // clang with '-ftrapv' generates 'call __mulodi4', which is only implemented 55 // in compiler-rt library. However compiler-rt library only has i386/x86_64 56 // backends (see '/usr/lib/clang/3.7.0/lib/linux/libclang_rt.*'). GCC, on the 57 // other hand, generate 'call __mulvdi3', which is implemented in libgcc. See 58 // bug chromium:503229. 59 armUnsupported := map[string]bool{"-ftrapv": true} 60 if builder.cfg.isHostWrapper { 61 unsupported["-ftrapv"] = true 62 } 63 64 // Clang may use different options for the same or similar functionality. 65 gccToClang := map[string]string{ 66 "-Wno-error=cpp": "-Wno-#warnings", 67 "-Wno-error=maybe-uninitialized": "-Wno-error=uninitialized", 68 } 69 70 // Note: not using builder.transformArgs as we need to add multiple arguments 71 // based on a single input argument, and also be able to return errors. 72 newArgs := []builderArg{} 73 74 for _, arg := range builder.args { 75 // Adds an argument with the given value, preserving the 76 // fromUser value of the original argument. 77 addNewArg := func(value string) { 78 newArgs = append(newArgs, builderArg{ 79 fromUser: arg.fromUser, 80 value: value, 81 }) 82 } 83 84 if mapped, ok := gccToClang[arg.value]; ok { 85 addNewArg(mapped) 86 continue 87 } 88 89 if unsupported[arg.value] { 90 continue 91 } 92 93 if hasAtLeastOnePrefix(arg.value, unsupportedPrefixes) { 94 continue 95 } 96 97 if builder.target.arch == "armv7a" && builder.target.sys == "linux" { 98 if armUnsupported[arg.value] { 99 continue 100 } 101 } 102 103 if clangOnly := "-Xclang-only="; strings.HasPrefix(arg.value, clangOnly) { 104 addNewArg(arg.value[len(clangOnly):]) 105 continue 106 } 107 108 if clangPath := "-Xclang-path="; strings.HasPrefix(arg.value, clangPath) { 109 clangPathValue := arg.value[len(clangPath):] 110 resourceDir, err := getClangResourceDir(env, filepath.Join(clangDir, clangBasename)) 111 if err != nil { 112 return err 113 } 114 clangDir = clangPathValue 115 116 addNewArg("-resource-dir=" + resourceDir) 117 addNewArg("--gcc-toolchain=/usr") 118 continue 119 } 120 121 addNewArg(arg.value) 122 } 123 builder.args = newArgs 124 125 builder.path = filepath.Join(clangDir, clangBasename) 126 127 // Specify the target for clang. 128 if !builder.cfg.isHostWrapper { 129 linkerPath := getLinkerPath(env, builder.target.target+"-ld.bfd", builder.rootPath) 130 relLinkerPath, err := filepath.Rel(env.getwd(), linkerPath) 131 if err != nil { 132 return wrapErrorwithSourceLocf(err, "failed to make linker path %s relative to %s", 133 linkerPath, env.getwd()) 134 } 135 prefixPath := path.Join(relLinkerPath, builder.target.target+"-") 136 builder.addPreUserArgs("--prefix=" + prefixPath) 137 builder.addPostUserArgs("-B" + relLinkerPath) 138 builder.addPostUserArgs("-target", builder.target.target) 139 } 140 return nil 141} 142 143func getClangResourceDir(env env, clangPath string) (string, error) { 144 readResourceCmd := &command{ 145 Path: clangPath, 146 Args: []string{"--print-resource-dir"}, 147 } 148 stdoutBuffer := bytes.Buffer{} 149 if err := env.run(readResourceCmd, nil, &stdoutBuffer, env.stderr()); err != nil { 150 return "", wrapErrorwithSourceLocf(err, 151 "failed to call clang to read the resouce-dir: %#v", 152 readResourceCmd) 153 } 154 resourceDir := strings.TrimRight(stdoutBuffer.String(), "\n") 155 return resourceDir, nil 156} 157 158// Return the a directory which contains an 'ld' that gcc is using. 159func getLinkerPath(env env, linkerCmd string, rootPath string) string { 160 // We did not pass the tuple i686-pc-linux-gnu to x86-32 clang. Instead, 161 // we passed '-m32' to clang. As a result, clang does not want to use the 162 // i686-pc-linux-gnu-ld, so we need to add this to help clang find the right 163 // linker. 164 if linkerPath, err := resolveAgainstPathEnv(env, linkerCmd); err == nil { 165 // FIXME: We are not using filepath.EvalSymlinks to only unpack 166 // one layer of symlinks to match the old wrapper. Investigate 167 // why this is important or simplify to filepath.EvalSymlinks. 168 if fi, err := os.Lstat(linkerPath); err == nil { 169 if fi.Mode()&os.ModeSymlink != 0 { 170 if linkPath, err := os.Readlink(linkerPath); err == nil { 171 linkerPath = linkPath 172 } 173 } 174 return filepath.Dir(linkerPath) 175 } 176 } 177 178 // When using the sdk outside chroot, we need to provide the cross linker path 179 // to the compiler via -B ${linker_path}. This is because for gcc, it can 180 // find the right linker via searching its internal paths. Clang does not have 181 // such feature, and it falls back to $PATH search only. However, the path of 182 // ${SDK_LOCATION}/bin is not necessarily in the ${PATH}. To fix this, we 183 // provide the directory that contains the cross linker wrapper to clang. 184 // Outside chroot, it is the top bin directory form the sdk tarball. 185 return filepath.Join(rootPath, "bin") 186} 187 188func hasAtLeastOnePrefix(s string, prefixes []string) bool { 189 for _, prefix := range prefixes { 190 if strings.HasPrefix(s, prefix) { 191 return true 192 } 193 } 194 return false 195} 196