blob: 4cd083010937602a65c028067a51394d0d748189 [file] [log] [blame]
Yun Peng65cda4f2017-06-22 11:06:11 +02001# pylint: disable=g-bad-file-header
2# Copyright 2016 The Bazel Authors. All rights reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Configuring the C++ toolchain on Unix platforms."""
16
17
18load(
19 "@bazel_tools//tools/cpp:lib_cc_configure.bzl",
20 "escape_string",
21 "get_env_var",
hlopko19c64282018-02-27 07:28:40 -080022 "split_escaped",
Yun Peng65cda4f2017-06-22 11:06:11 +020023 "which",
24 "tpl",
25)
26
hlopkob3d52b12018-02-19 05:07:57 -080027def _uniq(iterable):
28 """Remove duplicates from a list."""
29
30 unique_elements = {element: None for element in iterable}
31 return unique_elements.keys()
32
ibiryukovdefba582018-01-02 06:09:04 -080033def _prepare_include_path(repo_ctx, path):
34 """Resolve and sanitize include path before outputting it into the crosstool.
35
36 Args:
37 repo_ctx: repository_ctx object.
38 path: an include path to be sanitized.
39
40 Returns:
41 Sanitized include path that can be written to the crosstoot. Resulting path
42 is absolute if it is outside the repository and relative otherwise.
43 """
44
45 repo_root = str(repo_ctx.path("."))
46 # We're on UNIX, so the path delimiter is '/'.
47 repo_root += "/"
48 path = str(repo_ctx.path(path))
49 if path.startswith(repo_root):
50 return escape_string(path[len(repo_root):])
51 return escape_string(path)
Yun Peng65cda4f2017-06-22 11:06:11 +020052
53def _get_value(it):
54 """Convert `it` in serialized protobuf format."""
55 if type(it) == "int":
56 return str(it)
57 elif type(it) == "bool":
58 return "true" if it else "false"
59 else:
60 return "\"%s\"" % it
61
62
63def _build_crosstool(d, prefix=" "):
64 """Convert `d` to a string version of a CROSSTOOL file content."""
65 lines = []
66 for k in d:
67 if type(d[k]) == "list":
68 for it in d[k]:
69 lines.append("%s%s: %s" % (prefix, k, _get_value(it)))
70 else:
71 lines.append("%s%s: %s" % (prefix, k, _get_value(d[k])))
72 return "\n".join(lines)
73
74
75def _build_tool_path(d):
76 """Build the list of %-escaped tool_path for the CROSSTOOL file."""
77 lines = []
78 for k in d:
79 lines.append(" tool_path {name: \"%s\" path: \"%s\" }" % (k, escape_string(d[k])))
80 return "\n".join(lines)
81
Ilya Biryukov12471a72017-12-20 05:46:44 -080082def _find_tool(repository_ctx, tool, overriden_tools):
83 """Find a tool for repository, taking overriden tools into account."""
84 if tool in overriden_tools:
85 return overriden_tools[tool]
86 return which(repository_ctx, tool, "/usr/bin/" + tool)
Yun Peng65cda4f2017-06-22 11:06:11 +020087
Ilya Biryukov12471a72017-12-20 05:46:44 -080088def _get_tool_paths(repository_ctx, darwin, cc, overriden_tools):
Yun Peng65cda4f2017-06-22 11:06:11 +020089 """Compute the path to the various tools. Doesn't %-escape the result!"""
vladmosceaed512018-01-03 12:20:57 -080090 return dict({k: _find_tool(repository_ctx, k, overriden_tools)
Yun Peng65cda4f2017-06-22 11:06:11 +020091 for k in [
92 "ld",
93 "cpp",
94 "dwp",
95 "gcov",
96 "nm",
97 "objcopy",
98 "objdump",
99 "strip",
vladmosceaed512018-01-03 12:20:57 -0800100 ]}.items() + {
Yun Peng65cda4f2017-06-22 11:06:11 +0200101 "gcc": cc,
102 "ar": "/usr/bin/libtool"
103 if darwin else which(repository_ctx, "ar", "/usr/bin/ar")
vladmosceaed512018-01-03 12:20:57 -0800104 }.items())
Yun Peng65cda4f2017-06-22 11:06:11 +0200105
106
107def _escaped_cplus_include_paths(repository_ctx):
108 """Use ${CPLUS_INCLUDE_PATH} to compute the %-escaped list of flags for cxxflag."""
109 if "CPLUS_INCLUDE_PATH" in repository_ctx.os.environ:
110 result = []
111 for p in repository_ctx.os.environ["CPLUS_INCLUDE_PATH"].split(":"):
112 p = escape_string(str(repository_ctx.path(p))) # Normalize the path
113 result.append("-I" + p)
114 return result
115 else:
116 return []
117
118
119_INC_DIR_MARKER_BEGIN = "#include <...>"
120
121# OSX add " (framework directory)" at the end of line, strip it.
122_OSX_FRAMEWORK_SUFFIX = " (framework directory)"
123_OSX_FRAMEWORK_SUFFIX_LEN = len(_OSX_FRAMEWORK_SUFFIX)
124
125def _cxx_inc_convert(path):
126 """Convert path returned by cc -E xc++ in a complete path. Doesn't %-escape the path!"""
127 path = path.strip()
128 if path.endswith(_OSX_FRAMEWORK_SUFFIX):
129 path = path[:-_OSX_FRAMEWORK_SUFFIX_LEN].strip()
130 return path
131
132
hlopkob3d52b12018-02-19 05:07:57 -0800133def get_escaped_cxx_inc_directories(repository_ctx, cc, additional_flags = []):
Yun Peng65cda4f2017-06-22 11:06:11 +0200134 """Compute the list of default %-escaped C++ include directories."""
hlopkob3d52b12018-02-19 05:07:57 -0800135 result = repository_ctx.execute([cc, "-E", "-xc++", "-", "-v"] + additional_flags)
Yun Peng65cda4f2017-06-22 11:06:11 +0200136 index1 = result.stderr.find(_INC_DIR_MARKER_BEGIN)
137 if index1 == -1:
138 return []
139 index1 = result.stderr.find("\n", index1)
140 if index1 == -1:
141 return []
142 index2 = result.stderr.rfind("\n ")
143 if index2 == -1 or index2 < index1:
144 return []
145 index2 = result.stderr.find("\n", index2 + 1)
146 if index2 == -1:
147 inc_dirs = result.stderr[index1 + 1:]
148 else:
149 inc_dirs = result.stderr[index1 + 1:index2].strip()
150
ibiryukovdefba582018-01-02 06:09:04 -0800151 return [_prepare_include_path(repository_ctx, _cxx_inc_convert(p))
Yun Peng65cda4f2017-06-22 11:06:11 +0200152 for p in inc_dirs.split("\n")]
153
154
hlopkob3d52b12018-02-19 05:07:57 -0800155def _is_option_supported(repository_ctx, cc, option):
Yun Peng65cda4f2017-06-22 11:06:11 +0200156 """Checks that `option` is supported by the C compiler. Doesn't %-escape the option."""
157 result = repository_ctx.execute([
158 cc,
159 option,
160 "-o",
161 "/dev/null",
162 "-c",
163 str(repository_ctx.path("tools/cpp/empty.cc"))
164 ])
hlopkob3d52b12018-02-19 05:07:57 -0800165 return result.stderr.find(option) == -1
166
167
168def _add_option_if_supported(repository_ctx, cc, option):
169 """Returns `[option]` if supported, `[]` otherwise. Doesn't %-escape the option."""
170 return [option] if _is_option_supported(repository_ctx, cc, option) else []
Yun Peng65cda4f2017-06-22 11:06:11 +0200171
172
173def _is_gold_supported(repository_ctx, cc):
174 """Checks that `gold` is supported by the C compiler."""
175 result = repository_ctx.execute([
176 cc,
177 "-fuse-ld=gold",
178 "-o",
179 "/dev/null",
180 # Some macos clang versions don't fail when setting -fuse-ld=gold, adding
181 # these lines to force it to. This also means that we will not detect
182 # gold when only a very old (year 2010 and older) is present.
183 "-Wl,--start-lib",
184 "-Wl,--end-lib",
185 str(repository_ctx.path("tools/cpp/empty.cc"))
186 ])
187 return result.return_code == 0
188
ibiryukove99279b2018-01-02 08:59:34 -0800189def _get_no_canonical_prefixes_opt(repository_ctx, cc):
190 # If the compiler sometimes rewrites paths in the .d files without symlinks
191 # (ie when they're shorter), it confuses Bazel's logic for verifying all
192 # #included header files are listed as inputs to the action.
193
194 # The '-fno-canonical-system-headers' should be enough, but clang does not
195 # support it, so we also try '-no-canonical-prefixes' if first option does
196 # not work.
197 opt = _add_option_if_supported(repository_ctx, cc,
198 "-fno-canonical-system-headers")
199 if len(opt) == 0:
200 return _add_option_if_supported(repository_ctx, cc,
201 "-no-canonical-prefixes")
202 return opt
Yun Peng65cda4f2017-06-22 11:06:11 +0200203
204def _crosstool_content(repository_ctx, cc, cpu_value, darwin):
205 """Return the content for the CROSSTOOL file, in a dictionary."""
206 supports_gold_linker = _is_gold_supported(repository_ctx, cc)
ibiryukovdefba582018-01-02 06:09:04 -0800207 cc_path = repository_ctx.path(cc)
208 if not str(cc_path).startswith(str(repository_ctx.path(".")) + '/'):
209 # cc is outside the repository, set -B
210 bin_search_flag = ["-B" + escape_string(str(cc_path.dirname))]
211 else:
212 # cc is inside the repository, don't set -B.
213 bin_search_flag = []
hlopkob3d52b12018-02-19 05:07:57 -0800214
215 escaped_cxx_include_directories = _uniq(
216 get_escaped_cxx_inc_directories(repository_ctx, cc) +
217 get_escaped_cxx_inc_directories(
218 repository_ctx, cc, _get_no_canonical_prefixes_opt(repository_ctx, cc)))
Yun Peng65cda4f2017-06-22 11:06:11 +0200219 return {
220 "abi_version": escape_string(get_env_var(repository_ctx, "ABI_VERSION", "local", False)),
221 "abi_libc_version": escape_string(get_env_var(repository_ctx, "ABI_LIBC_VERSION", "local", False)),
222 "builtin_sysroot": "",
223 "compiler": escape_string(get_env_var(repository_ctx, "BAZEL_COMPILER", "compiler", False)),
224 "host_system_name": escape_string(get_env_var(repository_ctx, "BAZEL_HOST_SYSTEM", "local", False)),
225 "needsPic": True,
226 "supports_gold_linker": supports_gold_linker,
227 "supports_incremental_linker": False,
228 "supports_fission": False,
229 "supports_interface_shared_objects": False,
230 "supports_normalizing_ar": False,
231 "supports_start_end_lib": supports_gold_linker,
232 "target_libc": "macosx" if darwin else escape_string(get_env_var(repository_ctx, "BAZEL_TARGET_LIBC", "local", False)),
233 "target_cpu": escape_string(get_env_var(repository_ctx, "BAZEL_TARGET_CPU", cpu_value, False)),
234 "target_system_name": escape_string(get_env_var(repository_ctx, "BAZEL_TARGET_SYSTEM", "local", False)),
235 "cxx_flag": [
236 "-std=c++0x",
237 ] + _escaped_cplus_include_paths(repository_ctx),
hlopko19c64282018-02-27 07:28:40 -0800238 "linker_flag": (
Yun Peng65cda4f2017-06-22 11:06:11 +0200239 ["-fuse-ld=gold"] if supports_gold_linker else []
240 ) + _add_option_if_supported(
241 repository_ctx, cc, "-Wl,-no-as-needed"
242 ) + _add_option_if_supported(
243 repository_ctx, cc, "-Wl,-z,relro,-z,now"
244 ) + ([
245 "-undefined",
246 "dynamic_lookup",
247 "-headerpad_max_install_names",
ibiryukovdefba582018-01-02 06:09:04 -0800248 ] if darwin else bin_search_flag + [
Yun Peng65cda4f2017-06-22 11:06:11 +0200249 # Always have -B/usr/bin, see https://github.com/bazelbuild/bazel/issues/760.
250 "-B/usr/bin",
251 # Gold linker only? Can we enable this by default?
252 # "-Wl,--warn-execstack",
253 # "-Wl,--detect-odr-violations"
254 ] + _add_option_if_supported(
255 # Have gcc return the exit code from ld.
256 repository_ctx, cc, "-pass-exit-codes")
hlopko19c64282018-02-27 07:28:40 -0800257 ) + split_escaped(get_env_var(repository_ctx, "BAZEL_LINKOPTS", "-lstdc++:-lm"), ":"),
hlopkob3d52b12018-02-19 05:07:57 -0800258 "cxx_builtin_include_directory": escaped_cxx_include_directories,
Yun Peng65cda4f2017-06-22 11:06:11 +0200259 "objcopy_embed_flag": ["-I", "binary"],
260 "unfiltered_cxx_flag":
ibiryukove99279b2018-01-02 08:59:34 -0800261 _get_no_canonical_prefixes_opt(repository_ctx, cc) + [
Yun Peng65cda4f2017-06-22 11:06:11 +0200262 # Make C++ compilation deterministic. Use linkstamping instead of these
263 # compiler symbols.
264 "-Wno-builtin-macro-redefined",
265 "-D__DATE__=\\\"redacted\\\"",
266 "-D__TIMESTAMP__=\\\"redacted\\\"",
267 "-D__TIME__=\\\"redacted\\\""
268 ],
269 "compiler_flag": [
270 # Security hardening requires optimization.
271 # We need to undef it as some distributions now have it enabled by default.
272 "-U_FORTIFY_SOURCE",
273 "-fstack-protector",
274 # All warnings are enabled. Maybe enable -Werror as well?
275 "-Wall",
276 # Enable a few more warnings that aren't part of -Wall.
ibiryukovdefba582018-01-02 06:09:04 -0800277 ] + (["-Wthread-safety", "-Wself-assign"] if darwin else bin_search_flag + [
Yun Peng65cda4f2017-06-22 11:06:11 +0200278 # Always have -B/usr/bin, see https://github.com/bazelbuild/bazel/issues/760.
279 "-B/usr/bin",
280 ]) + (
281 # Disable problematic warnings.
282 _add_option_if_supported(repository_ctx, cc, "-Wunused-but-set-parameter") +
283 # has false positives
284 _add_option_if_supported(repository_ctx, cc, "-Wno-free-nonheap-object") +
285 # Enable coloring even if there's no attached terminal. Bazel removes the
286 # escape sequences if --nocolor is specified.
287 _add_option_if_supported(repository_ctx, cc, "-fcolor-diagnostics")) + [
288 # Keep stack frames for debugging, even in opt mode.
289 "-fno-omit-frame-pointer",
290 ],
291 }
292
293
294def _opt_content(darwin):
295 """Return the content of the opt specific section of the CROSSTOOL file."""
296 return {
297 "compiler_flag": [
298 # No debug symbols.
299 # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt or
300 # even generally? However, that can't happen here, as it requires special
301 # handling in Bazel.
302 "-g0",
303
304 # Conservative choice for -O
305 # -O3 can increase binary size and even slow down the resulting binaries.
306 # Profile first and / or use FDO if you need better performance than this.
307 "-O2",
308
309 # Security hardening on by default.
310 # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases.
311 "-D_FORTIFY_SOURCE=1",
312
313 # Disable assertions
314 "-DNDEBUG",
315
316 # Removal of unused code and data at link time (can this increase binary size in some cases?).
317 "-ffunction-sections",
318 "-fdata-sections"
319 ],
320 "linker_flag": [] if darwin else ["-Wl,--gc-sections"]
321 }
322
323
324def _dbg_content():
325 """Return the content of the dbg specific section of the CROSSTOOL file."""
326 # Enable debug symbols
327 return {"compiler_flag": "-g"}
328
329
330def get_env(repository_ctx):
331 """Convert the environment in a list of export if in Homebrew. Doesn't %-escape the result!"""
332 env = repository_ctx.os.environ
333 if "HOMEBREW_RUBY_PATH" in env:
334 return "\n".join([
335 "export %s='%s'" % (k, env[k].replace("'", "'\\''"))
336 for k in env
337 if k != "_" and k.find(".") == -1
338 ])
339 else:
340 return ""
341
342
343def _coverage_feature(darwin):
344 if darwin:
345 compile_flags = """flag_group {
346 flag: '-fprofile-instr-generate'
347 flag: '-fcoverage-mapping'
348 }"""
349 link_flags = """flag_group {
350 flag: '-fprofile-instr-generate'
351 }"""
352 else:
353 compile_flags = """flag_group {
354 flag: '-fprofile-arcs'
355 flag: '-ftest-coverage'
356 }"""
357 link_flags = """flag_group {
358 flag: '-lgcov'
359 }"""
360 return """
361 feature {
362 name: 'coverage'
363 provides: 'profile'
364 flag_set {
365 action: 'preprocess-assemble'
366 action: 'c-compile'
367 action: 'c++-compile'
368 action: 'c++-header-parsing'
369 action: 'c++-header-preprocessing'
370 action: 'c++-module-compile'
371 """ + compile_flags + """
372
373
374
375 }
376 flag_set {
377 action: 'c++-link-interface-dynamic-library'
378 action: 'c++-link-dynamic-library'
379 action: 'c++-link-executable'
380 """ + link_flags + """
381 }
382 }
383 """
384
Ilya Biryukov12471a72017-12-20 05:46:44 -0800385def find_cc(repository_ctx, overriden_tools):
Yun Peng65cda4f2017-06-22 11:06:11 +0200386 """Find the C++ compiler. Doesn't %-escape the result."""
Ilya Biryukov12471a72017-12-20 05:46:44 -0800387
388 if "gcc" in overriden_tools:
389 return overriden_tools["gcc"]
390
Yun Peng65cda4f2017-06-22 11:06:11 +0200391 cc_name = "gcc"
392 cc_environ = repository_ctx.os.environ.get("CC")
393 cc_paren = ""
394 if cc_environ != None:
395 cc_environ = cc_environ.strip()
396 if cc_environ:
397 cc_name = cc_environ
398 cc_paren = " (%s)" % cc_environ
399 if cc_name.startswith("/"):
400 # Absolute path, maybe we should make this suported by our which function.
401 return cc_name
402 cc = repository_ctx.which(cc_name)
403 if cc == None:
404 fail(
405 ("Cannot find gcc or CC%s, either correct your path or set the CC"
406 + " environment variable") % cc_paren)
407 return cc
408
Ilya Biryukov12471a72017-12-20 05:46:44 -0800409def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools):
Yun Peng65cda4f2017-06-22 11:06:11 +0200410 """Configure C++ toolchain on Unix platforms."""
Allen Lavoie8ba16a62017-08-01 17:31:26 +0200411 repository_ctx.file("tools/cpp/empty.cc", "int main() {}")
Yun Peng65cda4f2017-06-22 11:06:11 +0200412 darwin = cpu_value == "darwin"
Ilya Biryukov12471a72017-12-20 05:46:44 -0800413 cc = find_cc(repository_ctx, overriden_tools)
Yun Peng65cda4f2017-06-22 11:06:11 +0200414 tool_paths = _get_tool_paths(repository_ctx, darwin,
Ilya Biryukov12471a72017-12-20 05:46:44 -0800415 "cc_wrapper.sh" if darwin else str(cc),
416 overriden_tools)
Yun Peng65cda4f2017-06-22 11:06:11 +0200417 crosstool_content = _crosstool_content(repository_ctx, cc, cpu_value, darwin)
418 opt_content = _opt_content(darwin)
419 dbg_content = _dbg_content()
420 tpl(repository_ctx, "BUILD", {
421 "%{name}": cpu_value,
422 "%{supports_param_files}": "0" if darwin else "1",
423 "%{cc_compiler_deps}": ":cc_wrapper" if darwin else ":empty",
424 "%{compiler}": get_env_var(repository_ctx, "BAZEL_COMPILER", "compiler", False),
425 })
426 tpl(repository_ctx,
427 "osx_cc_wrapper.sh" if darwin else "linux_cc_wrapper.sh",
428 {"%{cc}": escape_string(str(cc)),
429 "%{env}": escape_string(get_env(repository_ctx))},
430 "cc_wrapper.sh")
431 tpl(repository_ctx, "CROSSTOOL", {
432 "%{cpu}": escape_string(cpu_value),
433 "%{default_toolchain_name}": escape_string(
434 get_env_var(repository_ctx,
435 "CC_TOOLCHAIN_NAME",
436 "local",
437 False)),
438 "%{toolchain_name}": escape_string(
439 get_env_var(repository_ctx, "CC_TOOLCHAIN_NAME", "local", False)),
440 "%{content}": _build_crosstool(crosstool_content) + "\n" +
441 _build_tool_path(tool_paths),
442 "%{opt_content}": _build_crosstool(opt_content, " "),
443 "%{dbg_content}": _build_crosstool(dbg_content, " "),
444 "%{cxx_builtin_include_directory}": "",
445 "%{coverage}": _coverage_feature(darwin),
446 "%{msvc_env_tmp}": "",
447 "%{msvc_env_path}": "",
448 "%{msvc_env_include}": "",
449 "%{msvc_env_lib}": "",
Yun Peng65cda4f2017-06-22 11:06:11 +0200450 "%{msvc_cl_path}": "",
Seth Greensteinae760d22017-11-17 02:20:06 -0800451 "%{msvc_ml_path}": "",
Yun Peng65cda4f2017-06-22 11:06:11 +0200452 "%{msvc_link_path}": "",
453 "%{msvc_lib_path}": "",
Yun Penge9f40902018-01-03 09:20:27 -0800454 "%{msys_x64_mingw_content}": "",
Yun Peng09a6a9f2017-12-11 07:24:45 -0800455 "%{dbg_mode_debug}": "",
456 "%{fastbuild_mode_debug}": "",
Yun Peng65cda4f2017-06-22 11:06:11 +0200457 "%{compilation_mode_content}": "",
458 })