blob: 0f702477914d8c5c84d2f59263fb8be39ebf7e24 [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
47def _which(ctx, cmd, default):
48 """A wrapper around ctx.which() to provide a fallback value."""
49 result = ctx.which(cmd)
50 return default if result == None else str(result)
51
52
53def _get_tool_paths(ctx, darwin, cc):
54 """Compute the path to the various tools."""
55 return {k: _which(ctx, k, "/usr/bin/" + k)
56 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"
68 if darwin else _which(ctx, "ar", "/usr/bin/ar")
69 }
70
71
72def _ld_library_paths(ctx):
73 """Use ${LD_LIBRARY_PATH} to compute the list -Wl,rpath flags."""
74 if "LD_LIBRARY_PATH" in ctx.os.environ:
75 result = []
76 for p in ctx.os.environ["LD_LIBRARY_PATH"].split(":"):
77 p = ctx.path(p) # Normalize the path
78 result.append("-Wl,rpath," + p)
79 result.append("-L" + p)
80 return result
81 else:
82 return []
83
84
85def _get_cpu_value(ctx):
86 """Compute the cpu_value based on the OS name."""
87 return "darwin" if ctx.os.name.lower().startswith("mac os") else "local"
88
89
90_INC_DIR_MARKER_BEGIN = "#include <...> search starts here:"
91_INC_DIR_MARKER_END = "End of search list."
92
93
94def _get_cxx_inc_directories(ctx, cc):
95 """Compute the list of default C++ include directories."""
96 result = ctx.execute([cc, "-E", "-xc++", "-", "-v"])
97 index1 = result.stderr.find(_INC_DIR_MARKER_BEGIN)
98 if index1 == -1:
99 return []
100 index2 = result.stderr.find(_INC_DIR_MARKER_END, index1)
101 if index2 == -1:
102 return []
103 inc_dirs = result.stderr[index1 + len(_INC_DIR_MARKER_BEGIN):index2].strip()
104 return [ctx.path(p.strip()) for p in inc_dirs.split("\n")]
105
106
107def _add_option_if_supported(ctx, cc, option):
108 """Checks that `option` is supported by the C compiler."""
109 result = ctx.execute([cc, option])
110 return [option] if result.stderr.find(option) == -1 else []
111
112
113def _crosstool_content(ctx, cc, cpu_value, darwin):
114 """Return the content for the CROSSTOOL file, in a dictionary."""
115 return {
116 "abi_version": "local",
117 "abi_libc_version": "local",
118 "builtin_sysroot": "",
119 "compiler": "compiler",
120 "host_system_name": "local",
121 "needsPic": True,
122 "supports_gold_linker": False,
123 "supports_incremental_linker": False,
124 "supports_fission": False,
125 "supports_interface_shared_objects": False,
126 "supports_normalizing_ar": False,
127 "supports_start_end_lib": False,
128 "supports_thin_archives": False,
129 "target_libc": "macosx" if darwin else "local",
130 "target_cpu": cpu_value,
131 "target_system_name": "local",
132 "cxx_flag": "-std=c++0x",
133 "linker_flag": [
134 "-lstdc++",
135 # Anticipated future default.
136 "-no-canonical-prefixes"
137 ] + (["-undefined", "dynamic_lookup"] if darwin else [
138 "-B/usr/bin",
139 # Have gcc return the exit code from ld.
140 "-pass-exit-codes",
141 # Stamp the binary with a unique identifier.
142 "-Wl,--build-id=md5",
143 "-Wl,--hash-style=gnu"
144 # Gold linker only? Can we enable this by default?
145 # "-Wl,--warn-execstack",
146 # "-Wl,--detect-odr-violations"
147 ]) + _ld_library_paths(ctx),
148 "ar_flag": ["-static", "-s", "-o"] if darwin else [],
149 "cxx_builtin_include_directory": _get_cxx_inc_directories(ctx, cc),
150 "objcopy_embed_flag": ["-I", "binary"],
151 "unfiltered_cxx_flag": [
152 # Anticipated future default.
153 "-no-canonical-prefixes",
154 ] + ([] if darwin else ["-fno-canonical-system-headers"]) + [
155 # Make C++ compilation deterministic. Use linkstamping instead of these
156 # compiler symbols.
157 "-Wno-builtin-macro-redefined",
158 "-D__DATE__=\\\"redacted\\\"",
159 "-D__TIMESTAMP__=\\\"redacted\\\"",
160 "-D__TIME__=\\\"redacted\\\""
161 ],
162 "compiler_flag": [
163 # Security hardening on by default.
164 # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases.
165 # We need to undef it before redefining it as some distributions now have
166 # it enabled by default.
167 "-U_FORTIFY_SOURCE",
168 "-D_FORTIFY_SOURCE=1",
169 "-fstack-protector",
170 # All warnings are enabled. Maybe enable -Werror as well?
171 "-Wall",
172 # Enable a few more warnings that aren't part of -Wall.
173 ] + (["-Wthread-safety", "-Wself-assign"] if darwin else [
174 "-Wunused-but-set-parameter",
175 # Disable some that are problematic.
176 "-Wno-free-nonheap-object", # has false positives
177 "-Wl,-z,-relro,-z,now"
178 ]) + (
179 # Enable coloring even if there's no attached terminal. Bazel removes the
180 # escape sequences if --nocolor is specified.
181 _add_option_if_supported(ctx, cc, "-fcolor-diagnostics")) + [
182 # Keep stack frames for debugging, even in opt mode.
183 "-fno-omit-frame-pointer",
184 ],
185 }
186
187
188def _opt_content(darwin):
189 """Return the content of the opt specific section of the CROSSTOOL file."""
190 return {
191 "compiler_flag": [
192 # No debug symbols.
193 # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt or
194 # even generally? However, that can't happen here, as it requires special
195 # handling in Bazel.
196 "-g0",
197
198 # Conservative choice for -O
199 # -O3 can increase binary size and even slow down the resulting binaries.
200 # Profile first and / or use FDO if you need better performance than this.
201 "-O2",
202
203 # Disable assertions
204 "-DNDEBUG",
205
206 # Removal of unused code and data at link time (can this increase binary size in some cases?).
207 "-ffunction-sections",
208 "-fdata-sections"
209 ],
210 "linker_flag": [] if darwin else ["-Wl,--gc-sections"]
211 }
212
213
214def _dbg_content():
215 """Return the content of the dbg specific section of the CROSSTOOL file."""
216 # Enable debug symbols
217 return {"compiler_flag": "-g"}
218
219
220def _find_cc(ctx):
221 """Find the C++ compiler."""
222 if "CC" in ctx.os.environ:
223 return ctx.path(ctx.os.environ["CC"])
224 else:
225 cc = ctx.which("gcc")
226 if cc == None:
227 fail(
228 "Cannot find gcc, either correct your path or set the CC environment"
229 " variable")
230 return cc
231
232
233def _tpl(ctx, tpl, substitutions={}):
234 ctx.template(tpl, Label("@bazel_tools//tools/cpp:%s.tpl" % tpl),
235 substitutions)
236
237
238def _impl(ctx):
239 cpu_value = _get_cpu_value(ctx)
240 darwin = cpu_value == "darwin"
241 cc = _find_cc(ctx)
242 crosstool_cc = "osx_cc_wrapper.sh" if darwin else str(cc)
243 darwin = cpu_value == "darwin"
244 tool_paths = _get_tool_paths(ctx, darwin, crosstool_cc)
245 crosstool_content = _crosstool_content(ctx, cc, cpu_value, darwin)
246 opt_content = _opt_content(darwin)
247 dbg_content = _dbg_content()
248 _tpl(ctx, "BUILD", {
249 "%{name}": cpu_value,
250 "%{supports_param_files}": "0" if darwin else "1"
251 })
252 _tpl(ctx, "osx_gcc_wrapper.sh", {"%{cc}": str(cc)})
253 _tpl(ctx, "CROSSTOOL", {
254 "%{cpu}": cpu_value,
255 "%{content}": _build_crosstool(crosstool_content) + "\n" +
256 _build_tool_path(tool_paths),
257 "%{opt_content}": _build_crosstool(opt_content, " "),
258 "%{dbg_content}": _build_crosstool(dbg_content, " "),
259 })
260
261
262cc_autoconf = repository_rule(_impl, local=True)
263
264
265def cc_configure():
266 """A C++ configuration rules that generate the crosstool file."""
267 cc_autoconf(name="local_config_cc")
268 native.bind(name="cc_toolchain", actual="@local_config_cc//:toolchain")