diff options
author | Damien Martin-Guillerez <dmarting@google.com> | 2016-03-02 16:24:13 +0000 |
---|---|---|
committer | Kristina Chodorow <kchodorow@google.com> | 2016-03-02 17:55:47 +0000 |
commit | 8fa5ae6a6364100f2a7f9130e62eb0edb447339a (patch) | |
tree | cc216ad18cd748b79ee9e9debbb399aeb54ac4a3 /tools | |
parent | be5b2ebcf70b5e89cb87060cee045a9773b496f4 (diff) |
Add a cc_configure rule to auto-configure C++ crosstool
This is the last step of http://goo.gl/fD4ZsY (issue #893).
Tests are in a separate change because they requires pretty
complex setup.
--
MOS_MIGRATED_REVID=116141979
Diffstat (limited to 'tools')
-rw-r--r-- | tools/cpp/BUILD.tpl | 54 | ||||
-rw-r--r-- | tools/cpp/CROSSTOOL | 2 | ||||
-rw-r--r-- | tools/cpp/CROSSTOOL.tpl | 63 | ||||
-rw-r--r-- | tools/cpp/cc_configure.bzl | 268 | ||||
-rwxr-xr-x | tools/cpp/osx_cc_wrapper.sh (renamed from tools/cpp/osx_gcc_wrapper.sh) | 8 | ||||
-rw-r--r-- | tools/cpp/osx_cc_wrapper.sh.tpl | 93 |
6 files changed, 483 insertions, 5 deletions
diff --git a/tools/cpp/BUILD.tpl b/tools/cpp/BUILD.tpl new file mode 100644 index 0000000000..3079485099 --- /dev/null +++ b/tools/cpp/BUILD.tpl @@ -0,0 +1,54 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "malloc", +) + +cc_library( + name = "stl", +) + +filegroup( + name = "empty", + srcs = [], +) + +# This is the entry point for --crosstool_top. Toolchains are found +# by lopping off the name of --crosstool_top and searching for +# the "${CPU}" entry in the toolchains attribute. +cc_toolchain_suite( + name = "toolchain", + toolchains = { + "%{name}": ":cc-compiler-%{name}", + }, +) + +cc_toolchain( + name = "cc-compiler-%{name}", + all_files = ":empty", + compiler_files = ":empty", + cpu = "local", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":empty", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = %{supports_param_files}, +) + + +# Android tooling requires a default toolchain for the armeabi-v7a cpu. +cc_toolchain( + name = "cc-compiler-armeabi-v7a", + all_files = ":empty", + compiler_files = ":empty", + cpu = "local", + dwp_files = ":empty", + dynamic_runtime_libs = [":empty"], + linker_files = ":empty", + objcopy_files = ":empty", + static_runtime_libs = [":empty"], + strip_files = ":empty", + supports_param_files = 1, +) diff --git a/tools/cpp/CROSSTOOL b/tools/cpp/CROSSTOOL index 6846d70b74..a668263a19 100644 --- a/tools/cpp/CROSSTOOL +++ b/tools/cpp/CROSSTOOL @@ -205,7 +205,7 @@ toolchain { tool_path { name: "compat-ld" path: "/usr/bin/ld" } tool_path { name: "cpp" path: "/usr/bin/cpp" } tool_path { name: "dwp" path: "/usr/bin/dwp" } - tool_path { name: "gcc" path: "osx_gcc_wrapper.sh" } + tool_path { name: "gcc" path: "osx_cc_wrapper.sh" } cxx_flag: "-std=c++0x" ar_flag: "-static" ar_flag: "-s" diff --git a/tools/cpp/CROSSTOOL.tpl b/tools/cpp/CROSSTOOL.tpl new file mode 100644 index 0000000000..82184bcfd9 --- /dev/null +++ b/tools/cpp/CROSSTOOL.tpl @@ -0,0 +1,63 @@ +major_version: "local" +minor_version: "" +default_target_cpu: "same_as_host" + +default_toolchain { + cpu: "%{cpu}" + toolchain_identifier: "local" +} + +default_toolchain { + cpu: "armeabi-v7a" + toolchain_identifier: "stub_armeabi-v7a" +} + +# Android tooling requires a default toolchain for the armeabi-v7a cpu. +toolchain { + abi_version: "armeabi-v7a" + abi_libc_version: "armeabi-v7a" + builtin_sysroot: "" + compiler: "compiler" + host_system_name: "armeabi-v7a" + needsPic: true + supports_gold_linker: false + supports_incremental_linker: false + supports_fission: false + supports_interface_shared_objects: false + supports_normalizing_ar: false + supports_start_end_lib: false + supports_thin_archives: false + target_libc: "armeabi-v7a" + target_cpu: "armeabi-v7a" + target_system_name: "armeabi-v7a" + toolchain_identifier: "stub_armeabi-v7a" + + tool_path { name: "ar" path: "/bin/false" } + tool_path { name: "compat-ld" path: "/bin/false" } + tool_path { name: "cpp" path: "/bin/false" } + tool_path { name: "dwp" path: "/bin/false" } + tool_path { name: "gcc" path: "/bin/false" } + tool_path { name: "gcov" path: "/bin/false" } + tool_path { name: "ld" path: "/bin/false" } + + tool_path { name: "nm" path: "/bin/false" } + tool_path { name: "objcopy" path: "/bin/false" } + tool_path { name: "objdump" path: "/bin/false" } + tool_path { name: "strip" path: "/bin/false" } + linking_mode_flags { mode: DYNAMIC } +} + +toolchain { + toolchain_identifier: "local" +%{content} + + compilation_mode_flags { + mode: DBG +%{dbg_content} + } + compilation_mode_flags { + mode: OPT +%{opt_content} + } + linking_mode_flags { mode: DYNAMIC } +} diff --git a/tools/cpp/cc_configure.bzl b/tools/cpp/cc_configure.bzl new file mode 100644 index 0000000000..0f70247791 --- /dev/null +++ b/tools/cpp/cc_configure.bzl @@ -0,0 +1,268 @@ +# 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. +"""Rules for configuring the C++ toolchain (experimental).""" + + +def _get_value(it): + """Convert `it` in serialized protobuf format.""" + if type(it) == "int": + return str(it) + elif type(it) == "bool": + return "true" if it else "false" + else: + return "\"%s\"" % it + + +def _build_crosstool(d, prefix=" "): + """Convert `d` to a string version of a CROSSTOOL file content.""" + lines = [] + for k in d: + if type(d[k]) == "list": + for it in d[k]: + lines.append("%s%s: %s" % (prefix, k, _get_value(it))) + else: + lines.append("%s%s: %s" % (prefix, k, _get_value(d[k]))) + return "\n".join(lines) + + +def _build_tool_path(d): + """Build the list of tool_path for the CROSSTOOL file.""" + lines = [] + for k in d: + lines.append(" tool_path {name: \"%s\" path: \"%s\" }" % (k, d[k])) + return "\n".join(lines) + + +def _which(ctx, cmd, default): + """A wrapper around ctx.which() to provide a fallback value.""" + result = ctx.which(cmd) + return default if result == None else str(result) + + +def _get_tool_paths(ctx, darwin, cc): + """Compute the path to the various tools.""" + return {k: _which(ctx, k, "/usr/bin/" + k) + for k in [ + "ld", + "cpp", + "dwp", + "gcov", + "nm", + "objcopy", + "objdump", + "strip", + ]} + { + "gcc": cc, + "ar": "/usr/bin/libtool" + if darwin else _which(ctx, "ar", "/usr/bin/ar") + } + + +def _ld_library_paths(ctx): + """Use ${LD_LIBRARY_PATH} to compute the list -Wl,rpath flags.""" + if "LD_LIBRARY_PATH" in ctx.os.environ: + result = [] + for p in ctx.os.environ["LD_LIBRARY_PATH"].split(":"): + p = ctx.path(p) # Normalize the path + result.append("-Wl,rpath," + p) + result.append("-L" + p) + return result + else: + return [] + + +def _get_cpu_value(ctx): + """Compute the cpu_value based on the OS name.""" + return "darwin" if ctx.os.name.lower().startswith("mac os") else "local" + + +_INC_DIR_MARKER_BEGIN = "#include <...> search starts here:" +_INC_DIR_MARKER_END = "End of search list." + + +def _get_cxx_inc_directories(ctx, cc): + """Compute the list of default C++ include directories.""" + result = ctx.execute([cc, "-E", "-xc++", "-", "-v"]) + index1 = result.stderr.find(_INC_DIR_MARKER_BEGIN) + if index1 == -1: + return [] + index2 = result.stderr.find(_INC_DIR_MARKER_END, index1) + if index2 == -1: + return [] + inc_dirs = result.stderr[index1 + len(_INC_DIR_MARKER_BEGIN):index2].strip() + return [ctx.path(p.strip()) for p in inc_dirs.split("\n")] + + +def _add_option_if_supported(ctx, cc, option): + """Checks that `option` is supported by the C compiler.""" + result = ctx.execute([cc, option]) + return [option] if result.stderr.find(option) == -1 else [] + + +def _crosstool_content(ctx, cc, cpu_value, darwin): + """Return the content for the CROSSTOOL file, in a dictionary.""" + return { + "abi_version": "local", + "abi_libc_version": "local", + "builtin_sysroot": "", + "compiler": "compiler", + "host_system_name": "local", + "needsPic": True, + "supports_gold_linker": False, + "supports_incremental_linker": False, + "supports_fission": False, + "supports_interface_shared_objects": False, + "supports_normalizing_ar": False, + "supports_start_end_lib": False, + "supports_thin_archives": False, + "target_libc": "macosx" if darwin else "local", + "target_cpu": cpu_value, + "target_system_name": "local", + "cxx_flag": "-std=c++0x", + "linker_flag": [ + "-lstdc++", + # Anticipated future default. + "-no-canonical-prefixes" + ] + (["-undefined", "dynamic_lookup"] if darwin else [ + "-B/usr/bin", + # Have gcc return the exit code from ld. + "-pass-exit-codes", + # Stamp the binary with a unique identifier. + "-Wl,--build-id=md5", + "-Wl,--hash-style=gnu" + # Gold linker only? Can we enable this by default? + # "-Wl,--warn-execstack", + # "-Wl,--detect-odr-violations" + ]) + _ld_library_paths(ctx), + "ar_flag": ["-static", "-s", "-o"] if darwin else [], + "cxx_builtin_include_directory": _get_cxx_inc_directories(ctx, cc), + "objcopy_embed_flag": ["-I", "binary"], + "unfiltered_cxx_flag": [ + # Anticipated future default. + "-no-canonical-prefixes", + ] + ([] if darwin else ["-fno-canonical-system-headers"]) + [ + # Make C++ compilation deterministic. Use linkstamping instead of these + # compiler symbols. + "-Wno-builtin-macro-redefined", + "-D__DATE__=\\\"redacted\\\"", + "-D__TIMESTAMP__=\\\"redacted\\\"", + "-D__TIME__=\\\"redacted\\\"" + ], + "compiler_flag": [ + # Security hardening on by default. + # Conservative choice; -D_FORTIFY_SOURCE=2 may be unsafe in some cases. + # We need to undef it before redefining it as some distributions now have + # it enabled by default. + "-U_FORTIFY_SOURCE", + "-D_FORTIFY_SOURCE=1", + "-fstack-protector", + # All warnings are enabled. Maybe enable -Werror as well? + "-Wall", + # Enable a few more warnings that aren't part of -Wall. + ] + (["-Wthread-safety", "-Wself-assign"] if darwin else [ + "-Wunused-but-set-parameter", + # Disable some that are problematic. + "-Wno-free-nonheap-object", # has false positives + "-Wl,-z,-relro,-z,now" + ]) + ( + # Enable coloring even if there's no attached terminal. Bazel removes the + # escape sequences if --nocolor is specified. + _add_option_if_supported(ctx, cc, "-fcolor-diagnostics")) + [ + # Keep stack frames for debugging, even in opt mode. + "-fno-omit-frame-pointer", + ], + } + + +def _opt_content(darwin): + """Return the content of the opt specific section of the CROSSTOOL file.""" + return { + "compiler_flag": [ + # 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", + + # Disable assertions + "-DNDEBUG", + + # Removal of unused code and data at link time (can this increase binary size in some cases?). + "-ffunction-sections", + "-fdata-sections" + ], + "linker_flag": [] if darwin else ["-Wl,--gc-sections"] + } + + +def _dbg_content(): + """Return the content of the dbg specific section of the CROSSTOOL file.""" + # Enable debug symbols + return {"compiler_flag": "-g"} + + +def _find_cc(ctx): + """Find the C++ compiler.""" + if "CC" in ctx.os.environ: + return ctx.path(ctx.os.environ["CC"]) + else: + cc = ctx.which("gcc") + if cc == None: + fail( + "Cannot find gcc, either correct your path or set the CC environment" + " variable") + return cc + + +def _tpl(ctx, tpl, substitutions={}): + ctx.template(tpl, Label("@bazel_tools//tools/cpp:%s.tpl" % tpl), + substitutions) + + +def _impl(ctx): + cpu_value = _get_cpu_value(ctx) + darwin = cpu_value == "darwin" + cc = _find_cc(ctx) + crosstool_cc = "osx_cc_wrapper.sh" if darwin else str(cc) + darwin = cpu_value == "darwin" + tool_paths = _get_tool_paths(ctx, darwin, crosstool_cc) + crosstool_content = _crosstool_content(ctx, cc, cpu_value, darwin) + opt_content = _opt_content(darwin) + dbg_content = _dbg_content() + _tpl(ctx, "BUILD", { + "%{name}": cpu_value, + "%{supports_param_files}": "0" if darwin else "1" + }) + _tpl(ctx, "osx_gcc_wrapper.sh", {"%{cc}": str(cc)}) + _tpl(ctx, "CROSSTOOL", { + "%{cpu}": cpu_value, + "%{content}": _build_crosstool(crosstool_content) + "\n" + + _build_tool_path(tool_paths), + "%{opt_content}": _build_crosstool(opt_content, " "), + "%{dbg_content}": _build_crosstool(dbg_content, " "), + }) + + +cc_autoconf = repository_rule(_impl, local=True) + + +def cc_configure(): + """A C++ configuration rules that generate the crosstool file.""" + cc_autoconf(name="local_config_cc") + native.bind(name="cc_toolchain", actual="@local_config_cc//:toolchain") diff --git a/tools/cpp/osx_gcc_wrapper.sh b/tools/cpp/osx_cc_wrapper.sh index c822836922..739fca5de6 100755 --- a/tools/cpp/osx_gcc_wrapper.sh +++ b/tools/cpp/osx_cc_wrapper.sh @@ -14,12 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# OS X rpath is not really working. This is a wrapper script around gcc -# to simulate rpath behavior. +# OS X relpath is not really working. This is a wrapper script around gcc +# to simulate relpath behavior. # # This wrapper uses install_name_tool to replace all paths in the binary # (bazel-out/.../path/to/original/library.so) by the paths relative to -# the binary. It parses the command line to behaves as rpath is supposed +# the binary. It parses the command line to behave as rpath is supposed # to work. # # See https://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac @@ -27,7 +27,7 @@ # set -eu -GCC="/usr/bin/gcc" +GCC=/usr/bin/gcc INSTALL_NAME_TOOL="/usr/bin/install_name_tool" LIBS= diff --git a/tools/cpp/osx_cc_wrapper.sh.tpl b/tools/cpp/osx_cc_wrapper.sh.tpl new file mode 100644 index 0000000000..5e66680f6a --- /dev/null +++ b/tools/cpp/osx_cc_wrapper.sh.tpl @@ -0,0 +1,93 @@ +#!/bin/bash +# +# Copyright 2015 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. +# +# OS X relpath is not really working. This is a wrapper script around gcc +# to simulate relpath behavior. +# +# This wrapper uses install_name_tool to replace all paths in the binary +# (bazel-out/.../path/to/original/library.so) by the paths relative to +# the binary. It parses the command line to behave as rpath is supposed +# to work. +# +# See https://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac +# on how to set those paths for Mach-O binaries. +# +set -eu + +INSTALL_NAME_TOOL="/usr/bin/install_name_tool" + +LIBS= +LIB_DIRS= +RPATH= +OUTPUT= +# let parse the option list +for i in "$@"; do + if [[ "${OUTPUT}" = "1" ]]; then + OUTPUT=$i + elif [[ "$i" =~ ^-l(.*)$ ]]; then + # lib + LIBS="${BASH_REMATCH[1]} $LIBS" + elif [[ "$i" =~ ^-L(.*)$ ]]; then + # lib + LIB_DIRS="${BASH_REMATCH[1]} $LIB_DIRS" + elif [[ "$i" =~ ^-Wl,-rpath,\$ORIGIN/(.*)$ ]]; then + # rpath + RPATH=${BASH_REMATCH[1]} + elif [[ "$i" = "-o" ]]; then + # output is coming + OUTPUT=1 + fi +done + +# Call the C++ compiler +%{cc} "$@" + +function get_library_path() { + for libdir in ${LIB_DIRS}; do + if [ -f ${libdir}/lib$1.so ]; then + echo "${libdir}/lib$1.so" + fi + done +} + +# A convenient method to return the actual path even for non symlinks +# and multi-level symlinks. +function get_realpath() { + local previous="$1" + local next=$(readlink "${previous}") + while [ -n "${next}" ]; do + previous="${next}" + next=$(readlink "${previous}") + done + echo "${previous}" +} + +# Get the path of a lib inside a tool +function get_otool_path() { + # the lib path is the path of the original lib relative to the workspace + get_realpath $1 | sed 's|^.*/bazel-out/|bazel-out/|' +} + +# Do replacements in the output +if [ -n "${RPATH}" ]; then + for lib in ${LIBS}; do + libpath=$(get_library_path ${lib}) + if [ -n "${libpath}" ]; then + ${INSTALL_NAME_TOOL} -change $(get_otool_path "${libpath}") "@loader_path/${RPATH}/lib${lib}.so" "${OUTPUT}" + fi + done +fi + |