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