blob: fc8a5d713fa380194b5dc4bf61a636bcfaef81a2 [file] [log] [blame]
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +00001# Copyright 2016 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Rules for configuring the C++ toolchain (experimental)."""
15
16
17def _get_value(it):
18 """Convert `it` in serialized protobuf format."""
19 if type(it) == "int":
20 return str(it)
21 elif type(it) == "bool":
22 return "true" if it else "false"
23 else:
24 return "\"%s\"" % it
25
26
27def _build_crosstool(d, prefix=" "):
28 """Convert `d` to a string version of a CROSSTOOL file content."""
29 lines = []
30 for k in d:
31 if type(d[k]) == "list":
32 for it in d[k]:
33 lines.append("%s%s: %s" % (prefix, k, _get_value(it)))
34 else:
35 lines.append("%s%s: %s" % (prefix, k, _get_value(d[k])))
36 return "\n".join(lines)
37
38
39def _build_tool_path(d):
40 """Build the list of tool_path for the CROSSTOOL file."""
41 lines = []
42 for k in d:
43 lines.append(" tool_path {name: \"%s\" path: \"%s\" }" % (k, d[k]))
44 return "\n".join(lines)
45
46
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000047def _which(repository_ctx, cmd, default):
48 """A wrapper around repository_ctx.which() to provide a fallback value."""
49 result = repository_ctx.which(cmd)
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000050 return default if result == None else str(result)
51
52
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000053def _get_tool_paths(repository_ctx, darwin, cc):
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000054 """Compute the path to the various tools."""
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000055 return {k: _which(repository_ctx, k, "/usr/bin/" + k)
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000056 for k in [
57 "ld",
58 "cpp",
59 "dwp",
60 "gcov",
61 "nm",
62 "objcopy",
63 "objdump",
64 "strip",
65 ]} + {
66 "gcc": cc,
67 "ar": "/usr/bin/libtool"
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000068 if darwin else _which(repository_ctx, "ar", "/usr/bin/ar")
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000069 }
70
71
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000072def _ld_library_paths(repository_ctx):
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000073 """Use ${LD_LIBRARY_PATH} to compute the list -Wl,rpath flags."""
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000074 if "LD_LIBRARY_PATH" in repository_ctx.os.environ:
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000075 result = []
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000076 for p in repository_ctx.os.environ["LD_LIBRARY_PATH"].split(":"):
77 p = str(repository_ctx.path(p)) # Normalize the path
Alpha Lamf9276e22016-04-01 09:38:55 +000078 result.append("-Wl,-rpath," + p)
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000079 result.append("-L" + p)
80 return result
81 else:
82 return []
83
84
Alpha Lamf9276e22016-04-01 09:38:55 +000085def _cplus_include_paths(repository_ctx):
86 """Use ${CPLUS_INCLUDE_PATH} to compute the list of flags for cxxflag."""
87 if "CPLUS_INCLUDE_PATH" in repository_ctx.os.environ:
88 result = []
89 for p in repository_ctx.os.environ["CPLUS_INCLUDE_PATH"].split(":"):
90 p = str(repository_ctx.path(p)) # Normalize the path
91 result.append("-I" + p)
92 return result
93 else:
94 return []
95
96
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +000097def _get_cpu_value(repository_ctx):
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +000098 """Compute the cpu_value based on the OS name."""
Damien Martin-Guillereza6d9ba72016-03-15 18:18:53 +000099 os_name = repository_ctx.os.name.lower()
100 if os_name.startswith("mac os"):
101 return "darwin"
Damien Martin-Guillerez9116b3e2016-03-17 20:28:54 +0000102 if os_name.find("freebsd") != -1:
Damien Martin-Guillereza6d9ba72016-03-15 18:18:53 +0000103 return "freebsd"
Damien Martin-Guillerez9116b3e2016-03-17 20:28:54 +0000104 if os_name.find("windows") != -1:
Damien Martin-Guillereza6d9ba72016-03-15 18:18:53 +0000105 return "x64_windows"
106 return "k8"
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000107
108
109_INC_DIR_MARKER_BEGIN = "#include <...> search starts here:"
110_INC_DIR_MARKER_END = "End of search list."
111
112
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000113def _get_cxx_inc_directories(repository_ctx, cc):
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000114 """Compute the list of default C++ include directories."""
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000115 result = repository_ctx.execute([cc, "-E", "-xc++", "-", "-v"])
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000116 index1 = result.stderr.find(_INC_DIR_MARKER_BEGIN)
117 if index1 == -1:
118 return []
119 index2 = result.stderr.find(_INC_DIR_MARKER_END, index1)
120 if index2 == -1:
121 return []
122 inc_dirs = result.stderr[index1 + len(_INC_DIR_MARKER_BEGIN):index2].strip()
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000123 return [repository_ctx.path(p.strip()) for p in inc_dirs.split("\n")]
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000124
125
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000126def _add_option_if_supported(repository_ctx, cc, option):
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000127 """Checks that `option` is supported by the C compiler."""
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000128 result = repository_ctx.execute([
129 cc,
130 option,
131 "-o",
132 "/dev/null",
133 "-c",
134 str(repository_ctx.path("tools/cpp/empty.cc"))
Damien Martin-Guillerez8a9e9ab2016-03-08 15:59:27 +0000135 ])
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000136 return [option] if result.stderr.find(option) == -1 else []
137
138
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000139def _crosstool_content(repository_ctx, cc, cpu_value, darwin):
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000140 """Return the content for the CROSSTOOL file, in a dictionary."""
141 return {
142 "abi_version": "local",
143 "abi_libc_version": "local",
144 "builtin_sysroot": "",
145 "compiler": "compiler",
146 "host_system_name": "local",
147 "needsPic": True,
148 "supports_gold_linker": False,
149 "supports_incremental_linker": False,
150 "supports_fission": False,
151 "supports_interface_shared_objects": False,
152 "supports_normalizing_ar": False,
153 "supports_start_end_lib": False,
154 "supports_thin_archives": False,
155 "target_libc": "macosx" if darwin else "local",
156 "target_cpu": cpu_value,
157 "target_system_name": "local",
Alpha Lamf9276e22016-04-01 09:38:55 +0000158 "cxx_flag": [
159 "-std=c++0x",
160 ] + _cplus_include_paths(repository_ctx),
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000161 "linker_flag": [
162 "-lstdc++",
163 # Anticipated future default.
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000164 ] + _add_option_if_supported(repository_ctx, cc, "-no-canonical-prefixes") + (
165 ["-undefined", "dynamic_lookup"] if darwin else [
166 "-B" + str(repository_ctx.path(cc).dirname),
167 # Have gcc return the exit code from ld.
168 "-pass-exit-codes",
169 # Stamp the binary with a unique identifier.
170 "-Wl,--build-id=md5",
171 "-Wl,--hash-style=gnu"
172 # Gold linker only? Can we enable this by default?
173 # "-Wl,--warn-execstack",
174 # "-Wl,--detect-odr-violations"
175 ]) + _ld_library_paths(repository_ctx),
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000176 "ar_flag": ["-static", "-s", "-o"] if darwin else [],
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000177 "cxx_builtin_include_directory": _get_cxx_inc_directories(repository_ctx, cc),
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000178 "objcopy_embed_flag": ["-I", "binary"],
Damien Martin-Guillerez8a9e9ab2016-03-08 15:59:27 +0000179 "unfiltered_cxx_flag":
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000180 # Anticipated future default.
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000181 _add_option_if_supported(repository_ctx, cc, "-no-canonical-prefixes") +
182 _add_option_if_supported(repository_ctx, cc, "-fno-canonical-system-headers") + [
183 # Make C++ compilation deterministic. Use linkstamping instead of these
184 # compiler symbols.
185 "-Wno-builtin-macro-redefined",
186 "-D__DATE__=\\\"redacted\\\"",
187 "-D__TIMESTAMP__=\\\"redacted\\\"",
188 "-D__TIME__=\\\"redacted\\\""
189 ],
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000190 "compiler_flag": [
191 # Security hardening on by default.
192 # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases.
193 # We need to undef it before redefining it as some distributions now have
194 # it enabled by default.
195 "-U_FORTIFY_SOURCE",
196 "-D_FORTIFY_SOURCE=1",
197 "-fstack-protector",
198 # All warnings are enabled. Maybe enable -Werror as well?
199 "-Wall",
200 # Enable a few more warnings that aren't part of -Wall.
201 ] + (["-Wthread-safety", "-Wself-assign"] if darwin else [
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000202 # Disable some that are problematic.
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000203 "-Wl,-z,-relro,-z,now"
204 ]) + (
Alpha Lamf9276e22016-04-01 09:38:55 +0000205 _add_option_if_supported(repository_ctx, cc, "-Wunused-but-set-parameter") +
Damien Martin-Guillerez8a9e9ab2016-03-08 15:59:27 +0000206 # has false positives
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000207 _add_option_if_supported(repository_ctx, cc, "-Wno-free-nonheap-object") +
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000208 # Enable coloring even if there's no attached terminal. Bazel removes the
209 # escape sequences if --nocolor is specified.
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000210 _add_option_if_supported(repository_ctx, cc, "-fcolor-diagnostics")) + [
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000211 # Keep stack frames for debugging, even in opt mode.
212 "-fno-omit-frame-pointer",
213 ],
214 }
215
216
217def _opt_content(darwin):
218 """Return the content of the opt specific section of the CROSSTOOL file."""
219 return {
220 "compiler_flag": [
221 # No debug symbols.
222 # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt or
223 # even generally? However, that can't happen here, as it requires special
224 # handling in Bazel.
225 "-g0",
226
227 # Conservative choice for -O
228 # -O3 can increase binary size and even slow down the resulting binaries.
229 # Profile first and / or use FDO if you need better performance than this.
230 "-O2",
231
232 # Disable assertions
233 "-DNDEBUG",
234
235 # Removal of unused code and data at link time (can this increase binary size in some cases?).
236 "-ffunction-sections",
237 "-fdata-sections"
238 ],
239 "linker_flag": [] if darwin else ["-Wl,--gc-sections"]
240 }
241
242
243def _dbg_content():
244 """Return the content of the dbg specific section of the CROSSTOOL file."""
245 # Enable debug symbols
246 return {"compiler_flag": "-g"}
247
248
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000249def _find_cc(repository_ctx):
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000250 """Find the C++ compiler."""
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000251 if "CC" in repository_ctx.os.environ:
252 return repository_ctx.path(repository_ctx.os.environ["CC"])
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000253 else:
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000254 cc = repository_ctx.which("gcc")
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000255 if cc == None:
256 fail(
Damien Martin-Guillerez12ecc0b2016-03-04 16:09:33 +0000257 "Cannot find gcc, either correct your path or set the CC" +
258 " ennvironment variable")
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000259 return cc
260
261
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000262def _tpl(repository_ctx, tpl, substitutions={}):
263 repository_ctx.template(
264 tpl,
265 Label("@bazel_tools//tools/cpp:%s.tpl" % tpl),
266 substitutions)
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000267
268
Damien Martin-Guillerezda258a02016-03-11 22:57:08 +0000269def _impl(repository_ctx):
270 repository_ctx.file("tools/cpp/empty.cc")
271 cpu_value = _get_cpu_value(repository_ctx)
Damien Martin-Guillereza6d9ba72016-03-15 18:18:53 +0000272 if cpu_value in ["freebsd", "x64_windows"]:
273 # This is defaulting to the static crosstool, we should eventually do those platform too.
274 # Theorically, FreeBSD should be straightforward to add but we cannot run it in a docker
275 # container so escaping until we have proper tests for FreeBSD.
276 # Windows support is still experimental, let's not fiddle with autoconfiguration for now.
Damien Martin-Guillerezbe1b1152016-03-31 13:41:48 +0000277 repository_ctx.symlink(Label("@bazel_tools//tools/cpp:CROSSTOOL"), "CROSSTOOL")
278 repository_ctx.symlink(Label("@bazel_tools//tools/cpp:BUILD.static"), "BUILD")
Damien Martin-Guillereza6d9ba72016-03-15 18:18:53 +0000279 else:
280 darwin = cpu_value == "darwin"
281 cc = _find_cc(repository_ctx)
282 crosstool_cc = "osx_cc_wrapper.sh" if darwin else str(cc)
283 darwin = cpu_value == "darwin"
284 tool_paths = _get_tool_paths(repository_ctx, darwin, crosstool_cc)
285 crosstool_content = _crosstool_content(repository_ctx, cc, cpu_value, darwin)
286 opt_content = _opt_content(darwin)
287 dbg_content = _dbg_content()
288 _tpl(repository_ctx, "BUILD", {
289 "%{name}": cpu_value,
290 "%{supports_param_files}": "0" if darwin else "1"
291 })
292 _tpl(repository_ctx, "osx_cc_wrapper.sh", {"%{cc}": str(cc)})
293 _tpl(repository_ctx, "CROSSTOOL", {
294 "%{cpu}": cpu_value,
295 "%{content}": _build_crosstool(crosstool_content) + "\n" +
296 _build_tool_path(tool_paths),
297 "%{opt_content}": _build_crosstool(opt_content, " "),
298 "%{dbg_content}": _build_crosstool(dbg_content, " "),
299 })
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000300
301
Damien Martin-Guillereza9303522016-03-24 17:12:36 +0000302cc_autoconf = repository_rule(implementation=_impl, local=True)
Damien Martin-Guillerez8fa5ae62016-03-02 16:24:13 +0000303
304
305def cc_configure():
306 """A C++ configuration rules that generate the crosstool file."""
307 cc_autoconf(name="local_config_cc")
308 native.bind(name="cc_toolchain", actual="@local_config_cc//:toolchain")