xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/clang_flags.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
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