aboutsummaryrefslogtreecommitdiff
path: root/cc/private/toolchain/unix_cc_configure.bzl
diff options
context:
space:
mode:
Diffstat (limited to 'cc/private/toolchain/unix_cc_configure.bzl')
-rw-r--r--cc/private/toolchain/unix_cc_configure.bzl587
1 files changed, 587 insertions, 0 deletions
diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl
new file mode 100644
index 0000000..0c936de
--- /dev/null
+++ b/cc/private/toolchain/unix_cc_configure.bzl
@@ -0,0 +1,587 @@
+# pylint: disable=g-bad-file-header
+# Copyright 2016 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Configuring the C++ toolchain on Unix platforms."""
+
+load(
+ ":lib_cc_configure.bzl",
+ "auto_configure_fail",
+ "auto_configure_warning",
+ "auto_configure_warning_maybe",
+ "escape_string",
+ "get_env_var",
+ "get_starlark_list",
+ "resolve_labels",
+ "split_escaped",
+ "which",
+ "write_builtin_include_directory_paths",
+)
+
+def _uniq(iterable):
+ """Remove duplicates from a list."""
+
+ unique_elements = {element: None for element in iterable}
+ return unique_elements.keys()
+
+def _prepare_include_path(repo_ctx, path):
+ """Resolve and sanitize include path before outputting it into the crosstool.
+
+ Args:
+ repo_ctx: repository_ctx object.
+ path: an include path to be sanitized.
+
+ Returns:
+ Sanitized include path that can be written to the crosstoot. Resulting path
+ is absolute if it is outside the repository and relative otherwise.
+ """
+
+ repo_root = str(repo_ctx.path("."))
+
+ # We're on UNIX, so the path delimiter is '/'.
+ repo_root += "/"
+ path = str(repo_ctx.path(path))
+ if path.startswith(repo_root):
+ return escape_string(path[len(repo_root):])
+ return escape_string(path)
+
+def _find_tool(repository_ctx, tool, overriden_tools):
+ """Find a tool for repository, taking overriden tools into account."""
+ if tool in overriden_tools:
+ return overriden_tools[tool]
+ return which(repository_ctx, tool, "/usr/bin/" + tool)
+
+def _get_tool_paths(repository_ctx, overriden_tools):
+ """Compute the %-escaped path to the various tools"""
+ return dict({
+ k: escape_string(_find_tool(repository_ctx, k, overriden_tools))
+ for k in [
+ "ar",
+ "ld",
+ "cpp",
+ "gcc",
+ "dwp",
+ "gcov",
+ "nm",
+ "objcopy",
+ "objdump",
+ "strip",
+ ]
+ }.items())
+
+def _escaped_cplus_include_paths(repository_ctx):
+ """Use ${CPLUS_INCLUDE_PATH} to compute the %-escaped list of flags for cxxflag."""
+ if "CPLUS_INCLUDE_PATH" in repository_ctx.os.environ:
+ result = []
+ for p in repository_ctx.os.environ["CPLUS_INCLUDE_PATH"].split(":"):
+ p = escape_string(str(repository_ctx.path(p))) # Normalize the path
+ result.append("-I" + p)
+ return result
+ else:
+ return []
+
+_INC_DIR_MARKER_BEGIN = "#include <...>"
+
+# OSX add " (framework directory)" at the end of line, strip it.
+_OSX_FRAMEWORK_SUFFIX = " (framework directory)"
+_OSX_FRAMEWORK_SUFFIX_LEN = len(_OSX_FRAMEWORK_SUFFIX)
+
+def _cxx_inc_convert(path):
+ """Convert path returned by cc -E xc++ in a complete path. Doesn't %-escape the path!"""
+ path = path.strip()
+ if path.endswith(_OSX_FRAMEWORK_SUFFIX):
+ path = path[:-_OSX_FRAMEWORK_SUFFIX_LEN].strip()
+ return path
+
+def get_escaped_cxx_inc_directories(repository_ctx, cc, lang_flag, additional_flags = []):
+ """Compute the list of default %-escaped C++ include directories.
+
+ Args:
+ repository_ctx: The repository context.
+ cc: path to the C compiler.
+ lang_flag: value for the language flag (c, c++).
+ additional_flags: additional flags to pass to cc.
+ Returns:
+ a list of escaped system include directories.
+ """
+ result = repository_ctx.execute([cc, "-E", lang_flag, "-", "-v"] + additional_flags)
+ index1 = result.stderr.find(_INC_DIR_MARKER_BEGIN)
+ if index1 == -1:
+ return []
+ index1 = result.stderr.find("\n", index1)
+ if index1 == -1:
+ return []
+ index2 = result.stderr.rfind("\n ")
+ if index2 == -1 or index2 < index1:
+ return []
+ index2 = result.stderr.find("\n", index2 + 1)
+ if index2 == -1:
+ inc_dirs = result.stderr[index1 + 1:]
+ else:
+ inc_dirs = result.stderr[index1 + 1:index2].strip()
+
+ inc_directories = [
+ _prepare_include_path(repository_ctx, _cxx_inc_convert(p))
+ for p in inc_dirs.split("\n")
+ ]
+
+ if _is_compiler_option_supported(repository_ctx, cc, "-print-resource-dir"):
+ resource_dir = repository_ctx.execute(
+ [cc, "-print-resource-dir"],
+ ).stdout.strip() + "/share"
+ inc_directories.append(_prepare_include_path(repository_ctx, resource_dir))
+
+ return inc_directories
+
+def _is_compiler_option_supported(repository_ctx, cc, option):
+ """Checks that `option` is supported by the C compiler. Doesn't %-escape the option."""
+ result = repository_ctx.execute([
+ cc,
+ option,
+ "-o",
+ "/dev/null",
+ "-c",
+ str(repository_ctx.path("tools/cpp/empty.cc")),
+ ])
+ return result.stderr.find(option) == -1
+
+def _is_linker_option_supported(repository_ctx, cc, option, pattern):
+ """Checks that `option` is supported by the C linker. Doesn't %-escape the option."""
+ result = repository_ctx.execute([
+ cc,
+ option,
+ "-o",
+ "/dev/null",
+ str(repository_ctx.path("tools/cpp/empty.cc")),
+ ])
+ return result.stderr.find(pattern) == -1
+
+def _find_gold_linker_path(repository_ctx, cc):
+ """Checks if `gold` is supported by the C compiler.
+
+ Args:
+ repository_ctx: repository_ctx.
+ cc: path to the C compiler.
+
+ Returns:
+ String to put as value to -fuse-ld= flag, or None if gold couldn't be found.
+ """
+ result = repository_ctx.execute([
+ cc,
+ str(repository_ctx.path("tools/cpp/empty.cc")),
+ "-o",
+ "/dev/null",
+ # Some macos clang versions don't fail when setting -fuse-ld=gold, adding
+ # these lines to force it to. This also means that we will not detect
+ # gold when only a very old (year 2010 and older) is present.
+ "-Wl,--start-lib",
+ "-Wl,--end-lib",
+ "-fuse-ld=gold",
+ "-v",
+ ])
+ if result.return_code != 0:
+ return None
+
+ for line in result.stderr.splitlines():
+ if line.find("gold") == -1:
+ continue
+ for flag in line.split(" "):
+ if flag.find("gold") == -1:
+ continue
+ if flag.find("--enable-gold") > -1 or flag.find("--with-plugin-ld") > -1:
+ # skip build configuration options of gcc itself
+ # TODO(hlopko): Add redhat-like worker on the CI (#9392)
+ continue
+
+ # flag is '-fuse-ld=gold' for GCC or "/usr/lib/ld.gold" for Clang
+ # strip space, single quote, and double quotes
+ flag = flag.strip(" \"'")
+
+ # remove -fuse-ld= from GCC output so we have only the flag value part
+ flag = flag.replace("-fuse-ld=", "")
+ return flag
+ auto_configure_warning(
+ "CC with -fuse-ld=gold returned 0, but its -v output " +
+ "didn't contain 'gold', falling back to the default linker.",
+ )
+ return None
+
+def _add_compiler_option_if_supported(repository_ctx, cc, option):
+ """Returns `[option]` if supported, `[]` otherwise. Doesn't %-escape the option."""
+ return [option] if _is_compiler_option_supported(repository_ctx, cc, option) else []
+
+def _add_linker_option_if_supported(repository_ctx, cc, option, pattern):
+ """Returns `[option]` if supported, `[]` otherwise. Doesn't %-escape the option."""
+ return [option] if _is_linker_option_supported(repository_ctx, cc, option, pattern) else []
+
+def _get_no_canonical_prefixes_opt(repository_ctx, cc):
+ # If the compiler sometimes rewrites paths in the .d files without symlinks
+ # (ie when they're shorter), it confuses Bazel's logic for verifying all
+ # #included header files are listed as inputs to the action.
+
+ # The '-fno-canonical-system-headers' should be enough, but clang does not
+ # support it, so we also try '-no-canonical-prefixes' if first option does
+ # not work.
+ opt = _add_compiler_option_if_supported(
+ repository_ctx,
+ cc,
+ "-fno-canonical-system-headers",
+ )
+ if len(opt) == 0:
+ return _add_compiler_option_if_supported(
+ repository_ctx,
+ cc,
+ "-no-canonical-prefixes",
+ )
+ return opt
+
+def get_env(repository_ctx):
+ """Convert the environment in a list of export if in Homebrew. Doesn't %-escape the result!
+
+ Args:
+ repository_ctx: The repository context.
+ Returns:
+ empty string or a list of exports in case we're running with homebrew. Don't ask me why.
+ """
+ env = repository_ctx.os.environ
+ if "HOMEBREW_RUBY_PATH" in env:
+ return "\n".join([
+ "export %s='%s'" % (k, env[k].replace("'", "'\\''"))
+ for k in env
+ if k != "_" and k.find(".") == -1
+ ])
+ else:
+ return ""
+
+def _coverage_flags(repository_ctx, darwin):
+ use_llvm_cov = "1" == get_env_var(
+ repository_ctx,
+ "BAZEL_USE_LLVM_NATIVE_COVERAGE",
+ default = "0",
+ enable_warning = False,
+ )
+ if darwin or use_llvm_cov:
+ compile_flags = '"-fprofile-instr-generate", "-fcoverage-mapping"'
+ link_flags = '"-fprofile-instr-generate"'
+ else:
+ # gcc requires --coverage being passed for compilation and linking
+ # https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#Instrumentation-Options
+ compile_flags = '"--coverage"'
+ link_flags = '"--coverage"'
+ return compile_flags, link_flags
+
+def _find_generic(repository_ctx, name, env_name, overriden_tools, warn = False, silent = False):
+ """Find a generic C++ toolchain tool. Doesn't %-escape the result."""
+
+ if name in overriden_tools:
+ return overriden_tools[name]
+
+ result = name
+ env_value = repository_ctx.os.environ.get(env_name)
+ env_value_with_paren = ""
+ if env_value != None:
+ env_value = env_value.strip()
+ if env_value:
+ result = env_value
+ env_value_with_paren = " (%s)" % env_value
+ if result.startswith("/"):
+ # Absolute path, maybe we should make this suported by our which function.
+ return result
+ result = repository_ctx.which(result)
+ if result == None:
+ msg = ("Cannot find %s or %s%s; either correct your path or set the %s" +
+ " environment variable") % (name, env_name, env_value_with_paren, env_name)
+ if warn:
+ if not silent:
+ auto_configure_warning(msg)
+ else:
+ auto_configure_fail(msg)
+ return result
+
+def find_cc(repository_ctx, overriden_tools):
+ return _find_generic(repository_ctx, "gcc", "CC", overriden_tools)
+
+def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools):
+ """Configure C++ toolchain on Unix platforms.
+
+ Args:
+ repository_ctx: The repository context.
+ cpu_value: current cpu name.
+ overriden_tools: overriden tools.
+ """
+ paths = resolve_labels(repository_ctx, [
+ "@rules_cc//cc/private/toolchain:BUILD.tpl",
+ "@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl",
+ "@rules_cc//cc/private/toolchain:unix_cc_toolchain_config.bzl",
+ "@rules_cc//cc/private/toolchain:linux_cc_wrapper.sh.tpl",
+ "@rules_cc//cc/private/toolchain:osx_cc_wrapper.sh.tpl",
+ ])
+
+ repository_ctx.symlink(
+ paths["@rules_cc//cc/private/toolchain:unix_cc_toolchain_config.bzl"],
+ "cc_toolchain_config.bzl",
+ )
+
+ repository_ctx.symlink(
+ paths["@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"],
+ "armeabi_cc_toolchain_config.bzl",
+ )
+
+ repository_ctx.file("tools/cpp/empty.cc", "int main() {}")
+ darwin = cpu_value == "darwin"
+
+ cc = _find_generic(repository_ctx, "gcc", "CC", overriden_tools)
+ overriden_tools = dict(overriden_tools)
+ overriden_tools["gcc"] = cc
+ overriden_tools["gcov"] = _find_generic(
+ repository_ctx,
+ "gcov",
+ "GCOV",
+ overriden_tools,
+ warn = True,
+ silent = True,
+ )
+ if darwin:
+ overriden_tools["gcc"] = "cc_wrapper.sh"
+ overriden_tools["ar"] = "/usr/bin/libtool"
+ auto_configure_warning_maybe(repository_ctx, "CC used: " + str(cc))
+ tool_paths = _get_tool_paths(repository_ctx, overriden_tools)
+ cc_toolchain_identifier = escape_string(get_env_var(
+ repository_ctx,
+ "CC_TOOLCHAIN_NAME",
+ "local",
+ False,
+ ))
+
+ cc_wrapper_src = (
+ "@rules_cc//cc/private/toolchain:osx_cc_wrapper.sh.tpl" if darwin else "@rules_cc//cc/private/toolchain:linux_cc_wrapper.sh.tpl"
+ )
+ repository_ctx.template(
+ "cc_wrapper.sh",
+ paths[cc_wrapper_src],
+ {
+ "%{cc}": escape_string(str(cc)),
+ "%{env}": escape_string(get_env(repository_ctx)),
+ },
+ )
+
+ cxx_opts = split_escaped(get_env_var(
+ repository_ctx,
+ "BAZEL_CXXOPTS",
+ "-std=c++0x",
+ False,
+ ), ":")
+
+ bazel_linklibs = "-lstdc++:-lm"
+ bazel_linkopts = ""
+ link_opts = split_escaped(get_env_var(
+ repository_ctx,
+ "BAZEL_LINKOPTS",
+ bazel_linkopts,
+ False,
+ ), ":")
+ link_libs = split_escaped(get_env_var(
+ repository_ctx,
+ "BAZEL_LINKLIBS",
+ bazel_linklibs,
+ False,
+ ), ":")
+ gold_linker_path = _find_gold_linker_path(repository_ctx, cc)
+ cc_path = repository_ctx.path(cc)
+ if not str(cc_path).startswith(str(repository_ctx.path(".")) + "/"):
+ # cc is outside the repository, set -B
+ bin_search_flag = ["-B" + escape_string(str(cc_path.dirname))]
+ else:
+ # cc is inside the repository, don't set -B.
+ bin_search_flag = []
+
+ coverage_compile_flags, coverage_link_flags = _coverage_flags(repository_ctx, darwin)
+ builtin_include_directories = _uniq(
+ get_escaped_cxx_inc_directories(repository_ctx, cc, "-xc") +
+ get_escaped_cxx_inc_directories(repository_ctx, cc, "-xc++", cxx_opts) +
+ get_escaped_cxx_inc_directories(
+ repository_ctx,
+ cc,
+ "-xc",
+ _get_no_canonical_prefixes_opt(repository_ctx, cc),
+ ) +
+ get_escaped_cxx_inc_directories(
+ repository_ctx,
+ cc,
+ "-xc++",
+ cxx_opts + _get_no_canonical_prefixes_opt(repository_ctx, cc),
+ ),
+ )
+
+ write_builtin_include_directory_paths(repository_ctx, cc, builtin_include_directories)
+ repository_ctx.template(
+ "BUILD",
+ paths["@rules_cc//cc/private/toolchain:BUILD.tpl"],
+ {
+ "%{abi_libc_version}": escape_string(get_env_var(
+ repository_ctx,
+ "ABI_LIBC_VERSION",
+ "local",
+ False,
+ )),
+ "%{abi_version}": escape_string(get_env_var(
+ repository_ctx,
+ "ABI_VERSION",
+ "local",
+ False,
+ )),
+ "%{cc_compiler_deps}": get_starlark_list([":builtin_include_directory_paths"] + (
+ [":cc_wrapper"] if darwin else []
+ )),
+ "%{cc_toolchain_identifier}": cc_toolchain_identifier,
+ "%{compile_flags}": get_starlark_list(
+ [
+ # Security hardening requires optimization.
+ # We need to undef it as some distributions now have it enabled by default.
+ "-U_FORTIFY_SOURCE",
+ "-fstack-protector",
+ # All warnings are enabled. Maybe enable -Werror as well?
+ "-Wall",
+ # Enable a few more warnings that aren't part of -Wall.
+ ] + (
+ _add_compiler_option_if_supported(repository_ctx, cc, "-Wthread-safety") +
+ _add_compiler_option_if_supported(repository_ctx, cc, "-Wself-assign")
+ ) + (
+ # Disable problematic warnings.
+ _add_compiler_option_if_supported(repository_ctx, cc, "-Wunused-but-set-parameter") +
+ # has false positives
+ _add_compiler_option_if_supported(repository_ctx, cc, "-Wno-free-nonheap-object") +
+ # Enable coloring even if there's no attached terminal. Bazel removes the
+ # escape sequences if --nocolor is specified.
+ _add_compiler_option_if_supported(repository_ctx, cc, "-fcolor-diagnostics")
+ ) + [
+ # Keep stack frames for debugging, even in opt mode.
+ "-fno-omit-frame-pointer",
+ ],
+ ),
+ "%{compiler}": escape_string(get_env_var(
+ repository_ctx,
+ "BAZEL_COMPILER",
+ "compiler",
+ False,
+ )),
+ "%{coverage_compile_flags}": coverage_compile_flags,
+ "%{coverage_link_flags}": coverage_link_flags,
+ "%{cxx_builtin_include_directories}": get_starlark_list(builtin_include_directories),
+ "%{cxx_flags}": get_starlark_list(cxx_opts + _escaped_cplus_include_paths(repository_ctx)),
+ "%{dbg_compile_flags}": get_starlark_list(["-g"]),
+ "%{host_system_name}": escape_string(get_env_var(
+ repository_ctx,
+ "BAZEL_HOST_SYSTEM",
+ "local",
+ False,
+ )),
+ "%{link_flags}": get_starlark_list((
+ ["-fuse-ld=" + gold_linker_path] if gold_linker_path else []
+ ) + _add_linker_option_if_supported(
+ repository_ctx,
+ cc,
+ "-Wl,-no-as-needed",
+ "-no-as-needed",
+ ) + _add_linker_option_if_supported(
+ repository_ctx,
+ cc,
+ "-Wl,-z,relro,-z,now",
+ "-z",
+ ) + (
+ [
+ "-undefined",
+ "dynamic_lookup",
+ "-headerpad_max_install_names",
+ ] if darwin else bin_search_flag + [
+ # Gold linker only? Can we enable this by default?
+ # "-Wl,--warn-execstack",
+ # "-Wl,--detect-odr-violations"
+ ] + _add_compiler_option_if_supported(
+ # Have gcc return the exit code from ld.
+ repository_ctx,
+ cc,
+ "-pass-exit-codes",
+ )
+ ) + link_opts),
+ "%{link_libs}": get_starlark_list(link_libs),
+ "%{name}": cpu_value,
+ "%{opt_compile_flags}": get_starlark_list(
+ [
+ # No debug symbols.
+ # Maybe we should enable https://gcc.gnu.org/wiki/DebugFission for opt or
+ # even generally? However, that can't happen here, as it requires special
+ # handling in Bazel.
+ "-g0",
+
+ # Conservative choice for -O
+ # -O3 can increase binary size and even slow down the resulting binaries.
+ # Profile first and / or use FDO if you need better performance than this.
+ "-O2",
+
+ # Security hardening on by default.
+ # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases.
+ "-D_FORTIFY_SOURCE=1",
+
+ # Disable assertions
+ "-DNDEBUG",
+
+ # Removal of unused code and data at link time (can this increase binary
+ # size in some cases?).
+ "-ffunction-sections",
+ "-fdata-sections",
+ ],
+ ),
+ "%{opt_link_flags}": get_starlark_list(
+ [] if darwin else _add_linker_option_if_supported(
+ repository_ctx,
+ cc,
+ "-Wl,--gc-sections",
+ "-gc-sections",
+ ),
+ ),
+ "%{supports_param_files}": "0" if darwin else "1",
+ "%{supports_start_end_lib}": "True" if gold_linker_path else "False",
+ "%{target_cpu}": escape_string(get_env_var(
+ repository_ctx,
+ "BAZEL_TARGET_CPU",
+ cpu_value,
+ False,
+ )),
+ "%{target_libc}": "macosx" if darwin else escape_string(get_env_var(
+ repository_ctx,
+ "BAZEL_TARGET_LIBC",
+ "local",
+ False,
+ )),
+ "%{target_system_name}": escape_string(get_env_var(
+ repository_ctx,
+ "BAZEL_TARGET_SYSTEM",
+ "local",
+ False,
+ )),
+ "%{tool_paths}": ",\n ".join(
+ ['"%s": "%s"' % (k, v) for k, v in tool_paths.items()],
+ ),
+ "%{unfiltered_compile_flags}": get_starlark_list(
+ _get_no_canonical_prefixes_opt(repository_ctx, cc) + [
+ # Make C++ compilation deterministic. Use linkstamping instead of these
+ # compiler symbols.
+ "-Wno-builtin-macro-redefined",
+ "-D__DATE__=\\\"redacted\\\"",
+ "-D__TIMESTAMP__=\\\"redacted\\\"",
+ "-D__TIME__=\\\"redacted\\\"",
+ ],
+ ),
+ },
+ )