diff options
Diffstat (limited to 'cc/private/toolchain/unix_cc_configure.bzl')
-rw-r--r-- | cc/private/toolchain/unix_cc_configure.bzl | 587 |
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\\\"", + ], + ), + }, + ) |