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