aboutsummaryrefslogtreecommitdiff
path: root/compiler_wrapper/clang_flags.go
blob: 9eb951ded2ed658e61390d58091b8919729ae038 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// 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 {
	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{
		"-pass-exit-codes": 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)
	}
	resourceDir := strings.TrimRight(stdoutBuffer.String(), "\n")
	return resourceDir, 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
}