diff --git a/WORKSPACE b/WORKSPACE index d37b398..8d3caad 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -24,3 +24,15 @@ rules_cc_internal_deps() load("//:internal_setup.bzl", "rules_cc_internal_setup") rules_cc_internal_setup() + +# We're pinning to a commit because this project does not have a recent release. +# Nothing special about this commit, though. +http_archive( + name = "com_google_googletest", + sha256 = "0fb00ff413f6b9b80ccee44a374ca7a18af7315aea72a43c62f2acd1ca74e9b5", + strip_prefix = "googletest-f13bbe2992d188e834339abe6f715b2b2f840a77", + urls = [ + "https://mirror.bazel.build/github.com/google/googletest/archive/f13bbe2992d188e834339abe6f715b2b2f840a77.tar.gz", + "https://github.com/google/googletest/archive/f13bbe2992d188e834339abe6f715b2b2f840a77.tar.gz", + ], +) diff --git a/cc/BUILD b/cc/BUILD index 145d2f9..47af570 100644 --- a/cc/BUILD +++ b/cc/BUILD @@ -1,8 +1,46 @@ +# Copyright 2018 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. + +package(default_visibility = ["//visibility:public"]) + licenses(["notice"]) # Apache 2.0 +filegroup( + name = "all_files_for_testing", + srcs = glob(["**"]) + [ + "//cc/private/rules_impl:srcs", + "//cc/private/toolchain:srcs", + ], +) + +exports_files([ + "defs.bzl", + "action_names.bzl", +]) + +# The toolchain type used to distinguish cc toolchains. alias( name = "toolchain_type", actual = "@bazel_tools//tools/cpp:toolchain_type", ) -exports_files(["defs.bzl"]) +filegroup( + name = "action_names_test_files", + testonly = True, + srcs = [ + "BUILD", + "action_names.bzl", + ], + visibility = ["//visibility:public"], +) diff --git a/cc/action_names.bzl b/cc/action_names.bzl new file mode 100644 index 0000000..60e8a5f --- /dev/null +++ b/cc/action_names.bzl @@ -0,0 +1,122 @@ +# Copyright 2018 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. +"""Constants for action names used for C++ rules.""" + +# Name for the C compilation action. +C_COMPILE_ACTION_NAME = "c-compile" + +# Name of the C++ compilation action. +CPP_COMPILE_ACTION_NAME = "c++-compile" + +# Name of the linkstamp-compile action. +LINKSTAMP_COMPILE_ACTION_NAME = "linkstamp-compile" + +# Name of the action used to compute CC_FLAGS make variable. +CC_FLAGS_MAKE_VARIABLE_ACTION_NAME = "cc-flags-make-variable" + +# Name of the C++ module codegen action. +CPP_MODULE_CODEGEN_ACTION_NAME = "c++-module-codegen" + +# Name of the C++ header parsing action. +CPP_HEADER_PARSING_ACTION_NAME = "c++-header-parsing" + +# Name of the C++ module compile action. +CPP_MODULE_COMPILE_ACTION_NAME = "c++-module-compile" + +# Name of the assembler action. +ASSEMBLE_ACTION_NAME = "assemble" + +# Name of the assembly preprocessing action. +PREPROCESS_ASSEMBLE_ACTION_NAME = "preprocess-assemble" + +# Name of the action producing ThinLto index. +LTO_INDEXING_ACTION_NAME = "lto-indexing" + +# Name of the action producing ThinLto index for executable. +LTO_INDEX_FOR_EXECUTABLE_ACTION_NAME = "lto-index-for-executable" + +# Name of the action producing ThinLto index for dynamic library. +LTO_INDEX_FOR_DYNAMIC_LIBRARY_ACTION_NAME = "lto-index-for-dynamic-library" + +# Name of the action producing ThinLto index for nodeps dynamic library. +LTO_INDEX_FOR_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME = "lto-index-for-nodeps-dynamic-library" + +# Name of the action compiling lto bitcodes into native objects. +LTO_BACKEND_ACTION_NAME = "lto-backend" + +# Name of the link action producing executable binary. +CPP_LINK_EXECUTABLE_ACTION_NAME = "c++-link-executable" + +# Name of the link action producing dynamic library. +CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME = "c++-link-dynamic-library" + +# Name of the link action producing dynamic library that doesn't include it's +# transitive dependencies. +CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME = "c++-link-nodeps-dynamic-library" + +# Name of the archiving action producing static library. +CPP_LINK_STATIC_LIBRARY_ACTION_NAME = "c++-link-static-library" + +# Name of the action stripping the binary. +STRIP_ACTION_NAME = "strip" + +# A string constant for the objc archive action. +OBJC_ARCHIVE_ACTION_NAME = "objc-archive" + +# A string constant for the objc compilation action. +OBJC_COMPILE_ACTION_NAME = "objc-compile" + +# A string constant for the objc++ compile action. +OBJCPP_COMPILE_ACTION_NAME = "objc++-compile" + +# A string constant for the objc executable link action. +OBJC_EXECUTABLE_ACTION_NAME = "objc-executable" + +# A string constant for the objc++ executable link action. +OBJCPP_EXECUTABLE_ACTION_NAME = "objc++-executable" + +# A string constant for the objc fully-link link action. +OBJC_FULLY_LINK_ACTION_NAME = "objc-fully-link" + +# A string constant for the clif actions. +CLIF_MATCH_ACTION_NAME = "clif-match" + +ACTION_NAMES = struct( + c_compile = C_COMPILE_ACTION_NAME, + cpp_compile = CPP_COMPILE_ACTION_NAME, + linkstamp_compile = LINKSTAMP_COMPILE_ACTION_NAME, + cc_flags_make_variable = CC_FLAGS_MAKE_VARIABLE_ACTION_NAME, + cpp_module_codegen = CPP_MODULE_CODEGEN_ACTION_NAME, + cpp_header_parsing = CPP_HEADER_PARSING_ACTION_NAME, + cpp_module_compile = CPP_MODULE_COMPILE_ACTION_NAME, + assemble = ASSEMBLE_ACTION_NAME, + preprocess_assemble = PREPROCESS_ASSEMBLE_ACTION_NAME, + lto_indexing = LTO_INDEXING_ACTION_NAME, + lto_backend = LTO_BACKEND_ACTION_NAME, + lto_index_for_executable = LTO_INDEX_FOR_EXECUTABLE_ACTION_NAME, + lto_index_for_dynamic_library = LTO_INDEX_FOR_DYNAMIC_LIBRARY_ACTION_NAME, + lto_index_for_nodeps_dynamic_library = LTO_INDEX_FOR_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME, + cpp_link_executable = CPP_LINK_EXECUTABLE_ACTION_NAME, + cpp_link_dynamic_library = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, + cpp_link_nodeps_dynamic_library = CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME, + cpp_link_static_library = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, + strip = STRIP_ACTION_NAME, + objc_archive = OBJC_ARCHIVE_ACTION_NAME, + objc_compile = OBJC_COMPILE_ACTION_NAME, + objc_executable = OBJC_EXECUTABLE_ACTION_NAME, + objc_fully_link = OBJC_FULLY_LINK_ACTION_NAME, + objcpp_compile = OBJCPP_COMPILE_ACTION_NAME, + objcpp_executable = OBJCPP_EXECUTABLE_ACTION_NAME, + clif_match = CLIF_MATCH_ACTION_NAME, +) diff --git a/cc/defs.bzl b/cc/defs.bzl index 5060efe..8de01bd 100644 --- a/cc/defs.bzl +++ b/cc/defs.bzl @@ -14,8 +14,8 @@ """Starlark rules for building C++ projects.""" -load("@bazel_tools//tools/cpp:cc_flags_supplier.bzl", _cc_flags_supplier = "cc_flags_supplier") -load("@bazel_tools//tools/cpp:compiler_flag.bzl", _compiler_flag = "compiler_flag") +load("@rules_cc//cc/private/rules_impl:cc_flags_supplier.bzl", _cc_flags_supplier = "cc_flags_supplier") +load("@rules_cc//cc/private/rules_impl:compiler_flag.bzl", _compiler_flag = "compiler_flag") _MIGRATION_TAG = "__CC_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__" diff --git a/cc/find_cc_toolchain.bzl b/cc/find_cc_toolchain.bzl index 8964772..5784fa7 100644 --- a/cc/find_cc_toolchain.bzl +++ b/cc/find_cc_toolchain.bzl @@ -24,7 +24,7 @@ Returns the current `CcToolchainInfo`. foo = rule( implementation = _foo_impl, attrs = { - "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), + "_cc_toolchain": attr.label(default = Label("@rules_cc//cc/private/toolchain:current_cc_toolchain")), }, ) * When https://github.com/bazelbuild/bazel/issues/7260 **is** flipped, current diff --git a/cc/private/rules_impl/BUILD b/cc/private/rules_impl/BUILD new file mode 100644 index 0000000..54eef46 --- /dev/null +++ b/cc/private/rules_impl/BUILD @@ -0,0 +1,14 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "srcs", + srcs = glob( + ["**"], + exclude = [ + ".*", + "*~", + ], + ), +) diff --git a/cc/private/rules_impl/cc_flags_supplier.bzl b/cc/private/rules_impl/cc_flags_supplier.bzl new file mode 100644 index 0000000..3711369 --- /dev/null +++ b/cc/private/rules_impl/cc_flags_supplier.bzl @@ -0,0 +1,35 @@ +# Copyright 2018 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. +"""Rule that provides the CC_FLAGS Make variable.""" + +load("//cc:action_names.bzl", "CC_FLAGS_MAKE_VARIABLE_ACTION_NAME") +load("//cc/private/rules_impl:cc_flags_supplier_lib.bzl", "build_cc_flags") +load("//cc:toolchain_utils.bzl", "find_cpp_toolchain") + +def _cc_flags_supplier_impl(ctx): + cc_toolchain = find_cpp_toolchain(ctx) + cc_flags = build_cc_flags(ctx, cc_toolchain, CC_FLAGS_MAKE_VARIABLE_ACTION_NAME) + variables = platform_common.TemplateVariableInfo({ + "CC_FLAGS": cc_flags, + }) + return [variables] + +cc_flags_supplier = rule( + implementation = _cc_flags_supplier_impl, + attrs = { + "_cc_toolchain": attr.label(default = Label("@rules_cc//cc/private/toolchain:current_cc_toolchain")), + }, + toolchains = ["@rules_cc//cc:toolchain_type"], + fragments = ["cpp"], +) diff --git a/cc/private/rules_impl/cc_flags_supplier_lib.bzl b/cc/private/rules_impl/cc_flags_supplier_lib.bzl new file mode 100644 index 0000000..042ecef --- /dev/null +++ b/cc/private/rules_impl/cc_flags_supplier_lib.bzl @@ -0,0 +1,71 @@ +# Copyright 2018 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. +"""Library of functions that provide the CC_FLAGS Make variable.""" + +# This should match the logic in CcCommon.computeCcFlags: +def build_cc_flags(ctx, cc_toolchain, action_name): + """Determine the value for CC_FLAGS based on the given toolchain.""" + + # Get default cc flags from toolchain's make_variables. + legacy_cc_flags = cc_common.legacy_cc_flags_make_variable_do_not_use( + cc_toolchain = cc_toolchain, + ) + + # Determine the sysroot flag. + sysroot_cc_flags = _from_sysroot(cc_toolchain) + + # Flags from feature config. + feature_config_cc_flags = _from_features(ctx, cc_toolchain, action_name) + + # Combine the different sources, but only add the sysroot flag if nothing + # else adds sysroot. + # If added, it must appear before the feature config flags. + cc_flags = [] + if legacy_cc_flags: + cc_flags.append(legacy_cc_flags) + if sysroot_cc_flags and not _contains_sysroot(feature_config_cc_flags): + cc_flags.append(sysroot_cc_flags) + cc_flags.extend(feature_config_cc_flags) + + return " ".join(cc_flags) + +def _contains_sysroot(flags): + for flag in flags: + if "--sysroot=" in flag: + return True + return False + +def _from_sysroot(cc_toolchain): + sysroot = cc_toolchain.sysroot + if sysroot: + return "--sysroot=%s" % sysroot + else: + return None + +def _from_features(ctx, cc_toolchain, action_name): + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + + variables = cc_common.empty_variables() + + cc_flags = cc_common.get_memory_inefficient_command_line( + feature_configuration = feature_configuration, + action_name = action_name, + variables = variables, + ) + return cc_flags diff --git a/cc/private/rules_impl/compiler_flag.bzl b/cc/private/rules_impl/compiler_flag.bzl new file mode 100644 index 0000000..b399922 --- /dev/null +++ b/cc/private/rules_impl/compiler_flag.bzl @@ -0,0 +1,29 @@ +# Copyright 2018 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. + +"""Rule that allows select() to differentiate between compilers.""" + +load("@rules_cc//cc:toolchain_utils.bzl", "find_cpp_toolchain") + +def _compiler_flag_impl(ctx): + toolchain = find_cpp_toolchain(ctx) + return [config_common.FeatureFlagInfo(value = toolchain.compiler)] + +compiler_flag = rule( + implementation = _compiler_flag_impl, + attrs = { + "_cc_toolchain": attr.label(default = Label("@rules_cc//cc/private/toolchain:current_cc_toolchain")), + }, + toolchains = ["@rules_cc//cc:toolchain_type"], +) diff --git a/cc/private/toolchain/BUILD b/cc/private/toolchain/BUILD new file mode 100644 index 0000000..9860828 --- /dev/null +++ b/cc/private/toolchain/BUILD @@ -0,0 +1,124 @@ +# Copyright 2018 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. +# +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +load("//cc:defs.bzl", "cc_flags_supplier", "cc_library", "compiler_flag") + +# It is frequently necessary to constrain platforms based on the cc compiler type. +constraint_setting(name = "cc_compiler") + +constraint_value( + name = "clang", + constraint_setting = ":cc_compiler", +) + +constraint_value( + name = "gcc", + constraint_setting = ":cc_compiler", +) + +constraint_value( + name = "msvc", + constraint_setting = ":cc_compiler", +) + +constraint_value( + name = "clang-cl", + constraint_setting = ":cc_compiler", +) + +constraint_value( + name = "mingw", + constraint_setting = ":cc_compiler", +) + +constraint_value( + name = "msys", + constraint_setting = ":cc_compiler", +) + +cc_toolchain_alias(name = "current_cc_toolchain") + +cc_host_toolchain_alias(name = "current_cc_host_toolchain") + +cc_libc_top_alias(name = "current_libc_top") + +cc_library( + name = "malloc", +) + +filegroup( + name = "grep-includes", + srcs = ["grep-includes.sh"], +) + +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 +# "cc-compiler-${CPU}" in this BUILD file, where CPU is the target CPU +# specified in --cpu. +# +# This file group should include +# * all cc_toolchain targets supported +# * all file groups that said cc_toolchain might refer to +alias( + name = "toolchain", + actual = "//external:cc_toolchain", +) + +filegroup( + name = "srcs", + srcs = glob(["**"]) + [ + "//cc/private/toolchain/runfiles:srcs", + ], +) + +filegroup( + name = "embedded_tools", + srcs = glob(["**"]) + [ + "//cc:action_names.bzl", + "//cc/private/toolchain/runfiles:embedded_tools", + ], +) + +filegroup( + name = "interface_library_builder", + srcs = ["build_interface_so"], +) + +filegroup( + name = "osx_wrapper", + srcs = ["osx_cc_wrapper.sh"], +) + +filegroup( + name = "link_dynamic_library", + srcs = ["link_dynamic_library.sh"], +) + +filegroup( + name = "lib_cc_configure", + srcs = ["lib_cc_configure.bzl"], +) + +compiler_flag(name = "compiler") + +cc_flags_supplier(name = "cc_flags") diff --git a/cc/private/toolchain/BUILD.empty b/cc/private/toolchain/BUILD.empty new file mode 100644 index 0000000..fd95fc4 --- /dev/null +++ b/cc/private/toolchain/BUILD.empty @@ -0,0 +1,50 @@ +# Copyright 2018 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. + +package(default_visibility = ["//visibility:public"]) + +load(":cc_toolchain_config.bzl", "cc_toolchain_config") + +cc_library( + name = "malloc", +) + +filegroup( + name = "empty", + srcs = [], +) + +cc_toolchain_suite( + name = "toolchain", + toolchains = { + "local|local": ":local", + "local": ":local", + }, +) + +cc_toolchain( + name = "local", + toolchain_identifier = "local", + toolchain_config = ":local_config", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", +) + +cc_toolchain_config(name = "local_config") diff --git a/cc/private/toolchain/BUILD.static.freebsd b/cc/private/toolchain/BUILD.static.freebsd new file mode 100644 index 0000000..c1fa44e --- /dev/null +++ b/cc/private/toolchain/BUILD.static.freebsd @@ -0,0 +1,112 @@ +# Copyright 2018 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. + +# This becomes the BUILD file for @local_config_cc// under FreeBSD. + +package(default_visibility = ["//visibility:public"]) + +load(":cc_toolchain_config.bzl", "cc_toolchain_config") +load("@rules_cc//cc:defs.bzl", "cc_toolchain_suite", "cc_toolchain", "cc_library") + +cc_library( + name = "malloc", +) + +filegroup( + name = "empty", + srcs = [], +) + +# Hardcoded toolchain, legacy behaviour. +cc_toolchain_suite( + name = "toolchain", + toolchains = { + "armeabi-v7a|compiler": ":cc-compiler-armeabi-v7a", + "freebsd|compiler": ":cc-compiler-freebsd", + "armeabi-v7a": ":cc-compiler-armeabi-v7a", + "freebsd": ":cc-compiler-freebsd", + }, +) + +cc_toolchain( + name = "cc-compiler-freebsd", + toolchain_identifier = "local_freebsd", + toolchain_config = ":local_freebsd", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, +) + +cc_toolchain_config( + name = "local_freebsd", + cpu = "freebsd", +) + +toolchain( + name = "cc-toolchain-freebsd", + exec_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:freebsd", + ], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:freebsd", + ], + toolchain = ":cc-compiler-freebsd", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +cc_toolchain( + name = "cc-compiler-armeabi-v7a", + toolchain_identifier = "stub_armeabi-v7a", + toolchain_config = ":stub_armeabi-v7a", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, +) + +cc_toolchain_config( + name = "stub_armeabi-v7a", + cpu = "armeabi-v7a", +) + +toolchain( + name = "cc-toolchain-armeabi-v7a", + exec_compatible_with = [ + "@platforms//cpu:arm", + ], + target_compatible_with = [ + "@platforms//cpu:arm", + "@platforms//os:android", + ], + toolchain = ":cc-compiler-armeabi-v7a", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +filegroup( + name = "link_dynamic_library", + srcs = ["link_dynamic_library.sh"], +) diff --git a/cc/private/toolchain/BUILD.toolchains.tpl b/cc/private/toolchain/BUILD.toolchains.tpl new file mode 100644 index 0000000..05ea101 --- /dev/null +++ b/cc/private/toolchain/BUILD.toolchains.tpl @@ -0,0 +1,19 @@ +load("@local_config_platform//:constraints.bzl", "HOST_CONSTRAINTS") +toolchain( + name = "cc-toolchain-%{name}", + exec_compatible_with = HOST_CONSTRAINTS, + target_compatible_with = HOST_CONSTRAINTS, + toolchain = "@local_config_cc//:cc-compiler-%{name}", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +toolchain( + name = "cc-toolchain-armeabi-v7a", + exec_compatible_with = HOST_CONSTRAINTS, + target_compatible_with = [ + "@platforms//cpu:arm", + "@platforms//os:android", + ], + toolchain = "@local_config_cc//:cc-compiler-armeabi-v7a", + toolchain_type = "@rules_cc//cc:toolchain_type", +) diff --git a/cc/private/toolchain/BUILD.tpl b/cc/private/toolchain/BUILD.tpl new file mode 100644 index 0000000..d40f7f5 --- /dev/null +++ b/cc/private/toolchain/BUILD.tpl @@ -0,0 +1,113 @@ +# 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. + +# This becomes the BUILD file for @local_config_cc// under non-FreeBSD unixes. + +package(default_visibility = ["//visibility:public"]) + +load(":cc_toolchain_config.bzl", "cc_toolchain_config") +load(":armeabi_cc_toolchain_config.bzl", "armeabi_cc_toolchain_config") +load("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite") + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "malloc", +) + +filegroup( + name = "empty", + srcs = [], +) + +filegroup( + name = "cc_wrapper", + srcs = ["cc_wrapper.sh"], +) + +filegroup( + name = "compiler_deps", + srcs = glob(["extra_tools/**"], allow_empty = True) + ["%{cc_compiler_deps}"], +) + +# 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}|%{compiler}": ":cc-compiler-%{name}", + "%{name}": ":cc-compiler-%{name}", + "armeabi-v7a|compiler": ":cc-compiler-armeabi-v7a", + "armeabi-v7a": ":cc-compiler-armeabi-v7a", + }, +) + +cc_toolchain( + name = "cc-compiler-%{name}", + toolchain_identifier = "%{cc_toolchain_identifier}", + toolchain_config = ":%{cc_toolchain_identifier}", + all_files = ":compiler_deps", + ar_files = ":compiler_deps", + as_files = ":compiler_deps", + compiler_files = ":compiler_deps", + dwp_files = ":empty", + linker_files = ":compiler_deps", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = %{supports_param_files}, +) + +cc_toolchain_config( + name = "%{cc_toolchain_identifier}", + cpu = "%{target_cpu}", + compiler = "%{compiler}", + toolchain_identifier = "%{cc_toolchain_identifier}", + host_system_name = "%{host_system_name}", + target_system_name = "%{target_system_name}", + target_libc = "%{target_libc}", + abi_version = "%{abi_version}", + abi_libc_version = "%{abi_libc_version}", + cxx_builtin_include_directories = [%{cxx_builtin_include_directories}], + tool_paths = {%{tool_paths}}, + compile_flags = [%{compile_flags}], + opt_compile_flags = [%{opt_compile_flags}], + dbg_compile_flags = [%{dbg_compile_flags}], + cxx_flags = [%{cxx_flags}], + link_flags = [%{link_flags}], + link_libs = [%{link_libs}], + opt_link_flags = [%{opt_link_flags}], + unfiltered_compile_flags = [%{unfiltered_compile_flags}], + coverage_compile_flags = [%{coverage_compile_flags}], + coverage_link_flags = [%{coverage_link_flags}], + supports_start_end_lib = %{supports_start_end_lib}, +) + +# Android tooling requires a default toolchain for the armeabi-v7a cpu. +cc_toolchain( + name = "cc-compiler-armeabi-v7a", + toolchain_identifier = "stub_armeabi-v7a", + toolchain_config = ":stub_armeabi-v7a", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 1, +) + +armeabi_cc_toolchain_config(name = "stub_armeabi-v7a") diff --git a/cc/private/toolchain/BUILD.windows.tpl b/cc/private/toolchain/BUILD.windows.tpl new file mode 100644 index 0000000..7295700 --- /dev/null +++ b/cc/private/toolchain/BUILD.windows.tpl @@ -0,0 +1,301 @@ +# Copyright 2018 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. + +# This becomes the BUILD file for @local_config_cc// under Windows. + +package(default_visibility = ["//visibility:public"]) + +load("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite", "cc_library") +load(":windows_cc_toolchain_config.bzl", "cc_toolchain_config") +load(":armeabi_cc_toolchain_config.bzl", "armeabi_cc_toolchain_config") +cc_library( + name = "malloc", +) + +filegroup( + name = "empty", + srcs = [], +) + +# Hardcoded toolchain, legacy behaviour. +cc_toolchain_suite( + name = "toolchain", + toolchains = { + "armeabi-v7a|compiler": ":cc-compiler-armeabi-v7a", + "x64_windows|msvc-cl": ":cc-compiler-x64_windows", + "x64_windows|msys-gcc": ":cc-compiler-x64_windows_msys", + "x64_windows|mingw-gcc": ":cc-compiler-x64_windows_mingw", + "x64_windows|clang-cl": ":cc-compiler-x64_windows-clang-cl", + "x64_windows_msys": ":cc-compiler-x64_windows_msys", + "x64_windows": ":cc-compiler-x64_windows", + "armeabi-v7a": ":cc-compiler-armeabi-v7a", + }, +) + +cc_toolchain( + name = "cc-compiler-x64_windows_msys", + toolchain_identifier = "msys_x64", + toolchain_config = ":msys_x64", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 1, +) + +cc_toolchain_config( + name = "msys_x64", + cpu = "x64_windows", + compiler = "msys-gcc", + host_system_name = "local", + target_system_name = "local", + target_libc = "msys", + abi_version = "local", + abi_libc_version = "local", + cxx_builtin_include_directories = [%{cxx_builtin_include_directories}], + tool_paths = {%{tool_paths}}, + tool_bin_path = "%{tool_bin_path}", + dbg_mode_debug_flag = "%{dbg_mode_debug_flag}", + fastbuild_mode_debug_flag = "%{fastbuild_mode_debug_flag}", +) + +toolchain( + name = "cc-toolchain-x64_windows_msys", + exec_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + "@rules_cc//cc/private/toolchain:msys", + ], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + ], + toolchain = ":cc-compiler-x64_windows_msys", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +cc_toolchain( + name = "cc-compiler-x64_windows_mingw", + toolchain_identifier = "msys_x64_mingw", + toolchain_config = ":msys_x64_mingw", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, +) + +cc_toolchain_config( + name = "msys_x64_mingw", + cpu = "x64_windows", + compiler = "mingw-gcc", + host_system_name = "local", + target_system_name = "local", + target_libc = "mingw", + abi_version = "local", + abi_libc_version = "local", + tool_bin_path = "%{mingw_tool_bin_path}", + cxx_builtin_include_directories = [%{mingw_cxx_builtin_include_directories}], + tool_paths = {%{mingw_tool_paths}}, + dbg_mode_debug_flag = "%{dbg_mode_debug_flag}", + fastbuild_mode_debug_flag = "%{fastbuild_mode_debug_flag}", +) + +toolchain( + name = "cc-toolchain-x64_windows_mingw", + exec_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + "@rules_cc//cc/private/toolchain:mingw", + ], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + ], + toolchain = ":cc-compiler-x64_windows_mingw", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +cc_toolchain( + name = "cc-compiler-x64_windows", + toolchain_identifier = "msvc_x64", + toolchain_config = ":msvc_x64", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 1, +) + +cc_toolchain_config( + name = "msvc_x64", + cpu = "x64_windows", + compiler = "msvc-cl", + host_system_name = "local", + target_system_name = "local", + target_libc = "msvcrt", + abi_version = "local", + abi_libc_version = "local", + toolchain_identifier = "msvc_x64", + msvc_env_tmp = "%{msvc_env_tmp}", + msvc_env_path = "%{msvc_env_path}", + msvc_env_include = "%{msvc_env_include}", + msvc_env_lib = "%{msvc_env_lib}", + msvc_cl_path = "%{msvc_cl_path}", + msvc_ml_path = "%{msvc_ml_path}", + msvc_link_path = "%{msvc_link_path}", + msvc_lib_path = "%{msvc_lib_path}", + cxx_builtin_include_directories = [%{msvc_cxx_builtin_include_directories}], + tool_paths = { + "ar": "%{msvc_lib_path}", + "ml": "%{msvc_ml_path}", + "cpp": "%{msvc_cl_path}", + "gcc": "%{msvc_cl_path}", + "gcov": "wrapper/bin/msvc_nop.bat", + "ld": "%{msvc_link_path}", + "nm": "wrapper/bin/msvc_nop.bat", + "objcopy": "wrapper/bin/msvc_nop.bat", + "objdump": "wrapper/bin/msvc_nop.bat", + "strip": "wrapper/bin/msvc_nop.bat", + }, + default_link_flags = ["/MACHINE:X64"], + dbg_mode_debug_flag = "%{dbg_mode_debug_flag}", + fastbuild_mode_debug_flag = "%{fastbuild_mode_debug_flag}", +) + +toolchain( + name = "cc-toolchain-x64_windows", + exec_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + ], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + ], + toolchain = ":cc-compiler-x64_windows", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +cc_toolchain( + name = "cc-compiler-x64_windows-clang-cl", + toolchain_identifier = "clang_cl_x64", + toolchain_config = ":clang_cl_x64", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 1, +) + +cc_toolchain_config( + name = "clang_cl_x64", + cpu = "x64_windows", + compiler = "clang-cl", + host_system_name = "local", + target_system_name = "local", + target_libc = "msvcrt", + abi_version = "local", + abi_libc_version = "local", + toolchain_identifier = "clang_cl_x64", + msvc_env_tmp = "%{clang_cl_env_tmp}", + msvc_env_path = "%{clang_cl_env_path}", + msvc_env_include = "%{clang_cl_env_include}", + msvc_env_lib = "%{clang_cl_env_lib}", + msvc_cl_path = "%{clang_cl_cl_path}", + msvc_ml_path = "%{clang_cl_ml_path}", + msvc_link_path = "%{clang_cl_link_path}", + msvc_lib_path = "%{clang_cl_lib_path}", + cxx_builtin_include_directories = [%{clang_cl_cxx_builtin_include_directories}], + tool_paths = { + "ar": "%{clang_cl_lib_path}", + "ml": "%{clang_cl_ml_path}", + "cpp": "%{clang_cl_cl_path}", + "gcc": "%{clang_cl_cl_path}", + "gcov": "wrapper/bin/msvc_nop.bat", + "ld": "%{clang_cl_link_path}", + "nm": "wrapper/bin/msvc_nop.bat", + "objcopy": "wrapper/bin/msvc_nop.bat", + "objdump": "wrapper/bin/msvc_nop.bat", + "strip": "wrapper/bin/msvc_nop.bat", + }, + default_link_flags = ["/MACHINE:X64", "/DEFAULTLIB:clang_rt.builtins-x86_64.lib"], + dbg_mode_debug_flag = "%{clang_cl_dbg_mode_debug_flag}", + fastbuild_mode_debug_flag = "%{clang_cl_fastbuild_mode_debug_flag}", +) + +toolchain( + name = "cc-toolchain-x64_windows-clang-cl", + exec_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + "@rules_cc//cc/private/toolchain:clang-cl", + ], + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + ], + toolchain = ":cc-compiler-x64_windows-clang-cl", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +cc_toolchain( + name = "cc-compiler-armeabi-v7a", + toolchain_identifier = "stub_armeabi-v7a", + toolchain_config = ":stub_armeabi-v7a", + all_files = ":empty", + ar_files = ":empty", + as_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 1, +) + +armeabi_cc_toolchain_config(name = "stub_armeabi-v7a") + +toolchain( + name = "cc-toolchain-armeabi-v7a", + exec_compatible_with = [ + ], + target_compatible_with = [ + "@platforms//cpu:arm", + "@platforms//os:android", + ], + toolchain = ":cc-compiler-armeabi-v7a", + toolchain_type = "@rules_cc//cc:toolchain_type", +) + +filegroup( + name = "link_dynamic_library", + srcs = ["link_dynamic_library.sh"], +) diff --git a/cc/private/toolchain/armeabi_cc_toolchain_config.bzl b/cc/private/toolchain/armeabi_cc_toolchain_config.bzl new file mode 100644 index 0000000..3d2aa74 --- /dev/null +++ b/cc/private/toolchain/armeabi_cc_toolchain_config.bzl @@ -0,0 +1,82 @@ +# Copyright 2019 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. + +"""A Starlark cc_toolchain configuration rule""" + +load( + "@rules_cc//cc/private/toolchain:cc_toolchain_config_lib.bzl", + "feature", + "tool_path", +) + +def _impl(ctx): + toolchain_identifier = "stub_armeabi-v7a" + host_system_name = "armeabi-v7a" + target_system_name = "armeabi-v7a" + target_cpu = "armeabi-v7a" + target_libc = "armeabi-v7a" + compiler = "compiler" + abi_version = "armeabi-v7a" + abi_libc_version = "armeabi-v7a" + cc_target_os = None + builtin_sysroot = None + action_configs = [] + + supports_pic_feature = feature(name = "supports_pic", enabled = True) + supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True) + features = [supports_dynamic_linker_feature, supports_pic_feature] + + cxx_builtin_include_directories = [] + artifact_name_patterns = [] + make_variables = [] + + tool_paths = [ + 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"), + ] + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + action_configs = action_configs, + artifact_name_patterns = artifact_name_patterns, + cxx_builtin_include_directories = cxx_builtin_include_directories, + toolchain_identifier = toolchain_identifier, + host_system_name = host_system_name, + target_system_name = target_system_name, + target_cpu = target_cpu, + target_libc = target_libc, + compiler = compiler, + abi_version = abi_version, + abi_libc_version = abi_libc_version, + tool_paths = tool_paths, + make_variables = make_variables, + builtin_sysroot = builtin_sysroot, + cc_target_os = cc_target_os, + ) + +armeabi_cc_toolchain_config = rule( + implementation = _impl, + attrs = {}, + provides = [CcToolchainConfigInfo], +) diff --git a/cc/private/toolchain/build_interface_so b/cc/private/toolchain/build_interface_so new file mode 100644 index 0000000..626e707 --- /dev/null +++ b/cc/private/toolchain/build_interface_so @@ -0,0 +1,8 @@ +#!/bin/bash + +if [[ $# != 2 ]]; then + echo "Usage: $0 " 1>&2 + exit 1 +fi + +exec cp $1 $2 diff --git a/cc/private/toolchain/cc_configure.bzl b/cc/private/toolchain/cc_configure.bzl new file mode 100644 index 0000000..3ef32bf --- /dev/null +++ b/cc/private/toolchain/cc_configure.bzl @@ -0,0 +1,181 @@ +# 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).""" + +load("@rules_cc//cc/private/toolchain:windows_cc_configure.bzl", "configure_windows_toolchain") +load("@rules_cc//cc/private/toolchain:osx_cc_configure.bzl", "configure_osx_toolchain") +load("@rules_cc//cc/private/toolchain:unix_cc_configure.bzl", "configure_unix_toolchain") +load( + "@rules_cc//cc/private/toolchain:lib_cc_configure.bzl", + "get_cpu_value", + "resolve_labels", +) +load("@bazel_tools//tools/osx:xcode_configure.bzl", "run_xcode_locator") + +def _generate_cpp_only_build_file(repository_ctx, cpu_value, paths): + repository_ctx.template( + "BUILD", + paths["@rules_cc//cc/private/toolchain:BUILD.toolchains.tpl"], + {"%{name}": cpu_value}, + ) + +def cc_autoconf_toolchains_impl(repository_ctx): + """Generate BUILD file with 'toolchain' targets for the local host C++ toolchain. + + Args: + repository_ctx: repository context + """ + paths = resolve_labels(repository_ctx, [ + "@rules_cc//cc/private/toolchain:BUILD.toolchains.tpl", + "@bazel_tools//tools/osx/crosstool:BUILD.toolchains", + "@bazel_tools//tools/osx/crosstool:osx_archs.bzl", + "@bazel_tools//tools/osx:xcode_locator.m", + ]) + env = repository_ctx.os.environ + cpu_value = get_cpu_value(repository_ctx) + + # Should we try to find C++ toolchain at all? If not, we don't have to generate toolchains for C++ at all. + should_detect_cpp_toolchain = "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN" not in env or env["BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN"] != "1" + + # Should we unconditionally *not* use xcode? If so, we don't have to run Xcode locator ever. + should_use_cpp_only_toolchain = "BAZEL_USE_CPP_ONLY_TOOLCHAIN" in env and env["BAZEL_USE_CPP_ONLY_TOOLCHAIN"] == "1" + + # Should we unconditionally use xcode? If so, we don't have to run Xcode locator now. + should_use_xcode = "BAZEL_USE_XCODE_TOOLCHAIN" in env and env["BAZEL_USE_XCODE_TOOLCHAIN"] == "1" + + if not should_detect_cpp_toolchain: + repository_ctx.file("BUILD", "# C++ toolchain autoconfiguration was disabled by BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN env variable.") + elif cpu_value == "darwin" and not should_use_cpp_only_toolchain: + xcode_toolchains = [] + + # Only detect xcode if the user didn't tell us it will be there. + if not should_use_xcode: + # TODO(#6926): Unify C++ and ObjC toolchains so we don't have to run xcode locator to generate toolchain targets. + # And also so we don't have to keep this code in sync with @rules_cc//cc/private/toolchain:osx_cc_configure.bzl. + (xcode_toolchains, _xcodeloc_err) = run_xcode_locator( + repository_ctx, + paths["@bazel_tools//tools/osx:xcode_locator.m"], + ) + + if should_use_xcode or xcode_toolchains: + repository_ctx.symlink(paths["@bazel_tools//tools/osx/crosstool:BUILD.toolchains"], "BUILD") + repository_ctx.symlink(paths["@bazel_tools//tools/osx/crosstool:osx_archs.bzl"], "osx_archs.bzl") + else: + _generate_cpp_only_build_file(repository_ctx, cpu_value, paths) + else: + _generate_cpp_only_build_file(repository_ctx, cpu_value, paths) + +cc_autoconf_toolchains = repository_rule( + environ = [ + "BAZEL_USE_CPP_ONLY_TOOLCHAIN", + "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN", + ], + implementation = cc_autoconf_toolchains_impl, +) + +def cc_autoconf_impl(repository_ctx, overriden_tools = dict()): + """Generate BUILD file with 'cc_toolchain' targets for the local host C++ toolchain. + + Args: + repository_ctx: repository context + overriden_tools: dict of tool paths to use instead of autoconfigured tools + """ + + env = repository_ctx.os.environ + cpu_value = get_cpu_value(repository_ctx) + if "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN" in env and env["BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN"] == "1": + paths = resolve_labels(repository_ctx, [ + "@rules_cc//cc/private/toolchain:BUILD.empty", + "@rules_cc//cc/private/toolchain:empty_cc_toolchain_config.bzl", + ]) + repository_ctx.symlink(paths["@rules_cc//cc/private/toolchain:empty_cc_toolchain_config.bzl"], "cc_toolchain_config.bzl") + repository_ctx.symlink(paths("@rules_cc//cc/private/toolchain:BUILD.empty"), "BUILD") + elif cpu_value == "freebsd": + paths = resolve_labels(repository_ctx, [ + "@rules_cc//cc/private/toolchain:BUILD.static.freebsd", + "@rules_cc//cc/private/toolchain:freebsd_cc_toolchain_config.bzl", + ]) + + # This is defaulting to a static crosstool, we should eventually + # autoconfigure this platform too. Theorically, FreeBSD should be + # straightforward to add but we cannot run it in a docker container so + # skipping until we have proper tests for FreeBSD. + repository_ctx.symlink(paths["@rules_cc//cc/private/toolchain:freebsd_cc_toolchain_config.bzl"], "cc_toolchain_config.bzl") + repository_ctx.symlink(paths["@rules_cc//cc/private/toolchain:BUILD.static.freebsd"], "BUILD") + elif cpu_value == "x64_windows": + # TODO(ibiryukov): overriden_tools are only supported in configure_unix_toolchain. + # We might want to add that to Windows too(at least for msys toolchain). + configure_windows_toolchain(repository_ctx) + elif (cpu_value == "darwin" and + ("BAZEL_USE_CPP_ONLY_TOOLCHAIN" not in env or env["BAZEL_USE_CPP_ONLY_TOOLCHAIN"] != "1")): + configure_osx_toolchain(repository_ctx, overriden_tools) + else: + configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools) + +MSVC_ENVVARS = [ + "BAZEL_VC", + "BAZEL_VC_FULL_VERSION", + "BAZEL_VS", + "BAZEL_WINSDK_FULL_VERSION", + "VS90COMNTOOLS", + "VS100COMNTOOLS", + "VS110COMNTOOLS", + "VS120COMNTOOLS", + "VS140COMNTOOLS", + "VS150COMNTOOLS", + "VS160COMNTOOLS", + "TMP", + "TEMP", +] + +cc_autoconf = repository_rule( + environ = [ + "ABI_LIBC_VERSION", + "ABI_VERSION", + "BAZEL_COMPILER", + "BAZEL_HOST_SYSTEM", + "BAZEL_CXXOPTS", + "BAZEL_LINKOPTS", + "BAZEL_LINKLIBS", + "BAZEL_PYTHON", + "BAZEL_SH", + "BAZEL_TARGET_CPU", + "BAZEL_TARGET_LIBC", + "BAZEL_TARGET_SYSTEM", + "BAZEL_USE_CPP_ONLY_TOOLCHAIN", + "BAZEL_USE_XCODE_TOOLCHAIN", + "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN", + "BAZEL_USE_LLVM_NATIVE_COVERAGE", + "BAZEL_LLVM", + "USE_CLANG_CL", + "CC", + "CC_CONFIGURE_DEBUG", + "CC_TOOLCHAIN_NAME", + "CPLUS_INCLUDE_PATH", + "GCOV", + "HOMEBREW_RUBY_PATH", + "SYSTEMROOT", + ] + MSVC_ENVVARS, + implementation = cc_autoconf_impl, +) + +def cc_configure(): + """A C++ configuration rules that generate the crosstool file.""" + cc_autoconf_toolchains(name = "local_config_cc_toolchains") + cc_autoconf(name = "local_config_cc") + native.bind(name = "cc_toolchain", actual = "@local_config_cc//:toolchain") + native.register_toolchains( + # Use register_toolchain's target pattern expansion to register all toolchains in the package. + "@local_config_cc_toolchains//:all", + ) diff --git a/cc/private/toolchain/cc_toolchain_config.bzl b/cc/private/toolchain/cc_toolchain_config.bzl new file mode 100644 index 0000000..df36274 --- /dev/null +++ b/cc/private/toolchain/cc_toolchain_config.bzl @@ -0,0 +1,1493 @@ +# Copyright 2019 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. + +"""A Starlark cc_toolchain configuration rule""" + +load( + "@rules_cc//cc/private/toolchain:cc_toolchain_config_lib.bzl", + "action_config", + "artifact_name_pattern", + "env_entry", + "env_set", + "feature", + "feature_set", + "flag_group", + "flag_set", + "make_variable", + "tool", + "tool_path", + "variable_with_value", + "with_feature_set", +) +load( + "@rules_cc//cc:action_names.bzl", + _ASSEMBLE_ACTION_NAME = "ASSEMBLE_ACTION_NAME", + _CLIF_MATCH_ACTION_NAME = "CLIF_MATCH_ACTION_NAME", + _CPP_COMPILE_ACTION_NAME = "CPP_COMPILE_ACTION_NAME", + _CPP_HEADER_PARSING_ACTION_NAME = "CPP_HEADER_PARSING_ACTION_NAME", + _CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME = "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME", + _CPP_LINK_EXECUTABLE_ACTION_NAME = "CPP_LINK_EXECUTABLE_ACTION_NAME", + _CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME = "CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME", + _CPP_LINK_STATIC_LIBRARY_ACTION_NAME = "CPP_LINK_STATIC_LIBRARY_ACTION_NAME", + _CPP_MODULE_CODEGEN_ACTION_NAME = "CPP_MODULE_CODEGEN_ACTION_NAME", + _CPP_MODULE_COMPILE_ACTION_NAME = "CPP_MODULE_COMPILE_ACTION_NAME", + _C_COMPILE_ACTION_NAME = "C_COMPILE_ACTION_NAME", + _LINKSTAMP_COMPILE_ACTION_NAME = "LINKSTAMP_COMPILE_ACTION_NAME", + _LTO_BACKEND_ACTION_NAME = "LTO_BACKEND_ACTION_NAME", + _LTO_INDEXING_ACTION_NAME = "LTO_INDEXING_ACTION_NAME", + _PREPROCESS_ASSEMBLE_ACTION_NAME = "PREPROCESS_ASSEMBLE_ACTION_NAME", + _STRIP_ACTION_NAME = "STRIP_ACTION_NAME", +) + +all_compile_actions = [ + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, +] + +all_cpp_compile_actions = [ + _CPP_COMPILE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, +] + +preprocessor_compile_actions = [ + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, +] + +codegen_compile_actions = [ + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, +] + +all_link_actions = [ + _CPP_LINK_EXECUTABLE_ACTION_NAME, + _CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, + _CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME, +] + +def _impl(ctx): + if ctx.attr.disable_static_cc_toolchains: + fail("@rules_cc//cc/private/toolchain:default-toolchain, as well as the cc_toolchains it points " + + "to have been removed. See https://github.com/bazelbuild/bazel/issues/8546.") + + if (ctx.attr.cpu == "darwin"): + toolchain_identifier = "local_darwin" + elif (ctx.attr.cpu == "freebsd"): + toolchain_identifier = "local_freebsd" + elif (ctx.attr.cpu == "local"): + toolchain_identifier = "local_linux" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_clang"): + toolchain_identifier = "local_windows_clang" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_mingw"): + toolchain_identifier = "local_windows_mingw" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + toolchain_identifier = "local_windows_msys64" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64_mingw64"): + toolchain_identifier = "local_windows_msys64_mingw64" + elif (ctx.attr.cpu == "armeabi-v7a"): + toolchain_identifier = "stub_armeabi-v7a" + elif (ctx.attr.cpu == "x64_windows_msvc"): + toolchain_identifier = "vc_14_0_x64" + else: + fail("Unreachable") + + if (ctx.attr.cpu == "armeabi-v7a"): + host_system_name = "armeabi-v7a" + elif (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local" or + ctx.attr.cpu == "x64_windows" or + ctx.attr.cpu == "x64_windows_msvc"): + host_system_name = "local" + else: + fail("Unreachable") + + if (ctx.attr.cpu == "armeabi-v7a"): + target_system_name = "armeabi-v7a" + elif (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local" or + ctx.attr.cpu == "x64_windows" or + ctx.attr.cpu == "x64_windows_msvc"): + target_system_name = "local" + else: + fail("Unreachable") + + if (ctx.attr.cpu == "armeabi-v7a"): + target_cpu = "armeabi-v7a" + elif (ctx.attr.cpu == "darwin"): + target_cpu = "darwin" + elif (ctx.attr.cpu == "freebsd"): + target_cpu = "freebsd" + elif (ctx.attr.cpu == "local"): + target_cpu = "local" + elif (ctx.attr.cpu == "x64_windows"): + target_cpu = "x64_windows" + elif (ctx.attr.cpu == "x64_windows_msvc"): + target_cpu = "x64_windows_msvc" + else: + fail("Unreachable") + + if (ctx.attr.cpu == "armeabi-v7a"): + target_libc = "armeabi-v7a" + elif (ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local" or + ctx.attr.cpu == "x64_windows"): + target_libc = "local" + elif (ctx.attr.cpu == "darwin"): + target_libc = "macosx" + elif (ctx.attr.cpu == "x64_windows_msvc"): + target_libc = "msvcrt140" + else: + fail("Unreachable") + + if (ctx.attr.cpu == "x64_windows_msvc"): + compiler = "cl" + elif (ctx.attr.cpu == "armeabi-v7a" or + ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local"): + compiler = "compiler" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_clang"): + compiler = "windows_clang" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_mingw"): + compiler = "windows_mingw" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + compiler = "windows_msys64" + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64_mingw64"): + compiler = "windows_msys64_mingw64" + else: + fail("Unreachable") + + if (ctx.attr.cpu == "armeabi-v7a"): + abi_version = "armeabi-v7a" + elif (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local" or + ctx.attr.cpu == "x64_windows" or + ctx.attr.cpu == "x64_windows_msvc"): + abi_version = "local" + else: + fail("Unreachable") + + if (ctx.attr.cpu == "armeabi-v7a"): + abi_libc_version = "armeabi-v7a" + elif (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local" or + ctx.attr.cpu == "x64_windows" or + ctx.attr.cpu == "x64_windows_msvc"): + abi_libc_version = "local" + else: + fail("Unreachable") + + cc_target_os = None + + builtin_sysroot = None + + if (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local"): + objcopy_embed_data_action = action_config( + action_name = "objcopy_embed_data", + enabled = True, + tools = [tool(path = "/usr/bin/objcopy")], + ) + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_clang"): + objcopy_embed_data_action = action_config( + action_name = "objcopy_embed_data", + enabled = True, + tools = [tool(path = "C:/Program Files (x86)/LLVM/bin/objcopy")], + ) + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_mingw"): + objcopy_embed_data_action = action_config( + action_name = "objcopy_embed_data", + enabled = True, + tools = [tool(path = "C:/mingw/bin/objcopy")], + ) + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64_mingw64"): + objcopy_embed_data_action = action_config( + action_name = "objcopy_embed_data", + enabled = True, + tools = [tool(path = "C:/tools/msys64/mingw64/bin/objcopy")], + ) + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + objcopy_embed_data_action = action_config( + action_name = "objcopy_embed_data", + enabled = True, + tools = [tool(path = "C:/tools/msys64/usr/bin/objcopy")], + ) + + c_compile_action = action_config( + action_name = _C_COMPILE_ACTION_NAME, + implies = [ + "compiler_input_flags", + "compiler_output_flags", + "default_compile_flags", + "user_compile_flags", + "sysroot", + "unfiltered_compile_flags", + ], + tools = [tool(path = "wrapper/bin/msvc_cl.bat")], + ) + + cpp_compile_action = action_config( + action_name = _CPP_COMPILE_ACTION_NAME, + implies = [ + "compiler_input_flags", + "compiler_output_flags", + "default_compile_flags", + "user_compile_flags", + "sysroot", + "unfiltered_compile_flags", + ], + tools = [tool(path = "wrapper/bin/msvc_cl.bat")], + ) + + if (ctx.attr.cpu == "armeabi-v7a"): + action_configs = [] + elif (ctx.attr.cpu == "x64_windows_msvc"): + action_configs = [c_compile_action, cpp_compile_action] + elif (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local" or + ctx.attr.cpu == "x64_windows"): + action_configs = [objcopy_embed_data_action] + else: + fail("Unreachable") + + random_seed_feature = feature(name = "random_seed", enabled = True) + + compiler_output_flags_feature = feature( + name = "compiler_output_flags", + flag_sets = [ + flag_set( + actions = [_ASSEMBLE_ACTION_NAME], + flag_groups = [ + flag_group( + flags = ["/Fo%{output_file}", "/Zi"], + expand_if_available = "output_file", + expand_if_not_available = "output_assembly_file", + ), + ], + ), + flag_set( + actions = [ + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["/Fo%{output_file}"], + expand_if_available = "output_file", + expand_if_not_available = "output_assembly_file", + ), + flag_group( + flags = ["/Fa%{output_file}"], + expand_if_available = "output_file", + ), + flag_group( + flags = ["/P", "/Fi%{output_file}"], + expand_if_available = "output_file", + ), + ], + ), + ], + ) + + if (ctx.attr.cpu == "local"): + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = [ + "-lstdc++", + "-Wl,-z,relro,-z,now", + "-no-canonical-prefixes", + "-pass-exit-codes", + ], + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["-Wl,--gc-sections"])], + with_features = [with_feature_set(features = ["opt"])], + ), + ], + ) + elif (ctx.attr.cpu == "freebsd"): + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = [ + "-lstdc++", + "-Wl,-z,relro,-z,now", + "-no-canonical-prefixes", + ], + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["-Wl,--gc-sections"])], + with_features = [with_feature_set(features = ["opt"])], + ), + ], + ) + elif (ctx.attr.cpu == "darwin"): + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = [ + "-lstdc++", + "-undefined", + "dynamic_lookup", + "-headerpad_max_install_names", + "-no-canonical-prefixes", + ], + ), + ], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["-lstdc++"])], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows_msvc"): + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["-m64"])], + ), + ], + ) + + if (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd"): + unfiltered_compile_flags_feature = feature( + name = "unfiltered_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-no-canonical-prefixes", + "-Wno-builtin-macro-redefined", + "-D__DATE__=\"redacted\"", + "-D__TIMESTAMP__=\"redacted\"", + "-D__TIME__=\"redacted\"", + ], + ), + ], + ), + ], + ) + elif (ctx.attr.cpu == "local"): + unfiltered_compile_flags_feature = feature( + name = "unfiltered_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-no-canonical-prefixes", + "-fno-canonical-system-headers", + "-Wno-builtin-macro-redefined", + "-D__DATE__=\"redacted\"", + "-D__TIMESTAMP__=\"redacted\"", + "-D__TIME__=\"redacted\"", + ], + ), + ], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows_msvc"): + unfiltered_compile_flags_feature = feature( + name = "unfiltered_compile_flags", + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["%{unfiltered_compile_flags}"], + iterate_over = "unfiltered_compile_flags", + expand_if_available = "unfiltered_compile_flags", + ), + ], + ), + ], + ) + + supports_pic_feature = feature(name = "supports_pic", enabled = True) + + if (ctx.attr.cpu == "darwin"): + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-D_FORTIFY_SOURCE=1", + "-fstack-protector", + "-fcolor-diagnostics", + "-Wall", + "-Wthread-safety", + "-Wself-assign", + "-fno-omit-frame-pointer", + ], + ), + ], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-g"])], + with_features = [with_feature_set(features = ["dbg"])], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-g0", + "-O2", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + ], + ), + ], + with_features = [with_feature_set(features = ["opt"])], + ), + flag_set( + actions = [ + _LINKSTAMP_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-std=c++0x"])], + ), + ], + ) + elif (ctx.attr.cpu == "local"): + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-U_FORTIFY_SOURCE", + "-D_FORTIFY_SOURCE=1", + "-fstack-protector", + "-Wall", + "-Wunused-but-set-parameter", + "-Wno-free-nonheap-object", + "-fno-omit-frame-pointer", + ], + ), + ], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-g"])], + with_features = [with_feature_set(features = ["dbg"])], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-g0", + "-O2", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + ], + ), + ], + with_features = [with_feature_set(features = ["opt"])], + ), + flag_set( + actions = [ + _LINKSTAMP_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-std=c++0x"])], + ), + ], + ) + elif (ctx.attr.cpu == "freebsd"): + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-U_FORTIFY_SOURCE", + "-D_FORTIFY_SOURCE=1", + "-fstack-protector", + "-Wall", + "-fno-omit-frame-pointer", + ], + ), + ], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-g"])], + with_features = [with_feature_set(features = ["dbg"])], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-g0", + "-O2", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + ], + ), + ], + with_features = [with_feature_set(features = ["opt"])], + ), + flag_set( + actions = [ + _LINKSTAMP_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-std=c++0x"])], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows_msvc"): + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = [ + "-m64", + "/D__inline__=__inline", + "/DCOMPILER_MSVC", + "/DNOGDI", + "/DNOMINMAX", + "/DPRAGMA_SUPPORTED", + "/D_WIN32_WINNT=0x0601", + "/D_CRT_SECURE_NO_DEPRECATE", + "/D_CRT_SECURE_NO_WARNINGS", + "/D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS", + "/D_USE_MATH_DEFINES", + "/nologo", + "/bigobj", + "/Zm500", + "/J", + "/Gy", + "/GF", + "/W3", + "/EHsc", + "/wd4351", + "/wd4291", + "/wd4250", + "/wd4996", + ], + ), + ], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["/DDEBUG=1", "-g", "/Od", "-Xcompilation-mode=dbg"], + ), + ], + with_features = [with_feature_set(features = ["dbg"])], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["/DNDEBUG", "/Od", "-Xcompilation-mode=fastbuild"], + ), + ], + with_features = [with_feature_set(features = ["fastbuild"])], + ), + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["/DNDEBUG", "/O2", "-Xcompilation-mode=opt"], + ), + ], + with_features = [with_feature_set(features = ["opt"])], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_clang" or + ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_mingw" or + ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64_mingw64"): + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _LINKSTAMP_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-std=c++0x"])], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _LINKSTAMP_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [flag_group(flags = ["-std=gnu++0x"])], + ), + ], + ) + + opt_feature = feature(name = "opt") + + supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True) + + objcopy_embed_flags_feature = feature( + name = "objcopy_embed_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = ["objcopy_embed_data"], + flag_groups = [flag_group(flags = ["-I", "binary"])], + ), + ], + ) + + dbg_feature = feature(name = "dbg") + + if (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local"): + user_compile_flags_feature = feature( + name = "user_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows_msvc"): + user_compile_flags_feature = feature( + name = "user_compile_flags", + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + + if (ctx.attr.cpu == "darwin" or + ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local"): + sysroot_feature = feature( + name = "sysroot", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _LINKSTAMP_COMPILE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _LTO_BACKEND_ACTION_NAME, + _CLIF_MATCH_ACTION_NAME, + _CPP_LINK_EXECUTABLE_ACTION_NAME, + _CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, + _CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + expand_if_available = "sysroot", + ), + ], + ), + ], + ) + elif (ctx.attr.cpu == "x64_windows_msvc"): + sysroot_feature = feature( + name = "sysroot", + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + _CPP_LINK_EXECUTABLE_ACTION_NAME, + _CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, + _CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + iterate_over = "sysroot", + expand_if_available = "sysroot", + ), + ], + ), + ], + ) + + include_paths_feature = feature( + name = "include_paths", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["/I%{quote_include_paths}"], + iterate_over = "quote_include_paths", + ), + flag_group( + flags = ["/I%{include_paths}"], + iterate_over = "include_paths", + ), + flag_group( + flags = ["/I%{system_include_paths}"], + iterate_over = "system_include_paths", + ), + ], + ), + ], + ) + + dependency_file_feature = feature( + name = "dependency_file", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["/DEPENDENCY_FILE", "%{dependency_file}"], + expand_if_available = "dependency_file", + ), + ], + ), + ], + ) + + compiler_input_flags_feature = feature( + name = "compiler_input_flags", + flag_sets = [ + flag_set( + actions = [ + _ASSEMBLE_ACTION_NAME, + _PREPROCESS_ASSEMBLE_ACTION_NAME, + _C_COMPILE_ACTION_NAME, + _CPP_COMPILE_ACTION_NAME, + _CPP_HEADER_PARSING_ACTION_NAME, + _CPP_MODULE_COMPILE_ACTION_NAME, + _CPP_MODULE_CODEGEN_ACTION_NAME, + ], + flag_groups = [ + flag_group( + flags = ["/c", "%{source_file}"], + expand_if_available = "source_file", + ), + ], + ), + ], + ) + + fastbuild_feature = feature(name = "fastbuild") + + if (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + features = [ + default_compile_flags_feature, + default_link_flags_feature, + supports_dynamic_linker_feature, + objcopy_embed_flags_feature, + ] + elif (ctx.attr.cpu == "darwin"): + features = [ + default_compile_flags_feature, + default_link_flags_feature, + supports_dynamic_linker_feature, + supports_pic_feature, + objcopy_embed_flags_feature, + dbg_feature, + opt_feature, + user_compile_flags_feature, + sysroot_feature, + unfiltered_compile_flags_feature, + ] + elif (ctx.attr.cpu == "freebsd" or + ctx.attr.cpu == "local"): + features = [ + default_compile_flags_feature, + default_link_flags_feature, + supports_dynamic_linker_feature, + supports_pic_feature, + objcopy_embed_flags_feature, + opt_feature, + dbg_feature, + user_compile_flags_feature, + sysroot_feature, + unfiltered_compile_flags_feature, + ] + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_clang" or + ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_mingw" or + ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64_mingw64"): + features = [ + default_compile_flags_feature, + supports_dynamic_linker_feature, + objcopy_embed_flags_feature, + ] + elif (ctx.attr.cpu == "x64_windows_msvc"): + features = [ + default_link_flags_feature, + random_seed_feature, + default_compile_flags_feature, + include_paths_feature, + dependency_file_feature, + user_compile_flags_feature, + sysroot_feature, + unfiltered_compile_flags_feature, + compiler_output_flags_feature, + compiler_input_flags_feature, + dbg_feature, + fastbuild_feature, + opt_feature, + ] + elif (ctx.attr.cpu == "armeabi-v7a"): + features = [supports_dynamic_linker_feature, supports_pic_feature] + + if (ctx.attr.cpu == "armeabi-v7a"): + cxx_builtin_include_directories = [] + elif (ctx.attr.cpu == "darwin"): + cxx_builtin_include_directories = ["/"] + elif (ctx.attr.cpu == "freebsd"): + cxx_builtin_include_directories = ["/usr/lib/clang", "/usr/local/include", "/usr/include"] + elif (ctx.attr.cpu == "local" or + ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_clang"): + cxx_builtin_include_directories = ["/usr/lib/gcc/", "/usr/local/include", "/usr/include"] + elif (ctx.attr.cpu == "x64_windows_msvc"): + cxx_builtin_include_directories = [ + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/INCLUDE", + "C:/Program Files (x86)/Windows Kits/10/include/", + "C:/Program Files (x86)/Windows Kits/8.1/include/", + "C:/Program Files (x86)/GnuWin32/include/", + "C:/python_27_amd64/files/include", + ] + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_mingw"): + cxx_builtin_include_directories = ["C:/mingw/include", "C:/mingw/lib/gcc"] + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64_mingw64"): + cxx_builtin_include_directories = ["C:/tools/msys64/mingw64/x86_64-w64-mingw32/include"] + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + cxx_builtin_include_directories = ["C:/tools/msys64/", "/usr/"] + else: + fail("Unreachable") + + artifact_name_patterns = [] + + make_variables = [] + + if (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64_mingw64"): + tool_paths = [ + tool_path( + name = "ar", + path = "C:/tools/msys64/mingw64/bin/ar", + ), + tool_path( + name = "compat-ld", + path = "C:/tools/msys64/mingw64/bin/ld", + ), + tool_path( + name = "cpp", + path = "C:/tools/msys64/mingw64/bin/cpp", + ), + tool_path( + name = "dwp", + path = "C:/tools/msys64/mingw64/bin/dwp", + ), + tool_path( + name = "gcc", + path = "C:/tools/msys64/mingw64/bin/gcc", + ), + tool_path( + name = "gcov", + path = "C:/tools/msys64/mingw64/bin/gcov", + ), + tool_path( + name = "ld", + path = "C:/tools/msys64/mingw64/bin/ld", + ), + tool_path( + name = "nm", + path = "C:/tools/msys64/mingw64/bin/nm", + ), + tool_path( + name = "objcopy", + path = "C:/tools/msys64/mingw64/bin/objcopy", + ), + tool_path( + name = "objdump", + path = "C:/tools/msys64/mingw64/bin/objdump", + ), + tool_path( + name = "strip", + path = "C:/tools/msys64/mingw64/bin/strip", + ), + ] + elif (ctx.attr.cpu == "armeabi-v7a"): + tool_paths = [ + 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"), + ] + elif (ctx.attr.cpu == "freebsd"): + tool_paths = [ + tool_path(name = "ar", path = "/usr/bin/ar"), + 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 = "/usr/bin/clang"), + tool_path(name = "gcov", path = "/usr/bin/gcov"), + tool_path(name = "ld", path = "/usr/bin/ld"), + tool_path(name = "nm", path = "/usr/bin/nm"), + tool_path(name = "objcopy", path = "/usr/bin/objcopy"), + tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "strip", path = "/usr/bin/strip"), + ] + elif (ctx.attr.cpu == "local"): + tool_paths = [ + tool_path(name = "ar", path = "/usr/bin/ar"), + 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 = "/usr/bin/gcc"), + tool_path(name = "gcov", path = "/usr/bin/gcov"), + tool_path(name = "ld", path = "/usr/bin/ld"), + tool_path(name = "nm", path = "/usr/bin/nm"), + tool_path(name = "objcopy", path = "/usr/bin/objcopy"), + tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "strip", path = "/usr/bin/strip"), + ] + elif (ctx.attr.cpu == "darwin"): + tool_paths = [ + tool_path(name = "ar", path = "/usr/bin/libtool"), + 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_cc_wrapper.sh"), + tool_path(name = "gcov", path = "/usr/bin/gcov"), + tool_path(name = "ld", path = "/usr/bin/ld"), + tool_path(name = "nm", path = "/usr/bin/nm"), + tool_path(name = "objcopy", path = "/usr/bin/objcopy"), + tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "strip", path = "/usr/bin/strip"), + ] + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_clang"): + tool_paths = [ + tool_path(name = "ar", path = "C:/mingw/bin/ar"), + tool_path( + name = "compat-ld", + path = "C:/Program Files (x86)/LLVM/bin/ld", + ), + tool_path( + name = "cpp", + path = "C:/Program Files (x86)/LLVM/bin/cpp", + ), + tool_path( + name = "dwp", + path = "C:/Program Files (x86)/LLVM/bin/dwp", + ), + tool_path( + name = "gcc", + path = "C:/Program Files (x86)/LLVM/bin/clang", + ), + tool_path( + name = "gcov", + path = "C:/Program Files (x86)/LLVM/bin/gcov", + ), + tool_path( + name = "ld", + path = "C:/Program Files (x86)/LLVM/bin/ld", + ), + tool_path( + name = "nm", + path = "C:/Program Files (x86)/LLVM/bin/nm", + ), + tool_path( + name = "objcopy", + path = "C:/Program Files (x86)/LLVM/bin/objcopy", + ), + tool_path( + name = "objdump", + path = "C:/Program Files (x86)/LLVM/bin/objdump", + ), + tool_path( + name = "strip", + path = "C:/Program Files (x86)/LLVM/bin/strip", + ), + ] + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_mingw"): + tool_paths = [ + tool_path(name = "ar", path = "C:/mingw/bin/ar"), + tool_path(name = "compat-ld", path = "C:/mingw/bin/ld"), + tool_path(name = "cpp", path = "C:/mingw/bin/cpp"), + tool_path(name = "dwp", path = "C:/mingw/bin/dwp"), + tool_path(name = "gcc", path = "C:/mingw/bin/gcc"), + tool_path(name = "gcov", path = "C:/mingw/bin/gcov"), + tool_path(name = "ld", path = "C:/mingw/bin/ld"), + tool_path(name = "nm", path = "C:/mingw/bin/nm"), + tool_path(name = "objcopy", path = "C:/mingw/bin/objcopy"), + tool_path(name = "objdump", path = "C:/mingw/bin/objdump"), + tool_path(name = "strip", path = "C:/mingw/bin/strip"), + ] + elif (ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "windows_msys64"): + tool_paths = [ + tool_path(name = "ar", path = "C:/tools/msys64/usr/bin/ar"), + tool_path( + name = "compat-ld", + path = "C:/tools/msys64/usr/bin/ld", + ), + tool_path( + name = "cpp", + path = "C:/tools/msys64/usr/bin/cpp", + ), + tool_path( + name = "dwp", + path = "C:/tools/msys64/usr/bin/dwp", + ), + tool_path( + name = "gcc", + path = "C:/tools/msys64/usr/bin/gcc", + ), + tool_path( + name = "gcov", + path = "C:/tools/msys64/usr/bin/gcov", + ), + tool_path(name = "ld", path = "C:/tools/msys64/usr/bin/ld"), + tool_path(name = "nm", path = "C:/tools/msys64/usr/bin/nm"), + tool_path( + name = "objcopy", + path = "C:/tools/msys64/usr/bin/objcopy", + ), + tool_path( + name = "objdump", + path = "C:/tools/msys64/usr/bin/objdump", + ), + tool_path( + name = "strip", + path = "C:/tools/msys64/usr/bin/strip", + ), + ] + elif (ctx.attr.cpu == "x64_windows_msvc"): + tool_paths = [ + tool_path(name = "ar", path = "wrapper/bin/msvc_link.bat"), + tool_path(name = "cpp", path = "wrapper/bin/msvc_cl.bat"), + tool_path(name = "gcc", path = "wrapper/bin/msvc_cl.bat"), + tool_path(name = "gcov", path = "wrapper/bin/msvc_nop.bat"), + tool_path(name = "ld", path = "wrapper/bin/msvc_link.bat"), + tool_path(name = "nm", path = "wrapper/bin/msvc_nop.bat"), + tool_path( + name = "objcopy", + path = "wrapper/bin/msvc_nop.bat", + ), + tool_path( + name = "objdump", + path = "wrapper/bin/msvc_nop.bat", + ), + tool_path( + name = "strip", + path = "wrapper/bin/msvc_nop.bat", + ), + ] + else: + fail("Unreachable") + + out = ctx.actions.declare_file(ctx.label.name) + ctx.actions.write(out, "Fake executable") + return [ + cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + action_configs = action_configs, + artifact_name_patterns = artifact_name_patterns, + cxx_builtin_include_directories = cxx_builtin_include_directories, + toolchain_identifier = toolchain_identifier, + host_system_name = host_system_name, + target_system_name = target_system_name, + target_cpu = target_cpu, + target_libc = target_libc, + compiler = compiler, + abi_version = abi_version, + abi_libc_version = abi_libc_version, + tool_paths = tool_paths, + make_variables = make_variables, + builtin_sysroot = builtin_sysroot, + cc_target_os = cc_target_os, + ), + DefaultInfo( + executable = out, + ), + ] + +cc_toolchain_config = rule( + implementation = _impl, + attrs = { + "cpu": attr.string(mandatory = True), + "compiler": attr.string(), + "disable_static_cc_toolchains": attr.bool(), + }, + provides = [CcToolchainConfigInfo], + executable = True, +) diff --git a/cc/private/toolchain/cc_toolchain_config_lib.bzl b/cc/private/toolchain/cc_toolchain_config_lib.bzl new file mode 100644 index 0000000..48a855b --- /dev/null +++ b/cc/private/toolchain/cc_toolchain_config_lib.bzl @@ -0,0 +1,542 @@ +# Copyright 2018 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. + +""" A library of functions creating structs for CcToolchainConfigInfo.""" + +def _check_is_none_or_right_type(obj, obj_of_right_type, parameter_name, method_name): + if obj != None: + _check_right_type(obj, obj_of_right_type, parameter_name, method_name) + +def _check_right_type(obj, obj_of_right_type, parameter_name, method_name): + if type(obj) != type(obj_of_right_type): + fail("{} parameter of {} should be a {}, found {}." + .format(parameter_name, method_name, type(obj_of_right_type), type(obj))) + +def _check_is_nonempty_string(obj, parameter_name, method_name): + _check_right_type(obj, "", parameter_name, method_name) + if obj == "": + fail("{} parameter of {} must be a nonempty string." + .format(parameter_name, method_name)) + +def _check_is_nonempty_list(obj, parameter_name, method_name): + _check_right_type(obj, [], parameter_name, method_name) + if len(obj) == 0: + fail("{} parameter of {} must be a nonempty list." + .format(parameter_name, method_name)) + +EnvEntryInfo = provider(fields = ["key", "value", "type_name"]) + +def env_entry(key, value): + """ A key/value pair to be added as an environment variable. + + The returned EnvEntry provider finds its use in EnvSet creation through + the env_entries parameter of env_set(); EnvSet groups environment variables + that need to be expanded for specific actions. + The value of this pair is expanded in the same way as is described in + flag_group. The key remains an unexpanded string literal. + + Args: + key: a string literal representing the name of the variable. + value: the value to be expanded. + + Returns: + An EnvEntryInfo provider. + """ + _check_is_nonempty_string(key, "key", "env_entry") + _check_is_nonempty_string(value, "value", "env_entry") + return EnvEntryInfo(key = key, value = value, type_name = "env_entry") + +VariableWithValueInfo = provider(fields = ["name", "value", "type_name"]) + +def variable_with_value(name, value): + """ Represents equality check between a variable and a certain value. + + The returned provider finds its use through flag_group.expand_if_equal, + making the expansion of the flag_group conditional on the value of the + variable. + + Args: + name: name of the variable. + value: the value the variable should be compared against. + + Returns: + A VariableWithValueInfo provider. + """ + _check_is_nonempty_string(name, "name", "variable_with_value") + _check_is_nonempty_string(value, "value", "variable_with_value") + return VariableWithValueInfo( + name = name, + value = value, + type_name = "variable_with_value", + ) + +MakeVariableInfo = provider(fields = ["name", "value", "type_name"]) + +def make_variable(name, value): + """ A make variable that is made accessible to rules.""" + _check_is_nonempty_string(name, "name", "make_variable") + _check_is_nonempty_string(value, "value", "make_variable") + return MakeVariableInfo( + name = name, + value = value, + type_name = "make_variable", + ) + +FeatureSetInfo = provider(fields = ["features", "type_name"]) + +def feature_set(features = []): + """ A set of features. + + Used to support logical 'and' when specifying feature requirements in a + feature. + + Args: + features: A list of unordered feature names. + + Returns: + A FeatureSetInfo provider. + """ + _check_right_type(features, [], "features", "feature_set") + return FeatureSetInfo(features = features, type_name = "feature_set") + +WithFeatureSetInfo = provider(fields = ["features", "not_features", "type_name"]) + +def with_feature_set(features = [], not_features = []): + """ A set of positive and negative features. + + This stanza will evaluate to true when every 'feature' is enabled, and + every 'not_feature' is not enabled. + + Args: + features: A list of feature names that need to be enabled. + not_features: A list of feature names that need to not be enabled. + + Returns: + A WithFeatureSetInfo provider. + """ + _check_right_type(features, [], "features", "with_feature_set") + _check_right_type(not_features, [], "not_features", "with_feature_set") + return WithFeatureSetInfo( + features = features, + not_features = not_features, + type_name = "with_feature_set", + ) + +EnvSetInfo = provider(fields = ["actions", "env_entries", "with_features", "type_name"]) + +def env_set(actions, env_entries = [], with_features = []): + """ Groups a set of environment variables to apply for certain actions. + + EnvSet providers are passed to feature() and action_config(), to be applied to + the actions they are specified for. + + Args: + actions: A list of actions this env set applies to; each env set must + specify at least one action. + env_entries: A list of EnvEntry - the environment variables applied + via this env set. + with_features: A list of feature sets defining when this env set gets + applied. The env set will be applied when any one of the feature + sets evaluate to true. (That is, when when every 'feature' is + enabled, and every 'not_feature' is not enabled.) + If 'with_features' is omitted, the env set will be applied + unconditionally for every action specified. + + Returns: + An EnvSetInfo provider. + """ + _check_is_nonempty_list(actions, "actions", "env_set") + _check_right_type(env_entries, [], "env_entries", "env_set") + _check_right_type(with_features, [], "with_features", "env_set") + return EnvSetInfo( + actions = actions, + env_entries = env_entries, + with_features = with_features, + type_name = "env_set", + ) + +FlagGroupInfo = provider(fields = [ + "flags", + "flag_groups", + "iterate_over", + "expand_if_available", + "expand_if_not_available", + "expand_if_true", + "expand_if_false", + "expand_if_equal", + "type_name", +]) + +def flag_group( + flags = [], + flag_groups = [], + iterate_over = None, + expand_if_available = None, + expand_if_not_available = None, + expand_if_true = None, + expand_if_false = None, + expand_if_equal = None): + """ A group of flags. Supports parametrization via variable expansion. + + To expand a variable of list type, flag_group has to be annotated with + `iterate_over` message. Then all nested flags or flag_groups will be + expanded repeatedly for each element of the list. + For example: + flag_group( + iterate_over = 'include_path', + flags = ['-I', '%{include_path}'], + ) + ... will get expanded to -I /to/path1 -I /to/path2 ... for each + include_path /to/pathN. + + To expand a variable of structure type, use dot-notation, e.g.: + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link.libraries", + flags = ["-L%{libraries_to_link.libraries.directory}"], + ) + ] + ) + + Flag groups can be nested; if they are, the flag group must only contain + other flag groups (no flags) so the order is unambiguously specified. + In order to expand a variable of nested lists, 'iterate_over' can be used. + For example: + flag_group ( + iterate_over = 'object_files', + flag_groups = [ + flag_group ( + flags = ['--start-lib'], + ), + flag_group ( + iterate_over = 'object_files', + flags = ['%{object_files}'], + ), + flag_group ( + flags = ['--end-lib'], + ) + ] + ) + ... will get expanded to + --start-lib a1.o a2.o ... --end-lib --start-lib b1.o b2.o .. --end-lib + with %{object_files} being a variable of nested list type + [['a1.o', 'a2.o', ...], ['b1.o', 'b2.o', ...], ...]. + + Args: + flags: a string list, representing flags. Only one of flags and + flag_groups can be set, as to avoid ambiguity. + flag_groups: a list of FlagGroup entries. Only one of flags and + flag_groups can be set, as to avoid ambiguity. + iterate_over: a string, representing a variable of list type. + expand_if_available: A build variable that needs to be available + in order to expand the flag_group. + expand_if_not_available: A build variable that needs to be + unavailable in order for this flag_group to be expanded. + expand_if_true: if set, this variable needs to evaluate to True in + order for the flag_group to be expanded. + expand_if_false: if set, this variable needs to evalate to False in + order for the flag_group to be expanded. + expand_if_equal: a VariableWithValue, the flag_group is expanded in + case of equality. + + Returns: + A FlagGroupInfo provider. + """ + + _check_right_type(flags, [], "flags", "flag_group") + _check_right_type(flag_groups, [], "flag_groups", "flag_group") + if len(flags) > 0 and len(flag_groups) > 0: + fail("flag_group must not contain both a flag and another flag_group.") + if len(flags) == 0 and len(flag_groups) == 0: + fail("flag_group must contain either a list of flags or a list of flag_groups.") + _check_is_none_or_right_type(expand_if_true, "string", "expand_if_true", "flag_group") + _check_is_none_or_right_type(expand_if_false, "string", "expand_if_false", "flag_group") + _check_is_none_or_right_type(expand_if_available, "string", "expand_if_available", "flag_group") + _check_is_none_or_right_type( + expand_if_not_available, + "string", + "expand_if_not_available", + "flag_group", + ) + _check_is_none_or_right_type(iterate_over, "string", "iterate_over", "flag_group") + + return FlagGroupInfo( + flags = flags, + flag_groups = flag_groups, + iterate_over = iterate_over, + expand_if_available = expand_if_available, + expand_if_not_available = expand_if_not_available, + expand_if_true = expand_if_true, + expand_if_false = expand_if_false, + expand_if_equal = expand_if_equal, + type_name = "flag_group", + ) + +FlagSetInfo = provider(fields = [ + "actions", + "with_features", + "flag_groups", + "type_name", +]) + +def flag_set( + actions = [], + with_features = [], + flag_groups = []): + """ A set of flags to be expanded in the command line for specific actions. + + Args: + actions: The actions this flag set applies to; each flag set must + specify at least one action. + with_features: A list of feature sets defining when this flag set gets + applied. The flag set will be applied when any one of the feature + sets evaluate to true. (That is, when when every 'feature' is + enabled, and every 'not_feature' is not enabled.) + If 'with_feature' is omitted, the flag set will be applied + unconditionally for every action specified. + flag_groups: A FlagGroup list - the flags applied via this flag set. + + Returns: + A FlagSetInfo provider. + """ + _check_right_type(actions, [], "actions", "flag_set") + _check_right_type(with_features, [], "with_features", "flag_set") + _check_right_type(flag_groups, [], "flag_groups", "flag_set") + return FlagSetInfo( + actions = actions, + with_features = with_features, + flag_groups = flag_groups, + type_name = "flag_set", + ) + +FeatureInfo = provider(fields = [ + "name", + "enabled", + "flag_sets", + "env_sets", + "requires", + "implies", + "provides", + "type_name", +]) + +def feature( + name, + enabled = False, + flag_sets = [], + env_sets = [], + requires = [], + implies = [], + provides = []): + """ Contains all flag specifications for one feature. + + Args: + name: The feature's name. It is possible to introduce a feature without + a change to Bazel by adding a 'feature' section to the toolchain + and adding the corresponding string as feature in the BUILD file. + enabled: If 'True', this feature is enabled unless a rule type + explicitly marks it as unsupported. + flag_sets: A FlagSet list - If the given feature is enabled, the flag + sets will be applied for the actions are specified for. + env_sets: an EnvSet list - If the given feature is enabled, the env + sets will be applied for the actions they are specified for. + requires: A list of feature sets defining when this feature is + supported by the toolchain. The feature is supported if any of the + feature sets fully apply, that is, when all features of a feature + set are enabled. + If 'requires' is omitted, the feature is supported independently of + which other features are enabled. + Use this for example to filter flags depending on the build mode + enabled (opt / fastbuild / dbg). + implies: A string list of features or action configs that are + automatically enabled when this feature is enabled. If any of the + implied features or action configs cannot be enabled, this feature + will (silently) not be enabled either. + provides: A list of names this feature conflicts with. + A feature cannot be enabled if: + - 'provides' contains the name of a different feature or action + config that we want to enable. + - 'provides' contains the same value as a 'provides' in a + different feature or action config that we want to enable. + Use this in order to ensure that incompatible features cannot be + accidentally activated at the same time, leading to hard to + diagnose compiler errors. + + Returns: + A FeatureInfo provider. + """ + _check_right_type(enabled, True, "enabled", "feature") + _check_right_type(flag_sets, [], "flag_sets", "feature") + _check_right_type(env_sets, [], "env_sets", "feature") + _check_right_type(requires, [], "requires", "feature") + _check_right_type(provides, [], "provides", "feature") + _check_right_type(implies, [], "implies", "feature") + return FeatureInfo( + name = name, + enabled = enabled, + flag_sets = flag_sets, + env_sets = env_sets, + requires = requires, + implies = implies, + provides = provides, + type_name = "feature", + ) + +ToolPathInfo = provider(fields = ["name", "path", "type_name"]) + +def tool_path(name, path): + """ Tool locations. + + Args: + name: Name of the tool. + path: Location of the tool; Can be absolute path (in case of non hermetic + toolchain), or path relative to the cc_toolchain's package. + + Returns: + A ToolPathInfo provider. + + Deprecated: + Prefer specifying an ActionConfig for the action that needs the tool. + TODO(b/27903698) migrate to ActionConfig. + """ + _check_is_nonempty_string(name, "name", "tool_path") + _check_is_nonempty_string(path, "path", "tool_path") + return ToolPathInfo(name = name, path = path, type_name = "tool_path") + +ToolInfo = provider(fields = ["path", "with_features", "execution_requirements", "type_name"]) + +def tool(path, with_features = [], execution_requirements = []): + """ Describes a tool associated with a crosstool action config. + + Args: + path: Location of the tool; Can be absolute path (in case of non hermetic + toolchain), or path relative to the cc_toolchain's package. + with_features: A list of feature sets defining when this tool is + applicable. The tool will used when any one of the feature sets + evaluate to true. (That is, when when every 'feature' is enabled, + and every 'not_feature' is not enabled.) + If 'with_feature' is omitted, the tool will apply for any feature + configuration. + execution_requirements: Requirements on the execution environment for + the execution of this tool, to be passed as out-of-band "hints" to + the execution backend. + Ex. "requires-darwin" + + Returns: + A ToolInfo provider. + """ + _check_is_nonempty_string(path, "path", "tool") + _check_right_type(with_features, [], "with_features", "tool") + _check_right_type(execution_requirements, [], "execution_requirements", "tool") + return ToolInfo( + path = path, + with_features = with_features, + execution_requirements = execution_requirements, + type_name = "tool", + ) + +ActionConfigInfo = provider(fields = [ + "config_name", + "action_name", + "enabled", + "tools", + "flag_sets", + "implies", + "type_name", +]) + +def action_config( + action_name, + enabled = False, + tools = [], + flag_sets = [], + implies = []): + """ Configuration of a Bazel action. + + An action config corresponds to a Bazel action, and allows selection of + a tool based on activated features. + Action config activation occurs by the same semantics as features: a + feature can 'require' or 'imply' an action config in the same way that it + would another feature. + + Args: + action_name: The name of the Bazel action that this config applies to, + ex. 'c-compile' or 'c-module-compile'. + enabled: If 'True', this action is enabled unless a rule type + explicitly marks it as unsupported. + tools: The tool applied to the action will be the first Tool with a + feature set that matches the feature configuration. An error will + be thrown if no tool matches a provided feature configuration - for + that reason, it's a good idea to provide a default tool with an + empty feature set. + flag_sets: If the given action config is enabled, the flag sets will be + applied to the corresponding action. + implies: A list of features or action configs that are automatically + enabled when this action config is enabled. If any of the implied + features or action configs cannot be enabled, this action config + will (silently) not be enabled either. + + Returns: + An ActionConfigInfo provider. + """ + _check_is_nonempty_string(action_name, "name", "action_config") + _check_right_type(enabled, True, "enabled", "action_config") + _check_right_type(tools, [], "tools", "action_config") + _check_right_type(flag_sets, [], "flag_sets", "action_config") + _check_right_type(implies, [], "implies", "action_config") + return ActionConfigInfo( + action_name = action_name, + enabled = enabled, + tools = tools, + flag_sets = flag_sets, + implies = implies, + type_name = "action_config", + ) + +ArtifactNamePatternInfo = provider(fields = [ + "category_name", + "prefix", + "extension", + "type_name", +]) + +def artifact_name_pattern(category_name, prefix, extension): + """ The name for an artifact of a given category of input or output artifacts to an action. + + Args: + category_name: The category of artifacts that this selection applies + to. This field is compared against a list of categories defined + in bazel. Example categories include "linked_output" or + "debug_symbols". An error is thrown if no category is matched. + prefix: The prefix for creating the artifact for this selection. + Together with the extension it is used to create an artifact name + based on the target name. + extension: The extension for creating the artifact for this selection. + Together with the prefix it is used to create an artifact name + based on the target name. + + Returns: + An ArtifactNamePatternInfo provider + """ + _check_is_nonempty_string(category_name, "category_name", "artifact_name_pattern") + _check_is_none_or_right_type(prefix, "", "prefix", "artifact_name_pattern") + _check_is_none_or_right_type(extension, "", "extension", "artifact_name_pattern") + return ArtifactNamePatternInfo( + category_name = category_name, + prefix = prefix, + extension = extension, + type_name = "artifact_name_pattern", + ) diff --git a/cc/private/toolchain/clang_installation_error.bat.tpl b/cc/private/toolchain/clang_installation_error.bat.tpl new file mode 100644 index 0000000..bec44eb --- /dev/null +++ b/cc/private/toolchain/clang_installation_error.bat.tpl @@ -0,0 +1,24 @@ +:: Copyright 2019 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. + +@echo OFF + +echo. 1>&2 +echo The target you are compiling requires the Clang compiler. 1>&2 +echo Bazel couldn't find a valid Clang installation on your machine. 1>&2 +%{clang_error_message} +echo Please check your installation following https://docs.bazel.build/versions/master/windows.html#using 1>&2 +echo. 1>&2 + +exit /b 1 diff --git a/cc/private/toolchain/empty.cc b/cc/private/toolchain/empty.cc new file mode 100644 index 0000000..4cda5c6 --- /dev/null +++ b/cc/private/toolchain/empty.cc @@ -0,0 +1,15 @@ +// Copyright 2017 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. + +int main() {} diff --git a/cc/private/toolchain/empty_cc_toolchain_config.bzl b/cc/private/toolchain/empty_cc_toolchain_config.bzl new file mode 100644 index 0000000..5d42d06 --- /dev/null +++ b/cc/private/toolchain/empty_cc_toolchain_config.bzl @@ -0,0 +1,42 @@ +# Copyright 2019 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. + +"""A fake C++ toolchain configuration rule""" + +def _impl(ctx): + out = ctx.actions.declare_file(ctx.label.name) + ctx.actions.write(out, "Fake executable") + return [ + cc_common.create_cc_toolchain_config_info( + ctx = ctx, + toolchain_identifier = "local_linux", + host_system_name = "local", + target_system_name = "local", + target_cpu = "local", + target_libc = "local", + compiler = "compiler", + abi_version = "local", + abi_libc_version = "local", + ), + DefaultInfo( + executable = out, + ), + ] + +cc_toolchain_config = rule( + implementation = _impl, + attrs = {}, + provides = [CcToolchainConfigInfo], + executable = True, +) diff --git a/cc/private/toolchain/freebsd_cc_toolchain_config.bzl b/cc/private/toolchain/freebsd_cc_toolchain_config.bzl new file mode 100644 index 0000000..4b55cbb --- /dev/null +++ b/cc/private/toolchain/freebsd_cc_toolchain_config.bzl @@ -0,0 +1,307 @@ +# Copyright 2019 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. + +"""A Starlark cc_toolchain configuration rule for freebsd.""" + +load( + "@rules_cc//cc/private/toolchain:cc_toolchain_config_lib.bzl", + "action_config", + "feature", + "flag_group", + "flag_set", + "tool", + "tool_path", + "with_feature_set", +) +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") + +all_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ACTION_NAMES.lto_backend, +] + +all_cpp_compile_actions = [ + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, +] + +all_link_actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, +] + +def _impl(ctx): + cpu = ctx.attr.cpu + compiler = "compiler" + toolchain_identifier = "local_freebsd" if cpu == "freebsd" else "stub_armeabi-v7a" + host_system_name = "local" if cpu == "freebsd" else "armeabi-v7a" + target_system_name = "local" if cpu == "freebsd" else "armeabi-v7a" + target_libc = "local" if cpu == "freebsd" else "armeabi-v7a" + abi_version = "local" if cpu == "freebsd" else "armeabi-v7a" + abi_libc_version = "local" if cpu == "freebsd" else "armeabi-v7a" + + objcopy_embed_data_action = action_config( + action_name = "objcopy_embed_data", + enabled = True, + tools = [tool(path = "/usr/bin/objcopy")], + ) + + action_configs = [objcopy_embed_data_action] if cpu == "freebsd" else [] + + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = [ + "-lstdc++", + "-Wl,-z,relro,-z,now", + "-no-canonical-prefixes", + ], + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["-Wl,--gc-sections"])], + with_features = [with_feature_set(features = ["opt"])], + ), + ], + ) + + unfiltered_compile_flags_feature = feature( + name = "unfiltered_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = [ + "-no-canonical-prefixes", + "-Wno-builtin-macro-redefined", + "-D__DATE__=\"redacted\"", + "-D__TIMESTAMP__=\"redacted\"", + "-D__TIME__=\"redacted\"", + ], + ), + ], + ), + ], + ) + + supports_pic_feature = feature(name = "supports_pic", enabled = True) + + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = [ + "-U_FORTIFY_SOURCE", + "-D_FORTIFY_SOURCE=1", + "-fstack-protector", + "-Wall", + "-fno-omit-frame-pointer", + ], + ), + ], + ), + flag_set( + actions = all_compile_actions, + flag_groups = [flag_group(flags = ["-g"])], + with_features = [with_feature_set(features = ["dbg"])], + ), + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = [ + "-g0", + "-O2", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + ], + ), + ], + with_features = [with_feature_set(features = ["opt"])], + ), + flag_set( + actions = all_cpp_compile_actions + [ACTION_NAMES.lto_backend], + flag_groups = [flag_group(flags = ["-std=c++0x"])], + ), + ], + ) + + opt_feature = feature(name = "opt") + + supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True) + + objcopy_embed_flags_feature = feature( + name = "objcopy_embed_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = ["objcopy_embed_data"], + flag_groups = [flag_group(flags = ["-I", "binary"])], + ), + ], + ) + + dbg_feature = feature(name = "dbg") + + user_compile_flags_feature = feature( + name = "user_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + + sysroot_feature = feature( + name = "sysroot", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ACTION_NAMES.lto_backend, + ] + all_link_actions, + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + expand_if_available = "sysroot", + ), + ], + ), + ], + ) + + if cpu == "freebsd": + features = [ + default_compile_flags_feature, + default_link_flags_feature, + supports_dynamic_linker_feature, + supports_pic_feature, + objcopy_embed_flags_feature, + opt_feature, + dbg_feature, + user_compile_flags_feature, + sysroot_feature, + unfiltered_compile_flags_feature, + ] + else: + features = [supports_dynamic_linker_feature, supports_pic_feature] + + if (cpu == "freebsd"): + cxx_builtin_include_directories = ["/usr/lib/clang", "/usr/local/include", "/usr/include"] + else: + cxx_builtin_include_directories = [] + + if cpu == "freebsd": + tool_paths = [ + tool_path(name = "ar", path = "/usr/bin/ar"), + 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 = "/usr/bin/clang"), + tool_path(name = "gcov", path = "/usr/bin/gcov"), + tool_path(name = "ld", path = "/usr/bin/ld"), + tool_path(name = "nm", path = "/usr/bin/nm"), + tool_path(name = "objcopy", path = "/usr/bin/objcopy"), + tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "strip", path = "/usr/bin/strip"), + ] + else: + tool_paths = [ + 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"), + ] + + out = ctx.actions.declare_file(ctx.label.name) + ctx.actions.write(out, "Fake executable") + return [ + cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + action_configs = action_configs, + cxx_builtin_include_directories = cxx_builtin_include_directories, + toolchain_identifier = toolchain_identifier, + host_system_name = host_system_name, + target_system_name = target_system_name, + target_cpu = cpu, + target_libc = target_libc, + compiler = compiler, + abi_version = abi_version, + abi_libc_version = abi_libc_version, + tool_paths = tool_paths, + ), + DefaultInfo( + executable = out, + ), + ] + +cc_toolchain_config = rule( + implementation = _impl, + attrs = { + "cpu": attr.string(mandatory = True), + }, + provides = [CcToolchainConfigInfo], + executable = True, +) diff --git a/cc/private/toolchain/grep-includes.sh b/cc/private/toolchain/grep-includes.sh new file mode 100755 index 0000000..ee51361 --- /dev/null +++ b/cc/private/toolchain/grep-includes.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright 2018 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. +# +# TODO(bazel-team): Support include scanning and grep-includes in Bazel +echo "grep-includes is not supported by Bazel" +exit 1 diff --git a/cc/private/toolchain/lib_cc_configure.bzl b/cc/private/toolchain/lib_cc_configure.bzl new file mode 100644 index 0000000..789018f --- /dev/null +++ b/cc/private/toolchain/lib_cc_configure.bzl @@ -0,0 +1,219 @@ +# 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. +"""Base library for configuring the C++ toolchain.""" + +def resolve_labels(repository_ctx, labels): + """Resolves a collection of labels to their paths. + + Label resolution can cause the evaluation of Skylark functions to restart. + For functions with side-effects (like the auto-configuration functions, which + inspect the system and touch the file system), such restarts are costly. + We cannot avoid the restarts, but we can minimize their penalty by resolving + all labels upfront. + + Among other things, doing less work on restarts can cut analysis times by + several seconds and may also prevent tickling kernel conditions that cause + build failures. See https://github.com/bazelbuild/bazel/issues/5196 for + more details. + + Args: + repository_ctx: The context with which to resolve the labels. + labels: Labels to be resolved expressed as a list of strings. + + Returns: + A dictionary with the labels as keys and their paths as values. + """ + return dict([(label, repository_ctx.path(Label(label))) for label in labels]) + +def escape_string(arg): + """Escape percent sign (%) in the string so it can appear in the Crosstool.""" + if arg != None: + return str(arg).replace("%", "%%") + else: + return None + +def split_escaped(string, delimiter): + """Split string on the delimiter unless %-escaped. + + Examples: + Basic usage: + split_escaped("a:b:c", ":") -> [ "a", "b", "c" ] + + Delimeter that is not supposed to be splitten on has to be %-escaped: + split_escaped("a%:b", ":") -> [ "a:b" ] + + Literal % can be represented by escaping it as %%: + split_escaped("a%%b", ":") -> [ "a%b" ] + + Consecutive delimiters produce empty strings: + split_escaped("a::b", ":") -> [ "a", "", "", "b" ] + + Args: + string: The string to be split. + delimiter: Non-empty string not containing %-sign to be used as a + delimiter. + + Returns: + A list of substrings. + """ + if delimiter == "": + fail("Delimiter cannot be empty") + if delimiter.find("%") != -1: + fail("Delimiter cannot contain %-sign") + + i = 0 + result = [] + accumulator = [] + length = len(string) + delimiter_length = len(delimiter) + + if not string: + return [] + + # Iterate over the length of string since Skylark doesn't have while loops + for _ in range(length): + if i >= length: + break + if i + 2 <= length and string[i:i + 2] == "%%": + accumulator.append("%") + i += 2 + elif (i + 1 + delimiter_length <= length and + string[i:i + 1 + delimiter_length] == "%" + delimiter): + accumulator.append(delimiter) + i += 1 + delimiter_length + elif i + delimiter_length <= length and string[i:i + delimiter_length] == delimiter: + result.append("".join(accumulator)) + accumulator = [] + i += delimiter_length + else: + accumulator.append(string[i]) + i += 1 + + # Append the last group still in accumulator + result.append("".join(accumulator)) + return result + +def auto_configure_fail(msg): + """Output failure message when auto configuration fails.""" + red = "\033[0;31m" + no_color = "\033[0m" + fail("\n%sAuto-Configuration Error:%s %s\n" % (red, no_color, msg)) + +def auto_configure_warning(msg): + """Output warning message during auto configuration.""" + yellow = "\033[1;33m" + no_color = "\033[0m" + print("\n%sAuto-Configuration Warning:%s %s\n" % (yellow, no_color, msg)) + +def get_env_var(repository_ctx, name, default = None, enable_warning = True): + """Find an environment variable in system path. Doesn't %-escape the value!""" + if name in repository_ctx.os.environ: + return repository_ctx.os.environ[name] + if default != None: + if enable_warning: + auto_configure_warning("'%s' environment variable is not set, using '%s' as default" % (name, default)) + return default + auto_configure_fail("'%s' environment variable is not set" % name) + +def which(repository_ctx, cmd, default = None): + """A wrapper around repository_ctx.which() to provide a fallback value. Doesn't %-escape the value!""" + result = repository_ctx.which(cmd) + return default if result == None else str(result) + +def which_cmd(repository_ctx, cmd, default = None): + """Find cmd in PATH using repository_ctx.which() and fail if cannot find it. Doesn't %-escape the cmd!""" + result = repository_ctx.which(cmd) + if result != None: + return str(result) + path = get_env_var(repository_ctx, "PATH") + if default != None: + auto_configure_warning("Cannot find %s in PATH, using '%s' as default.\nPATH=%s" % (cmd, default, path)) + return default + auto_configure_fail("Cannot find %s in PATH, please make sure %s is installed and add its directory in PATH.\nPATH=%s" % (cmd, cmd, path)) + return str(result) + +def execute( + repository_ctx, + command, + environment = None, + expect_failure = False): + """Execute a command, return stdout if succeed and throw an error if it fails. Doesn't %-escape the result!""" + if environment: + result = repository_ctx.execute(command, environment = environment) + else: + result = repository_ctx.execute(command) + if expect_failure != (result.return_code != 0): + if expect_failure: + auto_configure_fail( + "expected failure, command %s, stderr: (%s)" % ( + command, + result.stderr, + ), + ) + else: + auto_configure_fail( + "non-zero exit code: %d, command %s, stderr: (%s)" % ( + result.return_code, + command, + result.stderr, + ), + ) + stripped_stdout = result.stdout.strip() + if not stripped_stdout: + auto_configure_fail( + "empty output from command %s, stderr: (%s)" % (command, result.stderr), + ) + return stripped_stdout + +def get_cpu_value(repository_ctx): + """Compute the cpu_value based on the OS name. Doesn't %-escape the result!""" + os_name = repository_ctx.os.name.lower() + if os_name.startswith("mac os"): + return "darwin" + if os_name.find("freebsd") != -1: + return "freebsd" + if os_name.find("windows") != -1: + return "x64_windows" + + # Use uname to figure out whether we are on x86_32 or x86_64 + result = repository_ctx.execute(["uname", "-m"]) + if result.stdout.strip() in ["power", "ppc64le", "ppc", "ppc64"]: + return "ppc" + if result.stdout.strip() in ["s390x"]: + return "s390x" + if result.stdout.strip() in ["arm", "armv7l"]: + return "arm" + if result.stdout.strip() in ["aarch64"]: + return "aarch64" + return "k8" if result.stdout.strip() in ["amd64", "x86_64", "x64"] else "piii" + +def is_cc_configure_debug(repository_ctx): + """Returns True if CC_CONFIGURE_DEBUG is set to 1.""" + env = repository_ctx.os.environ + return "CC_CONFIGURE_DEBUG" in env and env["CC_CONFIGURE_DEBUG"] == "1" + +def build_flags(flags): + """Convert `flags` to a string of flag fields.""" + return "\n".join([" flag: '" + flag + "'" for flag in flags]) + +def get_starlark_list(values): + if not values: + return "" + return "\"" + "\",\n \"".join(values) + "\"" + +def auto_configure_warning_maybe(repository_ctx, msg): + """Output warning message when CC_CONFIGURE_DEBUG is enabled.""" + if is_cc_configure_debug(repository_ctx): + auto_configure_warning(msg) diff --git a/cc/private/toolchain/link_dynamic_library.sh b/cc/private/toolchain/link_dynamic_library.sh new file mode 100755 index 0000000..c71d498 --- /dev/null +++ b/cc/private/toolchain/link_dynamic_library.sh @@ -0,0 +1,113 @@ +#!/bin/bash +# +# 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. + +# This script handles interface library generation for dynamic library +# link action. +# +# Bazel can be configured to generate external interface library script +# to generate interface libraries in CppLinkAction for dynamic libraries. +# This is not needed on Windows (as the "interface" libraries are +# generated by default). This script therefore handles the cases when +# external script is provided, or when no script should be used. + +set -eu + +E_LINKER_COMMAND_NOT_FOUND=12 +E_INTERFACE_BUILDER_NOT_FOUND=13 + + +SUFFIX=".rewritten" + +other_args="" + +if [[ "$#" -eq 1 ]]; then + if [[ "$1" != @* ]]; then + echo "Parameter file must start with @" 1>&2; + exit "$E_LINKER_COMMAND_NOT_FOUND" + fi + + filename=$(echo "$1" | cut -c2-) + first_five_lines=$(head -n 5 $filename) + + # Should generate interface library switch (); if the value is "no", + # following 3 args are ignored (but must be present) + GENERATE_INTERFACE_LIBRARY=$(echo "$first_five_lines" | head -n1 | tail -n1) + # Tool which can generate interface library from dynamic library file + INTERFACE_LIBRARY_BUILDER=$(echo "$first_five_lines" | head -n2 | tail -n1) + # Dynamic library from which we want to generate interface library + DYNAMIC_LIBRARY=$(echo "$first_five_lines" | head -n3 | tail -n1) + # Resulting interface library + INTERFACE_LIBRARY=$(echo "$first_five_lines" | head -n4 | tail -n1) + # The command used to generate the dynamic library + LINKER_COMMAND=$(echo "$first_five_lines" | head -n5 | tail -n1) + + rest_of_lines=$(tail -n +6 $filename) + new_param_file="${filename}${SUFFIX}" + echo "$rest_of_lines" > $new_param_file + other_args="@$new_param_file" + + if [[ ! -e "$LINKER_COMMAND" ]]; then + echo "Linker command ($LINKER_COMMAND) not found." 1>&2; + exit "$E_LINKER_COMMAND_NOT_FOUND" + fi + + if [[ "no" == "$GENERATE_INTERFACE_LIBRARY" ]]; then + INTERFACE_GENERATION=: + else + if [[ ! -e "$INTERFACE_LIBRARY_BUILDER" ]]; then + echo "Interface library builder ($INTERFACE_LIBRARY_BUILDER) + not found." 1>&2; + exit "$E_INTERFACE_BUILDER_NOT_FOUND" + fi + INTERFACE_GENERATION="${INTERFACE_LIBRARY_BUILDER} ${DYNAMIC_LIBRARY} + ${INTERFACE_LIBRARY}" + fi + + ${LINKER_COMMAND} "$other_args" && ${INTERFACE_GENERATION} +else + # TODO(b/113358321): Remove this branch once projects are migrated to not + # splitting the linking command line. + # Should generate interface library switch (); if the value is "no", + # following 3 args are ignored (but must be present) + GENERATE_INTERFACE_LIBRARY="$1" + # Tool which can generate interface library from dynamic library file + INTERFACE_LIBRARY_BUILDER="$2" + # Dynamic library from which we want to generate interface library + DYNAMIC_LIBRARY="$3" + # Resulting interface library + INTERFACE_LIBRARY="$4" + # The command used to generate the dynamic library + LINKER_COMMAND="$5" + shift 5 + if [[ ! -e "$LINKER_COMMAND" ]]; then + echo "Linker command ($LINKER_COMMAND) not found." 1>&2; + exit "$E_LINKER_COMMAND_NOT_FOUND" + fi + + if [[ "no" == "$GENERATE_INTERFACE_LIBRARY" ]]; then + INTERFACE_GENERATION=: + else + if [[ ! -e "$INTERFACE_LIBRARY_BUILDER" ]]; then + echo "Interface library builder ($INTERFACE_LIBRARY_BUILDER) + not found." 1>&2; + exit "$E_INTERFACE_BUILDER_NOT_FOUND" + fi + INTERFACE_GENERATION="${INTERFACE_LIBRARY_BUILDER} ${DYNAMIC_LIBRARY} + ${INTERFACE_LIBRARY}" + fi + + ${LINKER_COMMAND} "$@" && ${INTERFACE_GENERATION} +fi diff --git a/cc/private/toolchain/linux_cc_wrapper.sh.tpl b/cc/private/toolchain/linux_cc_wrapper.sh.tpl new file mode 100644 index 0000000..a83be50 --- /dev/null +++ b/cc/private/toolchain/linux_cc_wrapper.sh.tpl @@ -0,0 +1,25 @@ +#!/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. +# +# Ship the environment to the C++ action +# +set -eu + +# Set-up the environment +%{env} + +# Call the C++ compiler +%{cc} "$@" diff --git a/cc/private/toolchain/msys_gcc_installation_error.bat b/cc/private/toolchain/msys_gcc_installation_error.bat new file mode 100644 index 0000000..25c3553 --- /dev/null +++ b/cc/private/toolchain/msys_gcc_installation_error.bat @@ -0,0 +1,23 @@ +:: Copyright 2018 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. + +@echo OFF + +echo. 1>&2 +echo The target you are compiling requires MSYS gcc / MINGW gcc. 1>&2 +echo Bazel couldn't find gcc installation on your machine. 1>&2 +echo Please install MSYS gcc / MINGW gcc and set BAZEL_SH environment variable 1>&2 +echo. 1>&2 + +exit /b 1 diff --git a/cc/private/toolchain/osx_cc_configure.bzl b/cc/private/toolchain/osx_cc_configure.bzl new file mode 100644 index 0000000..e40b4e4 --- /dev/null +++ b/cc/private/toolchain/osx_cc_configure.bzl @@ -0,0 +1,148 @@ +# 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 macOS.""" + +load("@bazel_tools//tools/osx:xcode_configure.bzl", "run_xcode_locator") +load( + "@rules_cc//cc/private/toolchain:lib_cc_configure.bzl", + "escape_string", + "resolve_labels", +) +load( + "@rules_cc//cc/private/toolchain:unix_cc_configure.bzl", + "configure_unix_toolchain", + "find_cc", + "get_env", + "get_escaped_cxx_inc_directories", +) + +def _get_escaped_xcode_cxx_inc_directories(repository_ctx, cc, xcode_toolchains): + """Compute the list of default C++ include paths on Xcode-enabled darwin. + + Args: + repository_ctx: The repository context. + cc: The default C++ compiler on the local system. + xcode_toolchains: A list containing the xcode toolchains available + Returns: + include_paths: A list of builtin include paths. + """ + + # TODO(cparsons): Falling back to the default C++ compiler builtin include + # paths shouldn't be unnecessary once all actions are using xcrun. + include_dirs = get_escaped_cxx_inc_directories(repository_ctx, cc, "-xc++") + for toolchain in xcode_toolchains: + include_dirs.append(escape_string(toolchain.developer_dir)) + + # Assume that all paths that point to /Applications/ are built in include paths + include_dirs.append("/Applications/") + return include_dirs + +def configure_osx_toolchain(repository_ctx, overriden_tools): + """Configure C++ toolchain on macOS.""" + paths = resolve_labels(repository_ctx, [ + "@rules_cc//cc/private/toolchain:osx_cc_wrapper.sh.tpl", + "@bazel_tools//tools/objc:libtool.sh", + "@bazel_tools//tools/objc:make_hashed_objlist.py", + "@bazel_tools//tools/objc:xcrunwrapper.sh", + "@bazel_tools//tools/osx/crosstool:BUILD.tpl", + "@bazel_tools//tools/osx/crosstool:cc_toolchain_config.bzl", + "@bazel_tools//tools/osx/crosstool:wrapped_ar.tpl", + "@bazel_tools//tools/osx/crosstool:wrapped_clang.cc", + "@bazel_tools//tools/osx:xcode_locator.m", + ]) + + env = repository_ctx.os.environ + should_use_xcode = "BAZEL_USE_XCODE_TOOLCHAIN" in env and env["BAZEL_USE_XCODE_TOOLCHAIN"] == "1" + xcode_toolchains = [] + + # Make the following logic in sync with @rules_cc//cc/private/toolchain:cc_configure.bzl#cc_autoconf_toolchains_impl + (xcode_toolchains, xcodeloc_err) = run_xcode_locator( + repository_ctx, + paths["@bazel_tools//tools/osx:xcode_locator.m"], + ) + if should_use_xcode and not xcode_toolchains: + fail("BAZEL_USE_XCODE_TOOLCHAIN is set to 1 but Bazel couldn't find Xcode installed on the " + + "system. Verify that 'xcode-select -p' is correct.") + if xcode_toolchains: + cc = find_cc(repository_ctx, overriden_tools = {}) + repository_ctx.template( + "cc_wrapper.sh", + paths["@rules_cc//cc/private/toolchain:osx_cc_wrapper.sh.tpl"], + { + "%{cc}": escape_string(str(cc)), + "%{env}": escape_string(get_env(repository_ctx)), + }, + ) + repository_ctx.symlink( + paths["@bazel_tools//tools/objc:xcrunwrapper.sh"], + "xcrunwrapper.sh", + ) + repository_ctx.symlink( + paths["@bazel_tools//tools/objc:libtool.sh"], + "libtool", + ) + repository_ctx.symlink( + paths["@bazel_tools//tools/objc:make_hashed_objlist.py"], + "make_hashed_objlist.py", + ) + repository_ctx.symlink( + paths["@bazel_tools//tools/osx/crosstool:wrapped_ar.tpl"], + "wrapped_ar", + ) + repository_ctx.symlink( + paths["@bazel_tools//tools/osx/crosstool:cc_toolchain_config.bzl"], + "cc_toolchain_config.bzl", + ) + wrapped_clang_src_path = str(repository_ctx.path( + paths["@bazel_tools//tools/osx/crosstool:wrapped_clang.cc"], + )) + xcrun_result = repository_ctx.execute([ + "env", + "-i", + "xcrun", + "clang", + "-std=c++11", + "-lc++", + "-o", + "wrapped_clang", + wrapped_clang_src_path, + ], 30) + if (xcrun_result.return_code == 0): + repository_ctx.symlink("wrapped_clang", "wrapped_clang_pp") + else: + error_msg = ( + "return code {code}, stderr: {err}, stdout: {out}" + ).format( + code = xcrun_result.return_code, + err = xcrun_result.stderr, + out = xcrun_result.stdout, + ) + fail("wrapped_clang failed to generate. Please file an issue at " + + "https://github.com/bazelbuild/bazel/issues with the following:\n" + + error_msg) + + escaped_include_paths = _get_escaped_xcode_cxx_inc_directories(repository_ctx, cc, xcode_toolchains) + escaped_cxx_include_directories = [] + for path in escaped_include_paths: + escaped_cxx_include_directories.append((" \"%s\"," % path)) + if xcodeloc_err: + escaped_cxx_include_directories.append("# Error: " + xcodeloc_err + "\n") + repository_ctx.template( + "BUILD", + paths["@bazel_tools//tools/osx/crosstool:BUILD.tpl"], + {"%{cxx_builtin_include_directories}": "\n".join(escaped_cxx_include_directories)}, + ) + else: + configure_unix_toolchain(repository_ctx, cpu_value = "darwin", overriden_tools = overriden_tools) diff --git a/cc/private/toolchain/osx_cc_wrapper.sh b/cc/private/toolchain/osx_cc_wrapper.sh new file mode 100755 index 0000000..207bcef --- /dev/null +++ b/cc/private/toolchain/osx_cc_wrapper.sh @@ -0,0 +1,104 @@ +#!/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 + +GCC=/usr/bin/gcc +INSTALL_NAME_TOOL="/usr/bin/install_name_tool" + +LIBS= +LIB_DIRS= +RPATHS= +OUTPUT= +# let parse the option list +for i in "$@"; do + if [[ "${OUTPUT}" = "1" ]]; then + OUTPUT=$i + elif [[ "$i" =~ ^-l(.*)$ ]]; then + LIBS="${BASH_REMATCH[1]} $LIBS" + elif [[ "$i" =~ ^-L(.*)$ ]]; then + LIB_DIRS="${BASH_REMATCH[1]} $LIB_DIRS" + elif [[ "$i" =~ ^-Wl,-rpath,\@loader_path/(.*)$ ]]; then + RPATHS="${BASH_REMATCH[1]} ${RPATHS}" + elif [[ "$i" = "-o" ]]; then + # output is coming + OUTPUT=1 + fi +done + +# Call gcc +${GCC} "$@" + +function get_library_path() { + for libdir in ${LIB_DIRS}; do + if [ -f ${libdir}/lib$1.so ]; then + echo "${libdir}/lib$1.so" + elif [ -f ${libdir}/lib$1.dylib ]; then + echo "${libdir}/lib$1.dylib" + 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 +for rpath in ${RPATHS}; do + for lib in ${LIBS}; do + unset libname + if [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.so" ]; then + libname="lib${lib}.so" + elif [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.dylib" ]; then + libname="lib${lib}.dylib" + fi + # ${libname-} --> return $libname if defined, or undefined otherwise. This is to make + # this set -e friendly + if [[ -n "${libname-}" ]]; then + libpath=$(get_library_path ${lib}) + if [ -n "${libpath}" ]; then + ${INSTALL_NAME_TOOL} -change $(get_otool_path "${libpath}") \ + "@loader_path/${rpath}/${libname}" "${OUTPUT}" + fi + fi + done +done + diff --git a/cc/private/toolchain/osx_cc_wrapper.sh.tpl b/cc/private/toolchain/osx_cc_wrapper.sh.tpl new file mode 100644 index 0000000..4c85cd9 --- /dev/null +++ b/cc/private/toolchain/osx_cc_wrapper.sh.tpl @@ -0,0 +1,106 @@ +#!/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= +RPATHS= +OUTPUT= +# let parse the option list +for i in "$@"; do + if [[ "${OUTPUT}" = "1" ]]; then + OUTPUT=$i + elif [[ "$i" =~ ^-l(.*)$ ]]; then + LIBS="${BASH_REMATCH[1]} $LIBS" + elif [[ "$i" =~ ^-L(.*)$ ]]; then + LIB_DIRS="${BASH_REMATCH[1]} $LIB_DIRS" + elif [[ "$i" =~ ^-Wl,-rpath,\@loader_path/(.*)$ ]]; then + RPATHS="${BASH_REMATCH[1]} ${RPATHS}" + elif [[ "$i" = "-o" ]]; then + # output is coming + OUTPUT=1 + fi +done + +# Set-up the environment +%{env} + +# 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" + elif [ -f ${libdir}/lib$1.dylib ]; then + echo "${libdir}/lib$1.dylib" + 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 +for rpath in ${RPATHS}; do + for lib in ${LIBS}; do + unset libname + if [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.so" ]; then + libname="lib${lib}.so" + elif [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.dylib" ]; then + libname="lib${lib}.dylib" + fi + # ${libname-} --> return $libname if defined, or undefined otherwise. This is to make + # this set -e friendly + if [[ -n "${libname-}" ]]; then + libpath=$(get_library_path ${lib}) + if [ -n "${libpath}" ]; then + ${INSTALL_NAME_TOOL} -change $(get_otool_path "${libpath}") \ + "@loader_path/${rpath}/${libname}" "${OUTPUT}" + fi + fi + done +done + diff --git a/cc/private/toolchain/runfiles/BUILD b/cc/private/toolchain/runfiles/BUILD new file mode 100644 index 0000000..cb4a9d5 --- /dev/null +++ b/cc/private/toolchain/runfiles/BUILD @@ -0,0 +1,84 @@ +package(default_visibility = ["//visibility:private"]) + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") + +filegroup( + name = "srcs", + srcs = glob( + ["**"], + exclude = [ + ".*", + "*~", + ], + ), + visibility = ["//cc/private/toolchain:__pkg__"], +) + +filegroup( + name = "embedded_tools", + srcs = [ + "BUILD.tools", + ":srcs_for_embedded_tools", + ], + visibility = ["//cc/private/toolchain:__pkg__"], +) + +# Rewrite the include path for runfiles.h in runfiles_src.cc, and create +# "generated_runfiles.{h,cc}". These files are renamed to "runfiles.{h,cc}" as +# part of creating the embedded tools of Bazel. +# +# We cannot just check in runfiles_src.{h,cc} as runfiles.{h,cc}, because it'd +# cause a header check failure on Windows when building targets in the Bazel +# source tree, if those targets depend on @rules_cc//cc/private/toolchain/runfiles, +# because due to lack of sandboxing they would accidentally pick up runfiles.h +# from @rules_cc//cc/private/toolchain/runfiles. +genrule( + name = "srcs_for_embedded_tools", + srcs = [ + "runfiles_src.cc", + "runfiles_src.h", + ], + outs = [ + "generated_runfiles.cc", + "generated_runfiles.h", + ], + # Keep this transformation logic in sync with the + # //scripts/bootstrap/compile.sh + cmd = ("sed " + + " 's|^#include.*/runfiles_src.h.*|#include \"tools/cpp/runfiles/runfiles.h\"|' " + + " $(location runfiles_src.cc) > $(location generated_runfiles.cc) && " + + "cp $(location runfiles_src.h) $(location generated_runfiles.h)"), +) + +cc_library( + name = "runfiles", + testonly = 1, + srcs = ["runfiles_src.cc"], + hdrs = ["runfiles_src.h"], +) + +cc_test( + name = "runfiles_test", + srcs = ["runfiles_test.cc"], + visibility = ["//visibility:public"], + deps = [ + ":runfiles", + "@com_google_googletest//:gtest_main", + ], +) + +test_suite( + name = "windows_tests", + tags = [ + "-no_windows", + "-slow", + ], +) + +test_suite( + name = "all_windows_tests", + tests = [ + ":windows_tests", + ], + visibility = ["//tools:__pkg__"], +) diff --git a/cc/private/toolchain/runfiles/BUILD.tools b/cc/private/toolchain/runfiles/BUILD.tools new file mode 100644 index 0000000..ef9518c --- /dev/null +++ b/cc/private/toolchain/runfiles/BUILD.tools @@ -0,0 +1,8 @@ +# This package will host the C++ runfiles library when it's finally released. + +cc_library( + name = "runfiles", + srcs = ["runfiles.cc"], + hdrs = ["runfiles.h"], + visibility = ["//visibility:public"], +) diff --git a/cc/private/toolchain/runfiles/runfiles_src.cc b/cc/private/toolchain/runfiles/runfiles_src.cc new file mode 100644 index 0000000..e1fb7f4 --- /dev/null +++ b/cc/private/toolchain/runfiles/runfiles_src.cc @@ -0,0 +1,318 @@ +// Copyright 2018 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. + +// The "srcs_for_embedded_tools" rule in the same package sets the line below to +// include runfiles.h from the correct path. Do not modify the line below. +#include "cc/private/toolchain/runfiles/runfiles_src.h" + +#ifdef _WIN32 +#include +#else // not _WIN32 +#include +#include +#include +#include +#endif // _WIN32 + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif // _WIN32 + +namespace bazel { +namespace tools { +namespace cpp { +namespace runfiles { + +using std::function; +using std::map; +using std::pair; +using std::string; +using std::vector; + +namespace { + +bool starts_with(const string& s, const char* prefix) { + if (!prefix || !*prefix) { + return true; + } + if (s.empty()) { + return false; + } + return s.find(prefix) == 0; +} + +bool contains(const string& s, const char* substr) { + if (!substr || !*substr) { + return true; + } + if (s.empty()) { + return false; + } + return s.find(substr) != string::npos; +} + +bool ends_with(const string& s, const string& suffix) { + if (suffix.empty()) { + return true; + } + if (s.empty()) { + return false; + } + return s.rfind(suffix) == s.size() - suffix.size(); +} + +bool IsReadableFile(const string& path) { + return std::ifstream(path).is_open(); +} + +bool IsDirectory(const string& path) { +#ifdef _WIN32 + DWORD attrs = GetFileAttributesA(path.c_str()); + return (attrs != INVALID_FILE_ATTRIBUTES) && + (attrs & FILE_ATTRIBUTE_DIRECTORY); +#else + struct stat buf; + return stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode); +#endif +} + +bool PathsFrom(const std::string& argv0, std::string runfiles_manifest_file, + std::string runfiles_dir, std::string* out_manifest, + std::string* out_directory); + +bool PathsFrom(const std::string& argv0, std::string runfiles_manifest_file, + std::string runfiles_dir, + std::function is_runfiles_manifest, + std::function is_runfiles_directory, + std::string* out_manifest, std::string* out_directory); + +bool ParseManifest(const string& path, map* result, + string* error); + +} // namespace + +Runfiles* Runfiles::Create(const string& argv0, + const string& runfiles_manifest_file, + const string& runfiles_dir, string* error) { + string manifest, directory; + if (!PathsFrom(argv0, runfiles_manifest_file, runfiles_dir, &manifest, + &directory)) { + if (error) { + std::ostringstream err; + err << "ERROR: " << __FILE__ << "(" << __LINE__ + << "): cannot find runfiles (argv0=\"" << argv0 << "\")"; + *error = err.str(); + } + return nullptr; + } + + const vector > envvars = { + {"RUNFILES_MANIFEST_FILE", manifest}, + {"RUNFILES_DIR", directory}, + // TODO(laszlocsomor): remove JAVA_RUNFILES once the Java launcher can + // pick up RUNFILES_DIR. + {"JAVA_RUNFILES", directory}}; + + map runfiles; + if (!manifest.empty()) { + if (!ParseManifest(manifest, &runfiles, error)) { + return nullptr; + } + } + + return new Runfiles(std::move(runfiles), std::move(directory), + std::move(envvars)); +} + +bool IsAbsolute(const string& path) { + if (path.empty()) { + return false; + } + char c = path.front(); + return (c == '/' && (path.size() < 2 || path[1] != '/')) || + (path.size() >= 3 && + ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && + path[1] == ':' && (path[2] == '\\' || path[2] == '/')); +} + +string GetEnv(const string& key) { +#ifdef _WIN32 + DWORD size = ::GetEnvironmentVariableA(key.c_str(), NULL, 0); + if (size == 0) { + return string(); // unset or empty envvar + } + std::unique_ptr value(new char[size]); + ::GetEnvironmentVariableA(key.c_str(), value.get(), size); + return value.get(); +#else + char* result = getenv(key.c_str()); + return (result == NULL) ? string() : string(result); +#endif +} + +string Runfiles::Rlocation(const string& path) const { + if (path.empty() || starts_with(path, "../") || contains(path, "/..") || + starts_with(path, "./") || contains(path, "/./") || + ends_with(path, "/.") || contains(path, "//")) { + return string(); + } + if (IsAbsolute(path)) { + return path; + } + const auto value = runfiles_map_.find(path); + if (value != runfiles_map_.end()) { + return value->second; + } + if (!directory_.empty()) { + return directory_ + "/" + path; + } + return ""; +} + +namespace { + +bool ParseManifest(const string& path, map* result, + string* error) { + std::ifstream stm(path); + if (!stm.is_open()) { + if (error) { + std::ostringstream err; + err << "ERROR: " << __FILE__ << "(" << __LINE__ + << "): cannot open runfiles manifest \"" << path << "\""; + *error = err.str(); + } + return false; + } + string line; + std::getline(stm, line); + size_t line_count = 1; + while (!line.empty()) { + string::size_type idx = line.find_first_of(' '); + if (idx == string::npos) { + if (error) { + std::ostringstream err; + err << "ERROR: " << __FILE__ << "(" << __LINE__ + << "): bad runfiles manifest entry in \"" << path << "\" line #" + << line_count << ": \"" << line << "\""; + *error = err.str(); + } + return false; + } + (*result)[line.substr(0, idx)] = line.substr(idx + 1); + std::getline(stm, line); + ++line_count; + } + return true; +} + +} // namespace + +namespace testing { + +bool TestOnly_PathsFrom(const string& argv0, string mf, string dir, + function is_runfiles_manifest, + function is_runfiles_directory, + string* out_manifest, string* out_directory) { + return PathsFrom(argv0, mf, dir, is_runfiles_manifest, is_runfiles_directory, + out_manifest, out_directory); +} + +bool TestOnly_IsAbsolute(const string& path) { return IsAbsolute(path); } + +} // namespace testing + +Runfiles* Runfiles::Create(const string& argv0, string* error) { + return Runfiles::Create(argv0, GetEnv("RUNFILES_MANIFEST_FILE"), + GetEnv("RUNFILES_DIR"), error); +} + +Runfiles* Runfiles::CreateForTest(std::string* error) { + return Runfiles::Create(std::string(), GetEnv("RUNFILES_MANIFEST_FILE"), + GetEnv("TEST_SRCDIR"), error); +} + +namespace { + +bool PathsFrom(const string& argv0, string mf, string dir, string* out_manifest, + string* out_directory) { + return PathsFrom( + argv0, mf, dir, [](const string& path) { return IsReadableFile(path); }, + [](const string& path) { return IsDirectory(path); }, out_manifest, + out_directory); +} + +bool PathsFrom(const string& argv0, string mf, string dir, + function is_runfiles_manifest, + function is_runfiles_directory, + string* out_manifest, string* out_directory) { + out_manifest->clear(); + out_directory->clear(); + + bool mfValid = is_runfiles_manifest(mf); + bool dirValid = is_runfiles_directory(dir); + + if (!argv0.empty() && !mfValid && !dirValid) { + mf = argv0 + ".runfiles/MANIFEST"; + dir = argv0 + ".runfiles"; + mfValid = is_runfiles_manifest(mf); + dirValid = is_runfiles_directory(dir); + if (!mfValid) { + mf = argv0 + ".runfiles_manifest"; + mfValid = is_runfiles_manifest(mf); + } + } + + if (!mfValid && !dirValid) { + return false; + } + + if (!mfValid) { + mf = dir + "/MANIFEST"; + mfValid = is_runfiles_manifest(mf); + if (!mfValid) { + mf = dir + "_manifest"; + mfValid = is_runfiles_manifest(mf); + } + } + + if (!dirValid && + (ends_with(mf, ".runfiles_manifest") || ends_with(mf, "/MANIFEST"))) { + static const size_t kSubstrLen = 9; // "_manifest" or "/MANIFEST" + dir = mf.substr(0, mf.size() - kSubstrLen); + dirValid = is_runfiles_directory(dir); + } + + if (mfValid) { + *out_manifest = mf; + } + + if (dirValid) { + *out_directory = dir; + } + + return true; +} + +} // namespace + +} // namespace runfiles +} // namespace cpp +} // namespace tools +} // namespace bazel diff --git a/cc/private/toolchain/runfiles/runfiles_src.h b/cc/private/toolchain/runfiles/runfiles_src.h new file mode 100644 index 0000000..b6e6178 --- /dev/null +++ b/cc/private/toolchain/runfiles/runfiles_src.h @@ -0,0 +1,222 @@ +// Copyright 2018 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. + +// Runfiles lookup library for Bazel-built C++ binaries and tests. +// +// USAGE: +// 1. Depend on this runfiles library from your build rule: +// +// cc_binary( +// name = "my_binary", +// ... +// deps = ["@rules_cc//cc/private/toolchain/runfiles"], +// ) +// +// 2. Include the runfiles library. +// +// #include "tools/cpp/runfiles/runfiles.h" +// +// using bazel::tools::cpp::runfiles::Runfiles; +// +// 3. Create a Runfiles object and use rlocation to look up runfile paths: +// +// int main(int argc, char** argv) { +// std::string error; +// std::unique_ptr runfiles( +// Runfiles::Create(argv[0], &error)); +// +// // Important: +// // If this is a test, use Runfiles::CreateForTest(&error). +// // Otherwise, if you don't have the value for argv[0] for whatever +// // reason, then use Runfiles::Create(&error). +// +// if (runfiles == nullptr) { +// ... // error handling +// } +// std::string path = +// runfiles->Rlocation("my_workspace/path/to/my/data.txt"); +// ... +// +// The code above creates a Runfiles object and retrieves a runfile path. +// +// The Runfiles::Create function uses the runfiles manifest and the +// runfiles directory from the RUNFILES_MANIFEST_FILE and RUNFILES_DIR +// environment variables. If not present, the function looks for the +// manifest and directory near argv[0], the path of the main program. +// +// To start child processes that also need runfiles, you need to set the right +// environment variables for them: +// +// std::unique_ptr runfiles(Runfiles::Create(argv[0], &error)); +// +// std::string path = runfiles->Rlocation("path/to/binary")); +// if (!path.empty()) { +// ... // create "args" argument vector for execv +// const auto envvars = runfiles->EnvVars(); +// pid_t child = fork(); +// if (child) { +// int status; +// waitpid(child, &status, 0); +// } else { +// for (const auto i : envvars) { +// setenv(i.first.c_str(), i.second.c_str(), 1); +// } +// execv(args[0], args); +// } + +#ifndef TOOLS_CPP_RUNFILES_RUNFILES_H_ +#define TOOLS_CPP_RUNFILES_RUNFILES_H_ 1 + +#include +#include +#include +#include +#include + +namespace bazel { +namespace tools { +namespace cpp { +namespace runfiles { + +class Runfiles { + public: + virtual ~Runfiles() {} + + // Returns a new `Runfiles` instance. + // + // Use this from within `cc_test` rules. + // + // Returns nullptr on error. If `error` is provided, the method prints an + // error message into it. + // + // This method looks at the RUNFILES_MANIFEST_FILE and TEST_SRCDIR + // environment variables. + static Runfiles* CreateForTest(std::string* error = nullptr); + + // Returns a new `Runfiles` instance. + // + // Use this from `cc_binary` or `cc_library` rules. You may pass an empty + // `argv0` if `argv[0]` from the `main` method is unknown. + // + // Returns nullptr on error. If `error` is provided, the method prints an + // error message into it. + // + // This method looks at the RUNFILES_MANIFEST_FILE and RUNFILES_DIR + // environment variables. If either is empty, the method looks for the + // manifest or directory using the other environment variable, or using argv0 + // (unless it's empty). + static Runfiles* Create(const std::string& argv0, + std::string* error = nullptr); + + // Returns a new `Runfiles` instance. + // + // Use this from any `cc_*` rule if you want to manually specify the paths to + // the runfiles manifest and/or runfiles directory. You may pass an empty + // `argv0` if `argv[0]` from the `main` method is unknown. + // + // This method is the same as `Create(argv0, error)`, except it uses + // `runfiles_manifest_file` and `runfiles_dir` as the corresponding + // environment variable values, instead of looking up the actual environment + // variables. + static Runfiles* Create(const std::string& argv0, + const std::string& runfiles_manifest_file, + const std::string& runfiles_dir, + std::string* error = nullptr); + + // Returns the runtime path of a runfile. + // + // Runfiles are data-dependencies of Bazel-built binaries and tests. + // + // The returned path may not exist. The caller should verify the path's + // existence. + // + // The function may return an empty string if it cannot find a runfile. + // + // Args: + // path: runfiles-root-relative path of the runfile; must not be empty and + // must not contain uplevel references. + // Returns: + // the path to the runfile, which the caller should check for existence, or + // an empty string if the method doesn't know about this runfile + std::string Rlocation(const std::string& path) const; + + // Returns environment variables for subprocesses. + // + // The caller should set the returned key-value pairs in the environment of + // subprocesses, so that those subprocesses can also access runfiles (in case + // they are also Bazel-built binaries). + const std::vector >& EnvVars() const { + return envvars_; + } + + private: + Runfiles(const std::map&& runfiles_map, + const std::string&& directory, + const std::vector >&& envvars) + : runfiles_map_(std::move(runfiles_map)), + directory_(std::move(directory)), + envvars_(std::move(envvars)) {} + Runfiles(const Runfiles&) = delete; + Runfiles(Runfiles&&) = delete; + Runfiles& operator=(const Runfiles&) = delete; + Runfiles& operator=(Runfiles&&) = delete; + + const std::map runfiles_map_; + const std::string directory_; + const std::vector > envvars_; +}; + +// The "testing" namespace contains functions that allow unit testing the code. +// Do not use these outside of runfiles_test.cc, they are only part of the +// public API for the benefit of the tests. +// These functions and their interface may change without notice. +namespace testing { + +// For testing only. +// +// Computes the path of the runfiles manifest and the runfiles directory. +// +// If the method finds both a valid manifest and valid directory according to +// `is_runfiles_manifest` and `is_runfiles_directory`, then the method sets +// the corresponding values to `out_manifest` and `out_directory` and returns +// true. +// +// If the method only finds a valid manifest or a valid directory, but not +// both, then it sets the corresponding output variable (`out_manifest` or +// `out_directory`) to the value while clearing the other output variable. The +// method still returns true in this case. +// +// If the method cannot find either a valid manifest or valid directory, it +// clears both output variables and returns false. +bool TestOnly_PathsFrom( + const std::string& argv0, std::string runfiles_manifest_file, + std::string runfiles_dir, + std::function is_runfiles_manifest, + std::function is_runfiles_directory, + std::string* out_manifest, std::string* out_directory); + +// For testing only. +// Returns true if `path` is an absolute Unix or Windows path. +// For Windows paths, this function does not regard drive-less absolute paths +// (i.e. absolute-on-current-drive, e.g. "\foo\bar") as absolute and returns +// false for these. +bool TestOnly_IsAbsolute(const std::string& path); + +} // namespace testing +} // namespace runfiles +} // namespace cpp +} // namespace tools +} // namespace bazel + +#endif // TOOLS_CPP_RUNFILES_RUNFILES_H_ diff --git a/cc/private/toolchain/runfiles/runfiles_test.cc b/cc/private/toolchain/runfiles/runfiles_test.cc new file mode 100644 index 0000000..36ef21f --- /dev/null +++ b/cc/private/toolchain/runfiles/runfiles_test.cc @@ -0,0 +1,582 @@ +// Copyright 2018 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. + +#include "cc/private/toolchain/runfiles/runfiles_src.h" +#ifdef _WIN32 +#include +#endif // _WIN32 + +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#define RUNFILES_TEST_TOSTRING_HELPER(x) #x +#define RUNFILES_TEST_TOSTRING(x) RUNFILES_TEST_TOSTRING_HELPER(x) +#define LINE_AS_STRING() RUNFILES_TEST_TOSTRING(__LINE__) + +namespace bazel { +namespace tools { +namespace cpp { +namespace runfiles { +namespace { + +using bazel::tools::cpp::runfiles::testing::TestOnly_IsAbsolute; +using bazel::tools::cpp::runfiles::testing::TestOnly_PathsFrom; +using std::cerr; +using std::endl; +using std::function; +using std::pair; +using std::string; +using std::unique_ptr; +using std::vector; + +class RunfilesTest : public ::testing::Test { + protected: + // Create a temporary file that is deleted with the destructor. + class MockFile { + public: + // Create an empty file with the given name under $TEST_TMPDIR. + static MockFile* Create(const string& name); + + // Create a file with the given name and contents under $TEST_TMPDIR. + // The method ensures to create all parent directories, so `name` is allowed + // to contain directory components. + static MockFile* Create(const string& name, const vector& lines); + + ~MockFile(); + const string& Path() const { return path_; } + + string DirName() const { + string::size_type pos = path_.find_last_of('/'); + return pos == string::npos ? "" : path_.substr(0, pos); + } + + private: + MockFile(const string& path) : path_(path) {} + MockFile(const MockFile&) = delete; + MockFile(MockFile&&) = delete; + MockFile& operator=(const MockFile&) = delete; + MockFile& operator=(MockFile&&) = delete; + + const string path_; + }; + + void AssertEnvvars(const Runfiles& runfiles, + const string& expected_manifest_file, + const string& expected_directory); + + static string GetTemp(); +}; + +void RunfilesTest::AssertEnvvars(const Runfiles& runfiles, + const string& expected_manifest_file, + const string& expected_directory) { + vector > expected = { + {"RUNFILES_MANIFEST_FILE", expected_manifest_file}, + {"RUNFILES_DIR", expected_directory}, + {"JAVA_RUNFILES", expected_directory}}; + ASSERT_EQ(runfiles.EnvVars(), expected); +} + +string RunfilesTest::GetTemp() { +#ifdef _WIN32 + DWORD size = ::GetEnvironmentVariableA("TEST_TMPDIR", NULL, 0); + if (size == 0) { + return string(); // unset or empty envvar + } + unique_ptr value(new char[size]); + ::GetEnvironmentVariableA("TEST_TMPDIR", value.get(), size); + return value.get(); +#else + char* result = getenv("TEST_TMPDIR"); + return result != NULL ? string(result) : string(); +#endif +} + +RunfilesTest::MockFile* RunfilesTest::MockFile::Create(const string& name) { + return Create(name, vector()); +} + +RunfilesTest::MockFile* RunfilesTest::MockFile::Create( + const string& name, const vector& lines) { + if (name.find("..") != string::npos || TestOnly_IsAbsolute(name)) { + cerr << "WARNING: " << __FILE__ << "(" << __LINE__ << "): bad name: \"" + << name << "\"" << endl; + return nullptr; + } + + string tmp(RunfilesTest::GetTemp()); + if (tmp.empty()) { + cerr << "WARNING: " << __FILE__ << "(" << __LINE__ + << "): $TEST_TMPDIR is empty" << endl; + return nullptr; + } + string path(tmp + "/" + name); + + string::size_type i = 0; +#ifdef _WIN32 + while ((i = name.find_first_of("/\\", i + 1)) != string::npos) { + string d = tmp + "\\" + name.substr(0, i); + if (!CreateDirectoryA(d.c_str(), NULL)) { + cerr << "ERROR: " << __FILE__ << "(" << __LINE__ + << "): failed to create directory \"" << d << "\"" << endl; + return nullptr; + } + } +#else + while ((i = name.find_first_of('/', i + 1)) != string::npos) { + string d = tmp + "/" + name.substr(0, i); + if (mkdir(d.c_str(), 0777)) { + cerr << "ERROR: " << __FILE__ << "(" << __LINE__ + << "): failed to create directory \"" << d << "\"" << endl; + return nullptr; + } + } +#endif + + std::ofstream stm(path); + for (auto i : lines) { + stm << i << std::endl; + } + return new MockFile(path); +} + +RunfilesTest::MockFile::~MockFile() { std::remove(path_.c_str()); } + +TEST_F(RunfilesTest, CreatesManifestBasedRunfilesFromManifestNextToBinary) { + unique_ptr mf(MockFile::Create( + "foo" LINE_AS_STRING() ".runfiles_manifest", {"a/b c/d"})); + EXPECT_TRUE(mf != nullptr); + string argv0(mf->Path().substr( + 0, mf->Path().size() - string(".runfiles_manifest").size())); + + string error; + unique_ptr r(Runfiles::Create(argv0, "", "", &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + EXPECT_EQ(r->Rlocation("a/b"), "c/d"); + // We know it's manifest-based because it returns empty string for unknown + // paths. + EXPECT_EQ(r->Rlocation("unknown"), ""); + AssertEnvvars(*r, mf->Path(), ""); +} + +TEST_F(RunfilesTest, + CreatesManifestBasedRunfilesFromManifestInRunfilesDirectory) { + unique_ptr mf(MockFile::Create( + "foo" LINE_AS_STRING() ".runfiles/MANIFEST", {"a/b c/d"})); + EXPECT_TRUE(mf != nullptr); + string argv0(mf->Path().substr( + 0, mf->Path().size() - string(".runfiles/MANIFEST").size())); + + string error; + unique_ptr r(Runfiles::Create(argv0, "", "", &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + EXPECT_EQ(r->Rlocation("a/b"), "c/d"); + EXPECT_EQ(r->Rlocation("foo"), argv0 + ".runfiles/foo"); + AssertEnvvars(*r, mf->Path(), argv0 + ".runfiles"); +} + +TEST_F(RunfilesTest, CreatesManifestBasedRunfilesFromEnvvar) { + unique_ptr mf(MockFile::Create( + "foo" LINE_AS_STRING() ".runfiles_manifest", {"a/b c/d"})); + EXPECT_TRUE(mf != nullptr); + + string error; + unique_ptr r(Runfiles::Create("ignore-argv0", mf->Path(), + "non-existent-runfiles_dir", &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + EXPECT_EQ(r->Rlocation("a/b"), "c/d"); + // We know it's manifest-based because it returns empty string for unknown + // paths. + EXPECT_EQ(r->Rlocation("unknown"), ""); + AssertEnvvars(*r, mf->Path(), ""); +} + +TEST_F(RunfilesTest, CannotCreateManifestBasedRunfilesDueToBadManifest) { + unique_ptr mf(MockFile::Create( + "foo" LINE_AS_STRING() ".runfiles_manifest", {"a b", "nospace"})); + EXPECT_TRUE(mf != nullptr); + + string error; + unique_ptr r( + Runfiles::Create("ignore-argv0", mf->Path(), "", &error)); + ASSERT_EQ(r, nullptr); + EXPECT_NE(error.find("bad runfiles manifest entry"), string::npos); + EXPECT_NE(error.find("line #2: \"nospace\""), string::npos); +} + +TEST_F(RunfilesTest, ManifestBasedRunfilesRlocationAndEnvVars) { + unique_ptr mf(MockFile::Create( + "foo" LINE_AS_STRING() ".runfiles_manifest", {"a/b c/d"})); + EXPECT_TRUE(mf != nullptr); + + string error; + unique_ptr r( + Runfiles::Create("ignore-argv0", mf->Path(), "", &error)); + + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + EXPECT_EQ(r->Rlocation("a/b"), "c/d"); + EXPECT_EQ(r->Rlocation("c/d"), ""); + EXPECT_EQ(r->Rlocation(""), ""); + EXPECT_EQ(r->Rlocation("foo"), ""); + EXPECT_EQ(r->Rlocation("foo/"), ""); + EXPECT_EQ(r->Rlocation("foo/bar"), ""); + EXPECT_EQ(r->Rlocation("../foo"), ""); + EXPECT_EQ(r->Rlocation("foo/.."), ""); + EXPECT_EQ(r->Rlocation("foo/../bar"), ""); + EXPECT_EQ(r->Rlocation("./foo"), ""); + EXPECT_EQ(r->Rlocation("foo/."), ""); + EXPECT_EQ(r->Rlocation("foo/./bar"), ""); + EXPECT_EQ(r->Rlocation("//foo"), ""); + EXPECT_EQ(r->Rlocation("foo//"), ""); + EXPECT_EQ(r->Rlocation("foo//bar"), ""); + EXPECT_EQ(r->Rlocation("/Foo"), "/Foo"); + EXPECT_EQ(r->Rlocation("c:/Foo"), "c:/Foo"); + EXPECT_EQ(r->Rlocation("c:\\Foo"), "c:\\Foo"); +} + +TEST_F(RunfilesTest, DirectoryBasedRunfilesRlocationAndEnvVars) { + unique_ptr dummy( + MockFile::Create("foo" LINE_AS_STRING() ".runfiles/dummy", {"a/b c/d"})); + EXPECT_TRUE(dummy != nullptr); + string dir = dummy->DirName(); + + string error; + unique_ptr r(Runfiles::Create("ignore-argv0", "", dir, &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + + EXPECT_EQ(r->Rlocation("a/b"), dir + "/a/b"); + EXPECT_EQ(r->Rlocation("c/d"), dir + "/c/d"); + EXPECT_EQ(r->Rlocation(""), ""); + EXPECT_EQ(r->Rlocation("foo"), dir + "/foo"); + EXPECT_EQ(r->Rlocation("foo/"), dir + "/foo/"); + EXPECT_EQ(r->Rlocation("foo/bar"), dir + "/foo/bar"); + EXPECT_EQ(r->Rlocation("../foo"), ""); + EXPECT_EQ(r->Rlocation("foo/.."), ""); + EXPECT_EQ(r->Rlocation("foo/../bar"), ""); + EXPECT_EQ(r->Rlocation("./foo"), ""); + EXPECT_EQ(r->Rlocation("foo/."), ""); + EXPECT_EQ(r->Rlocation("foo/./bar"), ""); + EXPECT_EQ(r->Rlocation("//foo"), ""); + EXPECT_EQ(r->Rlocation("foo//"), ""); + EXPECT_EQ(r->Rlocation("foo//bar"), ""); + EXPECT_EQ(r->Rlocation("/Foo"), "/Foo"); + EXPECT_EQ(r->Rlocation("c:/Foo"), "c:/Foo"); + EXPECT_EQ(r->Rlocation("c:\\Foo"), "c:\\Foo"); + AssertEnvvars(*r, "", dir); +} + +TEST_F(RunfilesTest, ManifestAndDirectoryBasedRunfilesRlocationAndEnvVars) { + unique_ptr mf(MockFile::Create( + "foo" LINE_AS_STRING() ".runfiles/MANIFEST", {"a/b c/d"})); + EXPECT_TRUE(mf != nullptr); + string dir = mf->DirName(); + + string error; + unique_ptr r( + Runfiles::Create("ignore-argv0", mf->Path(), "", &error)); + + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + EXPECT_EQ(r->Rlocation("a/b"), "c/d"); + EXPECT_EQ(r->Rlocation("c/d"), dir + "/c/d"); + EXPECT_EQ(r->Rlocation(""), ""); + EXPECT_EQ(r->Rlocation("foo"), dir + "/foo"); + EXPECT_EQ(r->Rlocation("foo/"), dir + "/foo/"); + EXPECT_EQ(r->Rlocation("foo/bar"), dir + "/foo/bar"); + EXPECT_EQ(r->Rlocation("../foo"), ""); + EXPECT_EQ(r->Rlocation("foo/.."), ""); + EXPECT_EQ(r->Rlocation("foo/../bar"), ""); + EXPECT_EQ(r->Rlocation("./foo"), ""); + EXPECT_EQ(r->Rlocation("foo/."), ""); + EXPECT_EQ(r->Rlocation("foo/./bar"), ""); + EXPECT_EQ(r->Rlocation("//foo"), ""); + EXPECT_EQ(r->Rlocation("foo//"), ""); + EXPECT_EQ(r->Rlocation("foo//bar"), ""); + EXPECT_EQ(r->Rlocation("/Foo"), "/Foo"); + EXPECT_EQ(r->Rlocation("c:/Foo"), "c:/Foo"); + EXPECT_EQ(r->Rlocation("c:\\Foo"), "c:\\Foo"); + AssertEnvvars(*r, mf->Path(), dir); +} + +TEST_F(RunfilesTest, ManifestBasedRunfilesEnvVars) { + unique_ptr mf( + MockFile::Create(string("foo" LINE_AS_STRING() ".runfiles_manifest"))); + EXPECT_TRUE(mf != nullptr); + + string error; + unique_ptr r( + Runfiles::Create("ignore-argv0", mf->Path(), "", &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + + AssertEnvvars(*r, mf->Path(), ""); +} + +TEST_F(RunfilesTest, CreatesDirectoryBasedRunfilesFromDirectoryNextToBinary) { + // We create a directory as a side-effect of creating a mock file. + unique_ptr mf( + MockFile::Create(string("foo" LINE_AS_STRING() ".runfiles/dummy"))); + string argv0(mf->Path().substr( + 0, mf->Path().size() - string(".runfiles/dummy").size())); + + string error; + unique_ptr r(Runfiles::Create(argv0, "", "", &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + + EXPECT_EQ(r->Rlocation("a/b"), argv0 + ".runfiles/a/b"); + // We know it's directory-based because it returns some result for unknown + // paths. + EXPECT_EQ(r->Rlocation("unknown"), argv0 + ".runfiles/unknown"); + AssertEnvvars(*r, "", argv0 + ".runfiles"); +} + +TEST_F(RunfilesTest, CreatesDirectoryBasedRunfilesFromEnvvar) { + // We create a directory as a side-effect of creating a mock file. + unique_ptr mf( + MockFile::Create(string("foo" LINE_AS_STRING() ".runfiles/dummy"))); + string dir = mf->DirName(); + + string error; + unique_ptr r(Runfiles::Create("ignore-argv0", "", dir, &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + + EXPECT_EQ(r->Rlocation("a/b"), dir + "/a/b"); + EXPECT_EQ(r->Rlocation("foo"), dir + "/foo"); + EXPECT_EQ(r->Rlocation("/Foo"), "/Foo"); + EXPECT_EQ(r->Rlocation("c:/Foo"), "c:/Foo"); + EXPECT_EQ(r->Rlocation("c:\\Foo"), "c:\\Foo"); + AssertEnvvars(*r, "", dir); +} + +TEST_F(RunfilesTest, FailsToCreateAnyRunfilesBecauseEnvvarsAreNotDefined) { + unique_ptr mf( + MockFile::Create(string("foo" LINE_AS_STRING() ".runfiles/MANIFEST"))); + EXPECT_TRUE(mf != nullptr); + + string error; + unique_ptr r( + Runfiles::Create("ignore-argv0", mf->Path(), "whatever", &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + + // We create a directory as a side-effect of creating a mock file. + mf.reset(MockFile::Create(string("foo" LINE_AS_STRING() ".runfiles/dummy"))); + r.reset(Runfiles::Create("ignore-argv0", "", mf->DirName(), &error)); + ASSERT_NE(r, nullptr); + EXPECT_TRUE(error.empty()); + + r.reset(Runfiles::Create("ignore-argv0", "", "", &error)); + ASSERT_EQ(r, nullptr); + EXPECT_NE(error.find("cannot find runfiles"), string::npos); +} + +TEST_F(RunfilesTest, MockFileTest) { + { + unique_ptr mf( + MockFile::Create(string("foo" LINE_AS_STRING() "/.."))); + EXPECT_TRUE(mf == nullptr); + } + + { + unique_ptr mf(MockFile::Create(string("/Foo" LINE_AS_STRING()))); + EXPECT_TRUE(mf == nullptr); + } + + { + unique_ptr mf( + MockFile::Create(string("C:/Foo" LINE_AS_STRING()))); + EXPECT_TRUE(mf == nullptr); + } + + string path; + { + unique_ptr mf( + MockFile::Create(string("foo" LINE_AS_STRING() "/bar1/qux"))); + EXPECT_TRUE(mf != nullptr); + path = mf->Path(); + + std::ifstream stm(path); + EXPECT_TRUE(stm.good()); + string actual; + stm >> actual; + EXPECT_TRUE(actual.empty()); + } + { + std::ifstream stm(path); + EXPECT_FALSE(stm.good()); + } + + { + unique_ptr mf(MockFile::Create( + string("foo" LINE_AS_STRING() "/bar2/qux"), vector())); + EXPECT_TRUE(mf != nullptr); + path = mf->Path(); + + std::ifstream stm(path); + EXPECT_TRUE(stm.good()); + string actual; + stm >> actual; + EXPECT_TRUE(actual.empty()); + } + { + std::ifstream stm(path); + EXPECT_FALSE(stm.good()); + } + + { + unique_ptr mf( + MockFile::Create(string("foo" LINE_AS_STRING() "/bar3/qux"), + {"hello world", "you are beautiful"})); + EXPECT_TRUE(mf != nullptr); + path = mf->Path(); + + std::ifstream stm(path); + EXPECT_TRUE(stm.good()); + string actual; + std::getline(stm, actual); + EXPECT_EQ("hello world", actual); + std::getline(stm, actual); + EXPECT_EQ("you are beautiful", actual); + std::getline(stm, actual); + EXPECT_EQ("", actual); + } + { + std::ifstream stm(path); + EXPECT_FALSE(stm.good()); + } +} + +TEST_F(RunfilesTest, IsAbsolute) { + EXPECT_FALSE(TestOnly_IsAbsolute("foo")); + EXPECT_FALSE(TestOnly_IsAbsolute("foo/bar")); + EXPECT_FALSE(TestOnly_IsAbsolute("\\foo")); + EXPECT_TRUE(TestOnly_IsAbsolute("c:\\foo")); + EXPECT_TRUE(TestOnly_IsAbsolute("c:/foo")); + EXPECT_TRUE(TestOnly_IsAbsolute("/foo")); + EXPECT_TRUE(TestOnly_IsAbsolute("x:\\foo")); + EXPECT_FALSE(TestOnly_IsAbsolute("::\\foo")); + EXPECT_FALSE(TestOnly_IsAbsolute("x\\foo")); + EXPECT_FALSE(TestOnly_IsAbsolute("x:")); + EXPECT_TRUE(TestOnly_IsAbsolute("x:\\")); +} + +TEST_F(RunfilesTest, PathsFromEnvVars) { + string mf, dir; + + // Both envvars have a valid value. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "mock1/MANIFEST"; }, + [](const string& path) { return path == "mock2"; }, &mf, &dir)); + EXPECT_EQ(mf, "mock1/MANIFEST"); + EXPECT_EQ(dir, "mock2"); + + // RUNFILES_MANIFEST_FILE is invalid but RUNFILES_DIR is good and there's a + // runfiles manifest in the runfiles directory. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "mock2/MANIFEST"; }, + [](const string& path) { return path == "mock2"; }, &mf, &dir)); + EXPECT_EQ(mf, "mock2/MANIFEST"); + EXPECT_EQ(dir, "mock2"); + + // RUNFILES_MANIFEST_FILE is invalid but RUNFILES_DIR is good, but there's no + // runfiles manifest in the runfiles directory. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return false; }, + [](const string& path) { return path == "mock2"; }, &mf, &dir)); + EXPECT_EQ(mf, ""); + EXPECT_EQ(dir, "mock2"); + + // RUNFILES_DIR is invalid but RUNFILES_MANIFEST_FILE is good, and it is in + // a valid-looking runfiles directory. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "mock1/MANIFEST"; }, + [](const string& path) { return path == "mock1"; }, &mf, &dir)); + EXPECT_EQ(mf, "mock1/MANIFEST"); + EXPECT_EQ(dir, "mock1"); + + // RUNFILES_DIR is invalid but RUNFILES_MANIFEST_FILE is good, but it is not + // in any valid-looking runfiles directory. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "mock1/MANIFEST"; }, + [](const string& path) { return false; }, &mf, &dir)); + EXPECT_EQ(mf, "mock1/MANIFEST"); + EXPECT_EQ(dir, ""); + + // Both envvars are invalid, but there's a manifest in a runfiles directory + // next to argv0, however there's no other content in the runfiles directory. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "argv0.runfiles/MANIFEST"; }, + [](const string& path) { return false; }, &mf, &dir)); + EXPECT_EQ(mf, "argv0.runfiles/MANIFEST"); + EXPECT_EQ(dir, ""); + + // Both envvars are invalid, but there's a manifest next to argv0. There's + // no runfiles tree anywhere. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "argv0.runfiles_manifest"; }, + [](const string& path) { return false; }, &mf, &dir)); + EXPECT_EQ(mf, "argv0.runfiles_manifest"); + EXPECT_EQ(dir, ""); + + // Both envvars are invalid, but there's a valid manifest next to argv0, and a + // valid runfiles directory (without a manifest in it). + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "argv0.runfiles_manifest"; }, + [](const string& path) { return path == "argv0.runfiles"; }, &mf, &dir)); + EXPECT_EQ(mf, "argv0.runfiles_manifest"); + EXPECT_EQ(dir, "argv0.runfiles"); + + // Both envvars are invalid, but there's a valid runfiles directory next to + // argv0, though no manifest in it. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return false; }, + [](const string& path) { return path == "argv0.runfiles"; }, &mf, &dir)); + EXPECT_EQ(mf, ""); + EXPECT_EQ(dir, "argv0.runfiles"); + + // Both envvars are invalid, but there's a valid runfiles directory next to + // argv0 with a valid manifest in it. + EXPECT_TRUE(TestOnly_PathsFrom( + "argv0", "mock1/MANIFEST", "mock2", + [](const string& path) { return path == "argv0.runfiles/MANIFEST"; }, + [](const string& path) { return path == "argv0.runfiles"; }, &mf, &dir)); + EXPECT_EQ(mf, "argv0.runfiles/MANIFEST"); + EXPECT_EQ(dir, "argv0.runfiles"); +} + +} // namespace +} // namespace runfiles +} // namespace cpp +} // namespace tools +} // namespace bazel diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl new file mode 100644 index 0000000..58b31bb --- /dev/null +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -0,0 +1,567 @@ +# 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( + "@rules_cc//cc/private/toolchain: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", +) + +def _field(name, value): + """Returns properly indented top level crosstool field.""" + if type(value) == "list": + return "\n".join([" " + name + ": '" + v + "'" for v in value]) + elif type(value) == "string": + return " " + name + ": '" + value + "'" + else: + auto_configure_fail("Unexpected field type: " + type(value)) + return "" + +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 _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 _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.""" + 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() + + return [ + _prepare_include_path(repository_ctx, _cxx_inc_convert(p)) + for p in inc_dirs.split("\n") + ] + +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 + + # 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!""" + 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.""" + 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, + ), ":") + link_opts = split_escaped(get_env_var( + repository_ctx, + "BAZEL_LINKOPTS", + "-lstdc++:-lm", + False, + ), ":") + link_libs = split_escaped(get_env_var( + repository_ctx, + "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) + + repository_ctx.template( + "BUILD", + paths["@rules_cc//cc/private/toolchain:BUILD.tpl"], + { + "%{cc_toolchain_identifier}": cc_toolchain_identifier, + "%{name}": cpu_value, + "%{supports_param_files}": "0" if darwin else "1", + "%{cc_compiler_deps}": ":cc_wrapper" if darwin else ":empty", + "%{compiler}": escape_string(get_env_var( + repository_ctx, + "BAZEL_COMPILER", + "compiler", + False, + )), + "%{abi_version}": escape_string(get_env_var( + repository_ctx, + "ABI_VERSION", + "local", + False, + )), + "%{abi_libc_version}": escape_string(get_env_var( + repository_ctx, + "ABI_LIBC_VERSION", + "local", + False, + )), + "%{host_system_name}": escape_string(get_env_var( + repository_ctx, + "BAZEL_HOST_SYSTEM", + "local", + False, + )), + "%{target_libc}": "macosx" if darwin else escape_string(get_env_var( + repository_ctx, + "BAZEL_TARGET_LIBC", + "local", + False, + )), + "%{target_cpu}": escape_string(get_env_var( + repository_ctx, + "BAZEL_TARGET_CPU", + cpu_value, + 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()], + ), + "%{cxx_builtin_include_directories}": get_starlark_list( + _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), + ), + ), + ), + "%{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", + ], + ), + "%{cxx_flags}": get_starlark_list(cxx_opts + _escaped_cplus_include_paths(repository_ctx)), + "%{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), + "%{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", + ), + ), + "%{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\\\"", + ], + ), + "%{dbg_compile_flags}": get_starlark_list(["-g"]), + "%{coverage_compile_flags}": coverage_compile_flags, + "%{coverage_link_flags}": coverage_link_flags, + "%{supports_start_end_lib}": "True" if gold_linker_path else "False", + }, + ) diff --git a/cc/private/toolchain/unix_cc_toolchain_config.bzl b/cc/private/toolchain/unix_cc_toolchain_config.bzl new file mode 100644 index 0000000..0999ae6 --- /dev/null +++ b/cc/private/toolchain/unix_cc_toolchain_config.bzl @@ -0,0 +1,1133 @@ +# Copyright 2019 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. + +"""A Starlark cc_toolchain configuration rule""" + +load( + "@rules_cc//cc/private/toolchain:cc_toolchain_config_lib.bzl", + "feature", + "feature_set", + "flag_group", + "flag_set", + "tool_path", + "variable_with_value", + "with_feature_set", +) +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") + +all_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ACTION_NAMES.lto_backend, +] + +all_cpp_compile_actions = [ + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, +] + +preprocessor_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.clif_match, +] + +codegen_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.lto_backend, +] + +all_link_actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, +] + +lto_index_actions = [ + ACTION_NAMES.lto_index_for_executable, + ACTION_NAMES.lto_index_for_dynamic_library, + ACTION_NAMES.lto_index_for_nodeps_dynamic_library, +] + +def _impl(ctx): + tool_paths = [ + tool_path(name = name, path = path) + for name, path in ctx.attr.tool_paths.items() + ] + action_configs = [] + + supports_pic_feature = feature( + name = "supports_pic", + enabled = True, + ) + supports_start_end_lib_feature = feature( + name = "supports_start_end_lib", + enabled = True, + ) + + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = ctx.attr.compile_flags, + ), + ] if ctx.attr.compile_flags else []), + ), + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = ctx.attr.dbg_compile_flags, + ), + ] if ctx.attr.dbg_compile_flags else []), + with_features = [with_feature_set(features = ["dbg"])], + ), + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = ctx.attr.opt_compile_flags, + ), + ] if ctx.attr.opt_compile_flags else []), + with_features = [with_feature_set(features = ["opt"])], + ), + flag_set( + actions = all_cpp_compile_actions + [ACTION_NAMES.lto_backend], + flag_groups = ([ + flag_group( + flags = ctx.attr.cxx_flags, + ), + ] if ctx.attr.cxx_flags else []), + ), + ], + ) + + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = ([ + flag_group( + flags = ctx.attr.link_flags, + ), + ] if ctx.attr.link_flags else []), + ), + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = ([ + flag_group( + flags = ctx.attr.opt_link_flags, + ), + ] if ctx.attr.opt_link_flags else []), + with_features = [with_feature_set(features = ["opt"])], + ), + ], + ) + + dbg_feature = feature(name = "dbg") + + opt_feature = feature(name = "opt") + + sysroot_feature = feature( + name = "sysroot", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.lto_backend, + ACTION_NAMES.clif_match, + ] + all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + expand_if_available = "sysroot", + ), + ], + ), + ], + ) + + fdo_optimize_feature = feature( + name = "fdo_optimize", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [ + flag_group( + flags = [ + "-fprofile-use=%{fdo_profile_path}", + "-fprofile-correction", + ], + expand_if_available = "fdo_profile_path", + ), + ], + ), + ], + provides = ["profile"], + ) + + supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True) + + user_compile_flags_feature = feature( + name = "user_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + + unfiltered_compile_flags_feature = feature( + name = "unfiltered_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_compile_actions, + flag_groups = ([ + flag_group( + flags = ctx.attr.unfiltered_compile_flags, + ), + ] if ctx.attr.unfiltered_compile_flags else []), + ), + ], + ) + + library_search_directories_feature = feature( + name = "library_search_directories", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = ["-L%{library_search_directories}"], + iterate_over = "library_search_directories", + expand_if_available = "library_search_directories", + ), + ], + ), + ], + ) + + static_libgcc_feature = feature( + name = "static_libgcc", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.lto_index_for_executable, + ACTION_NAMES.lto_index_for_dynamic_library, + ], + flag_groups = [flag_group(flags = ["-static-libgcc"])], + with_features = [ + with_feature_set(features = ["static_link_cpp_runtimes"]), + ], + ), + ], + ) + + pic_feature = feature( + name = "pic", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group(flags = ["-fPIC"], expand_if_available = "pic"), + ], + ), + ], + ) + + per_object_debug_info_feature = feature( + name = "per_object_debug_info", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["-gsplit-dwarf"], + expand_if_available = "per_object_debug_info_file", + ), + ], + ), + ], + ) + + preprocessor_defines_feature = feature( + name = "preprocessor_defines", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["-D%{preprocessor_defines}"], + iterate_over = "preprocessor_defines", + ), + ], + ), + ], + ) + + cs_fdo_optimize_feature = feature( + name = "cs_fdo_optimize", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.lto_backend], + flag_groups = [ + flag_group( + flags = [ + "-fprofile-use=%{fdo_profile_path}", + "-Xclang-only=-Wno-profile-instr-unprofiled", + "-Xclang-only=-Wno-profile-instr-out-of-date", + "-fprofile-correction", + ], + expand_if_available = "fdo_profile_path", + ), + ], + ), + ], + provides = ["csprofile"], + ) + + autofdo_feature = feature( + name = "autofdo", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [ + flag_group( + flags = [ + "-fauto-profile=%{fdo_profile_path}", + "-fprofile-correction", + ], + expand_if_available = "fdo_profile_path", + ), + ], + ), + ], + provides = ["profile"], + ) + + runtime_library_search_directories_feature = feature( + name = "runtime_library_search_directories", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + iterate_over = "runtime_library_search_directories", + flag_groups = [ + flag_group( + flags = [ + "-Wl,-rpath,$EXEC_ORIGIN/%{runtime_library_search_directories}", + ], + expand_if_true = "is_cc_test", + ), + flag_group( + flags = [ + "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}", + ], + expand_if_false = "is_cc_test", + ), + ], + expand_if_available = + "runtime_library_search_directories", + ), + ], + with_features = [ + with_feature_set(features = ["static_link_cpp_runtimes"]), + ], + ), + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + iterate_over = "runtime_library_search_directories", + flag_groups = [ + flag_group( + flags = [ + "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}", + ], + ), + ], + expand_if_available = + "runtime_library_search_directories", + ), + ], + with_features = [ + with_feature_set( + not_features = ["static_link_cpp_runtimes"], + ), + ], + ), + ], + ) + + fission_support_feature = feature( + name = "fission_support", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = ["-Wl,--gdb-index"], + expand_if_available = "is_using_fission", + ), + ], + ), + ], + ) + + shared_flag_feature = feature( + name = "shared_flag", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ACTION_NAMES.lto_index_for_dynamic_library, + ACTION_NAMES.lto_index_for_nodeps_dynamic_library, + ], + flag_groups = [flag_group(flags = ["-shared"])], + ), + ], + ) + + random_seed_feature = feature( + name = "random_seed", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group( + flags = ["-frandom-seed=%{output_file}"], + expand_if_available = "output_file", + ), + ], + ), + ], + ) + + includes_feature = feature( + name = "includes", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.clif_match, + ACTION_NAMES.objc_compile, + ACTION_NAMES.objcpp_compile, + ], + flag_groups = [ + flag_group( + flags = ["-include", "%{includes}"], + iterate_over = "includes", + expand_if_available = "includes", + ), + ], + ), + ], + ) + + fdo_instrument_feature = feature( + name = "fdo_instrument", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ] + all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = [ + "-fprofile-generate=%{fdo_instrument_path}", + "-fno-data-sections", + ], + expand_if_available = "fdo_instrument_path", + ), + ], + ), + ], + provides = ["profile"], + ) + + cs_fdo_instrument_feature = feature( + name = "cs_fdo_instrument", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.lto_backend, + ] + all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = [ + "-fcs-profile-generate=%{cs_fdo_instrument_path}", + ], + expand_if_available = "cs_fdo_instrument_path", + ), + ], + ), + ], + provides = ["csprofile"], + ) + + include_paths_feature = feature( + name = "include_paths", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.clif_match, + ACTION_NAMES.objc_compile, + ACTION_NAMES.objcpp_compile, + ], + flag_groups = [ + flag_group( + flags = ["-iquote", "%{quote_include_paths}"], + iterate_over = "quote_include_paths", + ), + flag_group( + flags = ["-I%{include_paths}"], + iterate_over = "include_paths", + ), + flag_group( + flags = ["-isystem", "%{system_include_paths}"], + iterate_over = "system_include_paths", + ), + ], + ), + ], + ) + + symbol_counts_feature = feature( + name = "symbol_counts", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = [ + "-Wl,--print-symbol-counts=%{symbol_counts_output}", + ], + expand_if_available = "symbol_counts_output", + ), + ], + ), + ], + ) + + llvm_coverage_map_format_feature = feature( + name = "llvm_coverage_map_format", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.objc_compile, + ACTION_NAMES.objcpp_compile, + ], + flag_groups = [ + flag_group( + flags = [ + "-fprofile-instr-generate", + "-fcoverage-mapping", + ], + ), + ], + ), + flag_set( + actions = all_link_actions + lto_index_actions + [ + "objc-executable", + "objc++-executable", + ], + flag_groups = [ + flag_group(flags = ["-fprofile-instr-generate"]), + ], + ), + ], + requires = [feature_set(features = ["coverage"])], + provides = ["profile"], + ) + + strip_debug_symbols_feature = feature( + name = "strip_debug_symbols", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = ["-Wl,-S"], + expand_if_available = "strip_debug_symbols", + ), + ], + ), + ], + ) + + build_interface_libraries_feature = feature( + name = "build_interface_libraries", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ACTION_NAMES.lto_index_for_dynamic_library, + ACTION_NAMES.lto_index_for_nodeps_dynamic_library, + ], + flag_groups = [ + flag_group( + flags = [ + "%{generate_interface_library}", + "%{interface_library_builder_path}", + "%{interface_library_input_path}", + "%{interface_library_output_path}", + ], + expand_if_available = "generate_interface_library", + ), + ], + with_features = [ + with_feature_set( + features = ["supports_interface_shared_libraries"], + ), + ], + ), + ], + ) + + libraries_to_link_feature = feature( + name = "libraries_to_link", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["-Wl,--start-lib"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["-Wl,-whole-archive"], + expand_if_true = + "libraries_to_link.is_whole_archive", + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "interface_library", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + flag_group( + flags = ["-l%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "dynamic_library", + ), + ), + flag_group( + flags = ["-l:%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "versioned_dynamic_library", + ), + ), + flag_group( + flags = ["-Wl,-no-whole-archive"], + expand_if_true = "libraries_to_link.is_whole_archive", + ), + flag_group( + flags = ["-Wl,--end-lib"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + flag_group( + flags = ["-Wl,@%{thinlto_param_file}"], + expand_if_true = "thinlto_param_file", + ), + ], + ), + ], + ) + + user_link_flags_feature = feature( + name = "user_link_flags", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = ["%{user_link_flags}"], + iterate_over = "user_link_flags", + expand_if_available = "user_link_flags", + ), + ] + ([flag_group(flags = ctx.attr.link_libs)] if ctx.attr.link_libs else []), + ), + ], + ) + + fdo_prefetch_hints_feature = feature( + name = "fdo_prefetch_hints", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.lto_backend, + ], + flag_groups = [ + flag_group( + flags = [ + "-Xclang-only=-mllvm", + "-Xclang-only=-prefetch-hints-file=%{fdo_prefetch_hints_path}", + ], + expand_if_available = "fdo_prefetch_hints_path", + ), + ], + ), + ], + ) + + linkstamps_feature = feature( + name = "linkstamps", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = ["%{linkstamp_paths}"], + iterate_over = "linkstamp_paths", + expand_if_available = "linkstamp_paths", + ), + ], + ), + ], + ) + + gcc_coverage_map_format_feature = feature( + name = "gcc_coverage_map_format", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.objc_compile, + ACTION_NAMES.objcpp_compile, + "objc-executable", + "objc++-executable", + ], + flag_groups = [ + flag_group( + flags = ["-fprofile-arcs", "-ftest-coverage"], + expand_if_available = "gcov_gcno_file", + ), + ], + ), + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [flag_group(flags = ["--coverage"])], + ), + ], + requires = [feature_set(features = ["coverage"])], + provides = ["profile"], + ) + + archiver_flags_feature = feature( + name = "archiver_flags", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group(flags = ["rcsD"]), + flag_group( + flags = ["%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + ], + ) + + force_pic_flags_feature = feature( + name = "force_pic_flags", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.lto_index_for_executable, + ], + flag_groups = [ + flag_group( + flags = ["-pie"], + expand_if_available = "force_pic", + ), + ], + ), + ], + ) + + dependency_file_feature = feature( + name = "dependency_file", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.objc_compile, + ACTION_NAMES.objcpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["-MD", "-MF", "%{dependency_file}"], + expand_if_available = "dependency_file", + ), + ], + ), + ], + ) + + dynamic_library_linker_tool_path = tool_paths + dynamic_library_linker_tool_feature = feature( + name = "dynamic_library_linker_tool", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ACTION_NAMES.lto_index_for_dynamic_library, + ACTION_NAMES.lto_index_for_nodeps_dynamic_library, + ], + flag_groups = [ + flag_group( + flags = [" + cppLinkDynamicLibraryToolPath + "], + expand_if_available = "generate_interface_library", + ), + ], + with_features = [ + with_feature_set( + features = ["supports_interface_shared_libraries"], + ), + ], + ), + ], + ) + + output_execpath_flags_feature = feature( + name = "output_execpath_flags", + flag_sets = [ + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = [ + flag_group( + flags = ["-o", "%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + ], + ) + + # Note that we also set --coverage for c++-link-nodeps-dynamic-library. The + # generated code contains references to gcov symbols, and the dynamic linker + # can't resolve them unless the library is linked against gcov. + coverage_feature = feature( + name = "coverage", + provides = ["profile"], + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = ([ + flag_group(flags = ctx.attr.coverage_compile_flags), + ] if ctx.attr.coverage_compile_flags else []), + ), + flag_set( + actions = all_link_actions + lto_index_actions, + flag_groups = ([ + flag_group(flags = ctx.attr.coverage_link_flags), + ] if ctx.attr.coverage_link_flags else []), + ), + ], + ) + + is_linux = ctx.attr.target_libc != "macosx" + + # TODO(#8303): Mac crosstool should also declare every feature. + if is_linux: + features = [ + dependency_file_feature, + random_seed_feature, + pic_feature, + per_object_debug_info_feature, + preprocessor_defines_feature, + includes_feature, + include_paths_feature, + fdo_instrument_feature, + cs_fdo_instrument_feature, + cs_fdo_optimize_feature, + fdo_prefetch_hints_feature, + autofdo_feature, + build_interface_libraries_feature, + dynamic_library_linker_tool_feature, + symbol_counts_feature, + shared_flag_feature, + linkstamps_feature, + output_execpath_flags_feature, + runtime_library_search_directories_feature, + library_search_directories_feature, + archiver_flags_feature, + force_pic_flags_feature, + fission_support_feature, + strip_debug_symbols_feature, + coverage_feature, + supports_pic_feature, + ] + ( + [ + supports_start_end_lib_feature, + ] if ctx.attr.supports_start_end_lib else [] + ) + [ + default_compile_flags_feature, + default_link_flags_feature, + libraries_to_link_feature, + user_link_flags_feature, + static_libgcc_feature, + fdo_optimize_feature, + supports_dynamic_linker_feature, + dbg_feature, + opt_feature, + user_compile_flags_feature, + sysroot_feature, + unfiltered_compile_flags_feature, + ] + else: + features = [ + supports_pic_feature, + ] + ( + [ + supports_start_end_lib_feature, + ] if ctx.attr.supports_start_end_lib else [] + ) + [ + coverage_feature, + default_compile_flags_feature, + default_link_flags_feature, + fdo_optimize_feature, + supports_dynamic_linker_feature, + dbg_feature, + opt_feature, + user_compile_flags_feature, + sysroot_feature, + unfiltered_compile_flags_feature, + ] + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + action_configs = action_configs, + cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories, + toolchain_identifier = ctx.attr.toolchain_identifier, + host_system_name = ctx.attr.host_system_name, + target_system_name = ctx.attr.target_system_name, + target_cpu = ctx.attr.cpu, + target_libc = ctx.attr.target_libc, + compiler = ctx.attr.compiler, + abi_version = ctx.attr.abi_version, + abi_libc_version = ctx.attr.abi_libc_version, + tool_paths = tool_paths, + ) + +cc_toolchain_config = rule( + implementation = _impl, + attrs = { + "cpu": attr.string(mandatory = True), + "compiler": attr.string(mandatory = True), + "toolchain_identifier": attr.string(mandatory = True), + "host_system_name": attr.string(mandatory = True), + "target_system_name": attr.string(mandatory = True), + "target_libc": attr.string(mandatory = True), + "abi_version": attr.string(mandatory = True), + "abi_libc_version": attr.string(mandatory = True), + "cxx_builtin_include_directories": attr.string_list(), + "tool_paths": attr.string_dict(), + "compile_flags": attr.string_list(), + "dbg_compile_flags": attr.string_list(), + "opt_compile_flags": attr.string_list(), + "cxx_flags": attr.string_list(), + "link_flags": attr.string_list(), + "link_libs": attr.string_list(), + "opt_link_flags": attr.string_list(), + "unfiltered_compile_flags": attr.string_list(), + "coverage_compile_flags": attr.string_list(), + "coverage_link_flags": attr.string_list(), + "supports_start_end_lib": attr.bool(), + }, + provides = [CcToolchainConfigInfo], +) diff --git a/cc/private/toolchain/vc_installation_error.bat.tpl b/cc/private/toolchain/vc_installation_error.bat.tpl new file mode 100644 index 0000000..b796585 --- /dev/null +++ b/cc/private/toolchain/vc_installation_error.bat.tpl @@ -0,0 +1,24 @@ +:: Copyright 2017 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. + +@echo OFF + +echo. 1>&2 +echo The target you are compiling requires Visual C++ build tools. 1>&2 +echo Bazel couldn't find a valid Visual C++ build tools installation on your machine. 1>&2 +%{vc_error_message} +echo Please check your installation following https://docs.bazel.build/versions/master/windows.html#using 1>&2 +echo. 1>&2 + +exit /b 1 diff --git a/cc/private/toolchain/windows_cc_configure.bzl b/cc/private/toolchain/windows_cc_configure.bzl new file mode 100644 index 0000000..a151beb --- /dev/null +++ b/cc/private/toolchain/windows_cc_configure.bzl @@ -0,0 +1,692 @@ +# 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 Windows.""" + +load( + "@rules_cc//cc/private/toolchain:lib_cc_configure.bzl", + "auto_configure_fail", + "auto_configure_warning", + "auto_configure_warning_maybe", + "escape_string", + "execute", + "resolve_labels", +) + +def _get_path_env_var(repository_ctx, name): + """Returns a path from an environment variable. + + Removes quotes, replaces '/' with '\', and strips trailing '\'s.""" + if name in repository_ctx.os.environ: + value = repository_ctx.os.environ[name] + if value[0] == "\"": + if len(value) == 1 or value[-1] != "\"": + auto_configure_fail("'%s' environment variable has no trailing quote" % name) + value = value[1:-1] + if "/" in value: + value = value.replace("/", "\\") + if value[-1] == "\\": + value = value.rstrip("\\") + return value + else: + return None + +def _get_temp_env(repository_ctx): + """Returns the value of TMP, or TEMP, or if both undefined then C:\\Windows.""" + tmp = _get_path_env_var(repository_ctx, "TMP") + if not tmp: + tmp = _get_path_env_var(repository_ctx, "TEMP") + if not tmp: + tmp = "C:\\Windows\\Temp" + auto_configure_warning( + "neither 'TMP' nor 'TEMP' environment variables are set, using '%s' as default" % tmp, + ) + return tmp + +def _get_escaped_windows_msys_starlark_content(repository_ctx, use_mingw = False): + """Return the content of msys cc toolchain rule.""" + msys_root = "" + bazel_sh = _get_path_env_var(repository_ctx, "BAZEL_SH") + if bazel_sh: + bazel_sh = bazel_sh.replace("\\", "/").lower() + tokens = bazel_sh.rsplit("/", 1) + if tokens[0].endswith("/usr/bin"): + msys_root = tokens[0][:len(tokens[0]) - len("usr/bin")] + elif tokens[0].endswith("/bin"): + msys_root = tokens[0][:len(tokens[0]) - len("bin")] + + prefix = "mingw64" if use_mingw else "usr" + tool_path_prefix = escape_string(msys_root) + prefix + tool_bin_path = tool_path_prefix + "/bin" + tool_path = {} + + for tool in ["ar", "compat-ld", "cpp", "dwp", "gcc", "gcov", "ld", "nm", "objcopy", "objdump", "strip"]: + if msys_root: + tool_path[tool] = tool_bin_path + "/" + tool + else: + tool_path[tool] = "msys_gcc_installation_error.bat" + tool_paths = ",\n ".join(['"%s": "%s"' % (k, v) for k, v in tool_path.items()]) + include_directories = (' "%s/",\n ' % tool_path_prefix) if msys_root else "" + return tool_paths, tool_bin_path, include_directories + +def _get_system_root(repository_ctx): + """Get System root path on Windows, default is C:\\Windows. Doesn't %-escape the result.""" + systemroot = _get_path_env_var(repository_ctx, "SYSTEMROOT") + if not systemroot: + systemroot = "C:\\Windows" + auto_configure_warning_maybe( + repository_ctx, + "SYSTEMROOT is not set, using default SYSTEMROOT=C:\\Windows", + ) + return escape_string(systemroot) + +def _add_system_root(repository_ctx, env): + """Running VCVARSALL.BAT and VCVARSQUERYREGISTRY.BAT need %SYSTEMROOT%\\\\system32 in PATH.""" + if "PATH" not in env: + env["PATH"] = "" + env["PATH"] = env["PATH"] + ";" + _get_system_root(repository_ctx) + "\\system32" + return env + +def find_vc_path(repository_ctx): + """Find Visual C++ build tools install path. Doesn't %-escape the result.""" + + # 1. Check if BAZEL_VC or BAZEL_VS is already set by user. + bazel_vc = _get_path_env_var(repository_ctx, "BAZEL_VC") + if bazel_vc: + if repository_ctx.path(bazel_vc).exists: + return bazel_vc + else: + auto_configure_warning_maybe( + repository_ctx, + "%BAZEL_VC% is set to non-existent path, ignoring.", + ) + + bazel_vs = _get_path_env_var(repository_ctx, "BAZEL_VS") + if bazel_vs: + if repository_ctx.path(bazel_vs).exists: + bazel_vc = bazel_vs + "\\VC" + if repository_ctx.path(bazel_vc).exists: + return bazel_vc + else: + auto_configure_warning_maybe( + repository_ctx, + "No 'VC' directory found under %BAZEL_VS%, ignoring.", + ) + else: + auto_configure_warning_maybe( + repository_ctx, + "%BAZEL_VS% is set to non-existent path, ignoring.", + ) + + auto_configure_warning_maybe( + repository_ctx, + "Neither %BAZEL_VC% nor %BAZEL_VS% are set, start looking for the latest Visual C++" + + " installed.", + ) + + # 2. Check if VS%VS_VERSION%COMNTOOLS is set, if true then try to find and use + # vcvarsqueryregistry.bat / VsDevCmd.bat to detect VC++. + auto_configure_warning_maybe(repository_ctx, "Looking for VS%VERSION%COMNTOOLS environment variables, " + + "eg. VS140COMNTOOLS") + for vscommontools_env, script in [ + ("VS160COMNTOOLS", "VsDevCmd.bat"), + ("VS150COMNTOOLS", "VsDevCmd.bat"), + ("VS140COMNTOOLS", "vcvarsqueryregistry.bat"), + ("VS120COMNTOOLS", "vcvarsqueryregistry.bat"), + ("VS110COMNTOOLS", "vcvarsqueryregistry.bat"), + ("VS100COMNTOOLS", "vcvarsqueryregistry.bat"), + ("VS90COMNTOOLS", "vcvarsqueryregistry.bat"), + ]: + if vscommontools_env not in repository_ctx.os.environ: + continue + script = _get_path_env_var(repository_ctx, vscommontools_env) + "\\" + script + if not repository_ctx.path(script).exists: + continue + repository_ctx.file( + "get_vc_dir.bat", + "@echo off\n" + + "call \"" + script + "\"\n" + + "echo %VCINSTALLDIR%", + True, + ) + env = _add_system_root(repository_ctx, repository_ctx.os.environ) + vc_dir = execute(repository_ctx, ["./get_vc_dir.bat"], environment = env) + + auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools found at %s" % vc_dir) + return vc_dir + + # 3. User might have purged all environment variables. If so, look for Visual C++ in registry. + # Works for Visual Studio 2017 and older. (Does not work for Visual Studio 2019 Preview.) + # TODO(laszlocsomor): check if "16.0" also has this registry key, after VS 2019 is released. + auto_configure_warning_maybe(repository_ctx, "Looking for Visual C++ through registry") + reg_binary = _get_system_root(repository_ctx) + "\\system32\\reg.exe" + vc_dir = None + for key, suffix in (("VC7", ""), ("VS7", "\\VC")): + for version in ["15.0", "14.0", "12.0", "11.0", "10.0", "9.0", "8.0"]: + if vc_dir: + break + result = repository_ctx.execute([reg_binary, "query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\" + key, "/v", version]) + auto_configure_warning_maybe(repository_ctx, "registry query result for VC %s:\n\nSTDOUT(start)\n%s\nSTDOUT(end)\nSTDERR(start):\n%s\nSTDERR(end)\n" % + (version, result.stdout, result.stderr)) + if not result.stderr: + for line in result.stdout.split("\n"): + line = line.strip() + if line.startswith(version) and line.find("REG_SZ") != -1: + vc_dir = line[line.find("REG_SZ") + len("REG_SZ"):].strip() + suffix + if vc_dir: + auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools found at %s" % vc_dir) + return vc_dir + + # 4. Check default directories for VC installation + auto_configure_warning_maybe(repository_ctx, "Looking for default Visual C++ installation directory") + program_files_dir = _get_path_env_var(repository_ctx, "PROGRAMFILES(X86)") + if not program_files_dir: + program_files_dir = "C:\\Program Files (x86)" + auto_configure_warning_maybe( + repository_ctx, + "'PROGRAMFILES(X86)' environment variable is not set, using '%s' as default" % program_files_dir, + ) + for path in [ + "Microsoft Visual Studio\\2019\\Preview\\VC", + "Microsoft Visual Studio\\2019\\BuildTools\\VC", + "Microsoft Visual Studio\\2019\\Community\\VC", + "Microsoft Visual Studio\\2019\\Professional\\VC", + "Microsoft Visual Studio\\2019\\Enterprise\\VC", + "Microsoft Visual Studio\\2017\\BuildTools\\VC", + "Microsoft Visual Studio\\2017\\Community\\VC", + "Microsoft Visual Studio\\2017\\Professional\\VC", + "Microsoft Visual Studio\\2017\\Enterprise\\VC", + "Microsoft Visual Studio 14.0\\VC", + ]: + path = program_files_dir + "\\" + path + if repository_ctx.path(path).exists: + vc_dir = path + break + + if not vc_dir: + auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools not found.") + return None + auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools found at %s" % vc_dir) + return vc_dir + +def _is_vs_2017_or_2019(vc_path): + """Check if the installed VS version is Visual Studio 2017.""" + + # In VS 2017 and 2019, the location of VC is like: + # C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\ + # In VS 2015 or older version, it is like: + # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\ + return vc_path.find("2017") != -1 or vc_path.find("2019") != -1 + +def _find_vcvars_bat_script(repository_ctx, vc_path): + """Find batch script to set up environment variables for VC. Doesn't %-escape the result.""" + if _is_vs_2017_or_2019(vc_path): + vcvars_script = vc_path + "\\Auxiliary\\Build\\VCVARSALL.BAT" + else: + vcvars_script = vc_path + "\\VCVARSALL.BAT" + + if not repository_ctx.path(vcvars_script).exists: + return None + + return vcvars_script + +def _is_support_vcvars_ver(vc_full_version): + """-vcvars_ver option is supported from version 14.11.25503 (VS 2017 version 15.3).""" + version = [int(i) for i in vc_full_version.split(".")] + min_version = [14, 11, 25503] + return version >= min_version + +def _is_support_winsdk_selection(repository_ctx, vc_path): + """Windows SDK selection is supported with VC 2017 / 2019 or with full VS 2015 installation.""" + if _is_vs_2017_or_2019(vc_path): + return True + + # By checking the source code of VCVARSALL.BAT in VC 2015, we know that + # when devenv.exe or wdexpress.exe exists, VCVARSALL.BAT supports Windows SDK selection. + vc_common_ide = repository_ctx.path(vc_path).dirname.get_child("Common7").get_child("IDE") + for tool in ["devenv.exe", "wdexpress.exe"]: + if vc_common_ide.get_child(tool).exists: + return True + return False + +def setup_vc_env_vars(repository_ctx, vc_path, envvars = [], allow_empty = False, escape = True): + """Get environment variables set by VCVARSALL.BAT script. Doesn't %-escape the result! + + Args: + repository_ctx: the repository_ctx object + vc_path: Visual C++ root directory + envvars: list of envvars to retrieve; default is ["PATH", "INCLUDE", "LIB", "WINDOWSSDKDIR"] + allow_empty: allow unset envvars; if False then report errors for those + escape: if True, escape "\" as "\\" and "%" as "%%" in the envvar values + + Returns: + dictionary of the envvars + """ + if not envvars: + envvars = ["PATH", "INCLUDE", "LIB", "WINDOWSSDKDIR"] + + vcvars_script = _find_vcvars_bat_script(repository_ctx, vc_path) + if not vcvars_script: + auto_configure_fail("Cannot find VCVARSALL.BAT script under %s" % vc_path) + + # Getting Windows SDK version set by user. + # Only supports VC 2017 & 2019 and VC 2015 with full VS installation. + winsdk_version = _get_winsdk_full_version(repository_ctx) + if winsdk_version and not _is_support_winsdk_selection(repository_ctx, vc_path): + auto_configure_warning(("BAZEL_WINSDK_FULL_VERSION=%s is ignored, " + + "because standalone Visual C++ Build Tools 2015 doesn't support specifying Windows " + + "SDK version, please install the full VS 2015 or use VC 2017/2019.") % winsdk_version) + winsdk_version = "" + + # Get VC version set by user. Only supports VC 2017 & 2019. + vcvars_ver = "" + if _is_vs_2017_or_2019(vc_path): + full_version = _get_vc_full_version(repository_ctx, vc_path) + + # Because VCVARSALL.BAT is from the latest VC installed, so we check if the latest + # version supports -vcvars_ver or not. + if _is_support_vcvars_ver(_get_latest_subversion(repository_ctx, vc_path)): + vcvars_ver = "-vcvars_ver=" + full_version + + cmd = "\"%s\" amd64 %s %s" % (vcvars_script, winsdk_version, vcvars_ver) + print_envvars = ",".join(["{k}=%{k}%".format(k = k) for k in envvars]) + repository_ctx.file( + "get_env.bat", + "@echo off\n" + + ("call %s > NUL \n" % cmd) + ("echo %s \n" % print_envvars), + True, + ) + env = _add_system_root(repository_ctx, {k: "" for k in envvars}) + envs = execute(repository_ctx, ["./get_env.bat"], environment = env).split(",") + env_map = {} + for env in envs: + key, value = env.split("=", 1) + env_map[key] = escape_string(value.replace("\\", "\\\\")) if escape else value + if not allow_empty: + _check_env_vars(env_map, cmd, expected = envvars) + return env_map + +def _check_env_vars(env_map, cmd, expected): + for env in expected: + if not env_map.get(env): + auto_configure_fail( + "Setting up VC environment variables failed, %s is not set by the following command:\n %s" % (env, cmd), + ) + +def _get_latest_subversion(repository_ctx, vc_path): + """Get the latest subversion of a VS 2017/2019 installation. + + For VS 2017 & 2019, there could be multiple versions of VC build tools. + The directories are like: + \\Tools\\MSVC\\14.10.24930\\bin\\HostX64\\x64 + \\Tools\\MSVC\\14.16.27023\\bin\\HostX64\\x64 + This function should return 14.16.27023 in this case.""" + versions = [path.basename for path in repository_ctx.path(vc_path + "\\Tools\\MSVC").readdir()] + if len(versions) < 1: + auto_configure_warning_maybe(repository_ctx, "Cannot find any VC installation under BAZEL_VC(%s)" % vc_path) + return None + + # Parse the version string into integers, then sort the integers to prevent textual sorting. + version_list = [] + for version in versions: + parts = [int(i) for i in version.split(".")] + version_list.append((parts, version)) + + version_list = sorted(version_list) + latest_version = version_list[-1][1] + + auto_configure_warning_maybe(repository_ctx, "Found the following VC verisons:\n%s\n\nChoosing the latest version = %s" % ("\n".join(versions), latest_version)) + return latest_version + +def _get_vc_full_version(repository_ctx, vc_path): + """Return the value of BAZEL_VC_FULL_VERSION if defined, otherwise the latest version.""" + if "BAZEL_VC_FULL_VERSION" in repository_ctx.os.environ: + return repository_ctx.os.environ["BAZEL_VC_FULL_VERSION"] + return _get_latest_subversion(repository_ctx, vc_path) + +def _get_winsdk_full_version(repository_ctx): + """Return the value of BAZEL_WINSDK_FULL_VERSION if defined, otherwise an empty string.""" + return repository_ctx.os.environ.get("BAZEL_WINSDK_FULL_VERSION", default = "") + +def find_msvc_tool(repository_ctx, vc_path, tool): + """Find the exact path of a specific build tool in MSVC. Doesn't %-escape the result.""" + tool_path = None + if _is_vs_2017_or_2019(vc_path): + full_version = _get_vc_full_version(repository_ctx, vc_path) + if full_version: + tool_path = "%s\\Tools\\MSVC\\%s\\bin\\HostX64\\x64\\%s" % (vc_path, full_version, tool) + else: + # For VS 2015 and older version, the tools are under: + # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64 + tool_path = vc_path + "\\bin\\amd64\\" + tool + + if not tool_path or not repository_ctx.path(tool_path).exists: + return None + + return tool_path.replace("\\", "/") + +def _find_missing_vc_tools(repository_ctx, vc_path): + """Check if any required tool is missing under given VC path.""" + missing_tools = [] + if not _find_vcvars_bat_script(repository_ctx, vc_path): + missing_tools.append("VCVARSALL.BAT") + + for tool in ["cl.exe", "link.exe", "lib.exe", "ml64.exe"]: + if not find_msvc_tool(repository_ctx, vc_path, tool): + missing_tools.append(tool) + + return missing_tools + +def _is_support_debug_fastlink(repository_ctx, linker): + """Run linker alone to see if it supports /DEBUG:FASTLINK.""" + if _use_clang_cl(repository_ctx): + # LLVM's lld-link.exe doesn't support /DEBUG:FASTLINK. + return False + result = execute(repository_ctx, [linker], expect_failure = True) + return result.find("/DEBUG[:{FASTLINK|FULL|NONE}]") != -1 + +def find_llvm_path(repository_ctx): + """Find LLVM install path.""" + + # 1. Check if BAZEL_LLVM is already set by user. + bazel_llvm = _get_path_env_var(repository_ctx, "BAZEL_LLVM") + if bazel_llvm: + return bazel_llvm + + auto_configure_warning_maybe(repository_ctx, "'BAZEL_LLVM' is not set, " + + "start looking for LLVM installation on machine.") + + # 2. Look for LLVM installation through registry. + auto_configure_warning_maybe(repository_ctx, "Looking for LLVM installation through registry") + reg_binary = _get_system_root(repository_ctx) + "\\system32\\reg.exe" + llvm_dir = None + result = repository_ctx.execute([reg_binary, "query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM"]) + auto_configure_warning_maybe(repository_ctx, "registry query result for LLVM:\n\nSTDOUT(start)\n%s\nSTDOUT(end)\nSTDERR(start):\n%s\nSTDERR(end)\n" % + (result.stdout, result.stderr)) + if not result.stderr: + for line in result.stdout.split("\n"): + line = line.strip() + if line.startswith("(Default)") and line.find("REG_SZ") != -1: + llvm_dir = line[line.find("REG_SZ") + len("REG_SZ"):].strip() + if llvm_dir: + auto_configure_warning_maybe(repository_ctx, "LLVM installation found at %s" % llvm_dir) + return llvm_dir + + # 3. Check default directories for LLVM installation + auto_configure_warning_maybe(repository_ctx, "Looking for default LLVM installation directory") + program_files_dir = _get_path_env_var(repository_ctx, "PROGRAMFILES") + if not program_files_dir: + program_files_dir = "C:\\Program Files" + auto_configure_warning_maybe( + repository_ctx, + "'PROGRAMFILES' environment variable is not set, using '%s' as default" % program_files_dir, + ) + path = program_files_dir + "\\LLVM" + if repository_ctx.path(path).exists: + llvm_dir = path + + if not llvm_dir: + auto_configure_warning_maybe(repository_ctx, "LLVM installation not found.") + return None + auto_configure_warning_maybe(repository_ctx, "LLVM installation found at %s" % llvm_dir) + return llvm_dir + +def find_llvm_tool(repository_ctx, llvm_path, tool): + """Find the exact path of a specific build tool in LLVM. Doesn't %-escape the result.""" + tool_path = llvm_path + "\\bin\\" + tool + + if not repository_ctx.path(tool_path).exists: + return None + + return tool_path.replace("\\", "/") + +def _use_clang_cl(repository_ctx): + """Returns True if USE_CLANG_CL is set to 1.""" + return repository_ctx.os.environ.get("USE_CLANG_CL", default = "0") == "1" + +def _find_missing_llvm_tools(repository_ctx, llvm_path): + """Check if any required tool is missing under given LLVM path.""" + missing_tools = [] + for tool in ["clang-cl.exe", "lld-link.exe", "llvm-lib.exe"]: + if not find_llvm_tool(repository_ctx, llvm_path, tool): + missing_tools.append(tool) + + return missing_tools + +def _get_clang_version(repository_ctx, clang_cl): + result = repository_ctx.execute([clang_cl, "-v"]) + if result.return_code != 0: + auto_configure_fail("Failed to get clang version by running \"%s -v\"" % clang_cl) + + # Stderr should look like "clang version X.X.X ..." + return result.stderr.strip().split(" ")[2] + +def _get_msys_mingw_vars(repository_ctx): + """Get the variables we need to populate the msys/mingw toolchains.""" + tool_paths, tool_bin_path, inc_dir_msys = _get_escaped_windows_msys_starlark_content(repository_ctx) + tool_paths_mingw, tool_bin_path_mingw, inc_dir_mingw = _get_escaped_windows_msys_starlark_content(repository_ctx, use_mingw = True) + msys_mingw_vars = { + "%{cxx_builtin_include_directories}": inc_dir_msys, + "%{mingw_cxx_builtin_include_directories}": inc_dir_mingw, + "%{tool_paths}": tool_paths, + "%{mingw_tool_paths}": tool_paths_mingw, + "%{tool_bin_path}": tool_bin_path, + "%{mingw_tool_bin_path}": tool_bin_path_mingw, + } + return msys_mingw_vars + +def _get_msvc_vars(repository_ctx, paths): + """Get the variables we need to populate the MSVC toolchains.""" + msvc_vars = dict() + vc_path = find_vc_path(repository_ctx) + missing_tools = None + if not vc_path: + repository_ctx.template( + "vc_installation_error.bat", + paths["@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"], + {"%{vc_error_message}": ""}, + ) + else: + missing_tools = _find_missing_vc_tools(repository_ctx, vc_path) + if missing_tools: + message = "\r\n".join([ + "echo. 1>&2", + "echo Visual C++ build tools seems to be installed at %s 1>&2" % vc_path, + "echo But Bazel can't find the following tools: 1>&2", + "echo %s 1>&2" % ", ".join(missing_tools), + "echo. 1>&2", + ]) + repository_ctx.template( + "vc_installation_error.bat", + paths["@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"], + {"%{vc_error_message}": message}, + ) + + if not vc_path or missing_tools: + msvc_vars = { + "%{msvc_env_tmp}": "msvc_not_found", + "%{msvc_env_path}": "msvc_not_found", + "%{msvc_env_include}": "msvc_not_found", + "%{msvc_env_lib}": "msvc_not_found", + "%{msvc_cl_path}": "vc_installation_error.bat", + "%{msvc_ml_path}": "vc_installation_error.bat", + "%{msvc_link_path}": "vc_installation_error.bat", + "%{msvc_lib_path}": "vc_installation_error.bat", + "%{dbg_mode_debug_flag}": "/DEBUG", + "%{fastbuild_mode_debug_flag}": "/DEBUG", + "%{msvc_cxx_builtin_include_directories}": "", + } + return msvc_vars + + env = setup_vc_env_vars(repository_ctx, vc_path) + escaped_paths = escape_string(env["PATH"]) + escaped_include_paths = escape_string(env["INCLUDE"]) + escaped_lib_paths = escape_string(env["LIB"]) + escaped_tmp_dir = escape_string(_get_temp_env(repository_ctx).replace("\\", "\\\\")) + + llvm_path = "" + if _use_clang_cl(repository_ctx): + llvm_path = find_llvm_path(repository_ctx) + if not llvm_path: + auto_configure_fail("\nUSE_CLANG_CL is set to 1, but Bazel cannot find Clang installation on your system.\n" + + "Please install Clang via http://releases.llvm.org/download.html\n") + cl_path = find_llvm_tool(repository_ctx, llvm_path, "clang-cl.exe") + link_path = find_llvm_tool(repository_ctx, llvm_path, "lld-link.exe") + if not link_path: + link_path = find_msvc_tool(repository_ctx, vc_path, "link.exe") + lib_path = find_llvm_tool(repository_ctx, llvm_path, "llvm-lib.exe") + if not lib_path: + lib_path = find_msvc_tool(repository_ctx, vc_path, "lib.exe") + else: + cl_path = find_msvc_tool(repository_ctx, vc_path, "cl.exe") + link_path = find_msvc_tool(repository_ctx, vc_path, "link.exe") + lib_path = find_msvc_tool(repository_ctx, vc_path, "lib.exe") + + msvc_ml_path = find_msvc_tool(repository_ctx, vc_path, "ml64.exe") + escaped_cxx_include_directories = [] + + for path in escaped_include_paths.split(";"): + if path: + escaped_cxx_include_directories.append("\"%s\"" % path) + if llvm_path: + clang_version = _get_clang_version(repository_ctx, cl_path) + clang_dir = llvm_path + "\\lib\\clang\\" + clang_version + clang_include_path = (clang_dir + "\\include").replace("\\", "\\\\") + escaped_cxx_include_directories.append("\"%s\"" % clang_include_path) + clang_lib_path = (clang_dir + "\\lib\\windows").replace("\\", "\\\\") + escaped_lib_paths = escaped_lib_paths + ";" + clang_lib_path + + support_debug_fastlink = _is_support_debug_fastlink(repository_ctx, link_path) + + msvc_vars = { + "%{msvc_env_tmp}": escaped_tmp_dir, + "%{msvc_env_path}": escaped_paths, + "%{msvc_env_include}": escaped_include_paths, + "%{msvc_env_lib}": escaped_lib_paths, + "%{msvc_cl_path}": cl_path, + "%{msvc_ml_path}": msvc_ml_path, + "%{msvc_link_path}": link_path, + "%{msvc_lib_path}": lib_path, + "%{dbg_mode_debug_flag}": "/DEBUG:FULL" if support_debug_fastlink else "/DEBUG", + "%{fastbuild_mode_debug_flag}": "/DEBUG:FASTLINK" if support_debug_fastlink else "/DEBUG", + "%{msvc_cxx_builtin_include_directories}": " " + ",\n ".join(escaped_cxx_include_directories), + } + return msvc_vars + +def _get_clang_cl_vars(repository_ctx, paths, msvc_vars): + """Get the variables we need to populate the clang-cl toolchains.""" + llvm_path = find_llvm_path(repository_ctx) + error_script = None + if msvc_vars["%{msvc_cl_path}"] == "vc_installation_error.bat": + error_script = "vc_installation_error.bat" + elif not llvm_path: + repository_ctx.template( + "clang_installation_error.bat", + paths["@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"], + {"%{clang_error_message}": ""}, + ) + error_script = "clang_installation_error.bat" + else: + missing_tools = _find_missing_llvm_tools(repository_ctx, llvm_path) + if missing_tools: + message = "\r\n".join([ + "echo. 1>&2", + "echo LLVM/Clang seems to be installed at %s 1>&2" % llvm_path, + "echo But Bazel can't find the following tools: 1>&2", + "echo %s 1>&2" % ", ".join(missing_tools), + "echo. 1>&2", + ]) + repository_ctx.template( + "clang_installation_error.bat", + paths["@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"], + {"%{clang_error_message}": message}, + ) + error_script = "clang_installation_error.bat" + + if error_script: + clang_cl_vars = { + "%{clang_cl_env_tmp}": "clang_cl_not_found", + "%{clang_cl_env_path}": "clang_cl_not_found", + "%{clang_cl_env_include}": "clang_cl_not_found", + "%{clang_cl_env_lib}": "clang_cl_not_found", + "%{clang_cl_cl_path}": error_script, + "%{clang_cl_link_path}": error_script, + "%{clang_cl_lib_path}": error_script, + "%{clang_cl_ml_path}": error_script, + "%{clang_cl_dbg_mode_debug_flag}": "/DEBUG", + "%{clang_cl_fastbuild_mode_debug_flag}": "/DEBUG", + "%{clang_cl_cxx_builtin_include_directories}": "", + } + return clang_cl_vars + + clang_cl_path = find_llvm_tool(repository_ctx, llvm_path, "clang-cl.exe") + lld_link_path = find_llvm_tool(repository_ctx, llvm_path, "lld-link.exe") + llvm_lib_path = find_llvm_tool(repository_ctx, llvm_path, "llvm-lib.exe") + + clang_version = _get_clang_version(repository_ctx, clang_cl_path) + clang_dir = llvm_path + "\\lib\\clang\\" + clang_version + clang_include_path = (clang_dir + "\\include").replace("\\", "\\\\") + clang_lib_path = (clang_dir + "\\lib\\windows").replace("\\", "\\\\") + + clang_cl_vars = { + "%{clang_cl_env_tmp}": msvc_vars["%{msvc_env_tmp}"], + "%{clang_cl_env_path}": msvc_vars["%{msvc_env_path}"], + "%{clang_cl_env_include}": msvc_vars["%{msvc_env_include}"] + ";" + clang_include_path, + "%{clang_cl_env_lib}": msvc_vars["%{msvc_env_lib}"] + ";" + clang_lib_path, + "%{clang_cl_cxx_builtin_include_directories}": msvc_vars["%{msvc_cxx_builtin_include_directories}"] + (",\n \"%s\"" % clang_include_path), + "%{clang_cl_cl_path}": clang_cl_path, + "%{clang_cl_link_path}": lld_link_path, + "%{clang_cl_lib_path}": llvm_lib_path, + "%{clang_cl_ml_path}": msvc_vars["%{msvc_ml_path}"], + # LLVM's lld-link.exe doesn't support /DEBUG:FASTLINK. + "%{clang_cl_dbg_mode_debug_flag}": "/DEBUG", + "%{clang_cl_fastbuild_mode_debug_flag}": "/DEBUG", + } + return clang_cl_vars + +def configure_windows_toolchain(repository_ctx): + """Configure C++ toolchain on Windows.""" + paths = resolve_labels(repository_ctx, [ + "@rules_cc//cc/private/toolchain:BUILD.windows.tpl", + "@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl", + "@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl", + "@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl", + "@rules_cc//cc/private/toolchain:msys_gcc_installation_error.bat", + "@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl", + ]) + + repository_ctx.symlink( + paths["@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl"], + "windows_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.symlink( + paths["@rules_cc//cc/private/toolchain:msys_gcc_installation_error.bat"], + "msys_gcc_installation_error.bat", + ) + + template_vars = dict() + msvc_vars = _get_msvc_vars(repository_ctx, paths) + template_vars.update(msvc_vars) + template_vars.update(_get_clang_cl_vars(repository_ctx, paths, msvc_vars)) + template_vars.update(_get_msys_mingw_vars(repository_ctx)) + + repository_ctx.template( + "BUILD", + paths["@rules_cc//cc/private/toolchain:BUILD.windows.tpl"], + template_vars, + ) diff --git a/cc/private/toolchain/windows_cc_toolchain_config.bzl b/cc/private/toolchain/windows_cc_toolchain_config.bzl new file mode 100644 index 0000000..6c02b5c --- /dev/null +++ b/cc/private/toolchain/windows_cc_toolchain_config.bzl @@ -0,0 +1,1342 @@ +# Copyright 2019 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. + +"""A Starlark cc_toolchain configuration rule for Windows""" + +load( + "@rules_cc//cc/private/toolchain:cc_toolchain_config_lib.bzl", + "action_config", + "artifact_name_pattern", + "env_entry", + "env_set", + "feature", + "feature_set", + "flag_group", + "flag_set", + "tool", + "tool_path", + "variable_with_value", + "with_feature_set", +) +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") + +all_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ACTION_NAMES.lto_backend, +] + +all_cpp_compile_actions = [ + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, +] + +preprocessor_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.clif_match, +] + +codegen_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.lto_backend, +] + +all_link_actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, +] + +def _use_msvc_toolchain(ctx): + return ctx.attr.cpu == "x64_windows" and (ctx.attr.compiler == "msvc-cl" or ctx.attr.compiler == "clang-cl") + +def _impl(ctx): + if _use_msvc_toolchain(ctx): + artifact_name_patterns = [ + artifact_name_pattern( + category_name = "object_file", + prefix = "", + extension = ".obj", + ), + artifact_name_pattern( + category_name = "static_library", + prefix = "", + extension = ".lib", + ), + artifact_name_pattern( + category_name = "alwayslink_static_library", + prefix = "", + extension = ".lo.lib", + ), + artifact_name_pattern( + category_name = "executable", + prefix = "", + extension = ".exe", + ), + artifact_name_pattern( + category_name = "dynamic_library", + prefix = "", + extension = ".dll", + ), + artifact_name_pattern( + category_name = "interface_library", + prefix = "", + extension = ".if.lib", + ), + ] + else: + artifact_name_patterns = [ + artifact_name_pattern( + category_name = "executable", + prefix = "", + extension = ".exe", + ), + ] + + if _use_msvc_toolchain(ctx): + cpp_link_nodeps_dynamic_library_action = action_config( + action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library, + implies = [ + "nologo", + "shared_flag", + "linkstamps", + "output_execpath_flags", + "input_param_flags", + "user_link_flags", + "default_link_flags", + "linker_subsystem_flag", + "linker_param_file", + "msvc_env", + "no_stripping", + "has_configured_linker_path", + "def_file", + ], + tools = [tool(path = ctx.attr.msvc_link_path)], + ) + + cpp_link_static_library_action = action_config( + action_name = ACTION_NAMES.cpp_link_static_library, + implies = [ + "nologo", + "archiver_flags", + "input_param_flags", + "linker_param_file", + "msvc_env", + ], + tools = [tool(path = ctx.attr.msvc_lib_path)], + ) + + assemble_action = action_config( + action_name = ACTION_NAMES.assemble, + implies = [ + "compiler_input_flags", + "compiler_output_flags", + "nologo", + "msvc_env", + "sysroot", + ], + tools = [tool(path = ctx.attr.msvc_ml_path)], + ) + + preprocess_assemble_action = action_config( + action_name = ACTION_NAMES.preprocess_assemble, + implies = [ + "compiler_input_flags", + "compiler_output_flags", + "nologo", + "msvc_env", + "sysroot", + ], + tools = [tool(path = ctx.attr.msvc_ml_path)], + ) + + c_compile_action = action_config( + action_name = ACTION_NAMES.c_compile, + implies = [ + "compiler_input_flags", + "compiler_output_flags", + "default_compile_flags", + "nologo", + "msvc_env", + "parse_showincludes", + "user_compile_flags", + "sysroot", + "unfiltered_compile_flags", + ], + tools = [tool(path = ctx.attr.msvc_cl_path)], + ) + + cpp_compile_action = action_config( + action_name = ACTION_NAMES.cpp_compile, + implies = [ + "compiler_input_flags", + "compiler_output_flags", + "default_compile_flags", + "nologo", + "msvc_env", + "parse_showincludes", + "user_compile_flags", + "sysroot", + "unfiltered_compile_flags", + ], + tools = [tool(path = ctx.attr.msvc_cl_path)], + ) + + cpp_link_executable_action = action_config( + action_name = ACTION_NAMES.cpp_link_executable, + implies = [ + "nologo", + "linkstamps", + "output_execpath_flags", + "input_param_flags", + "user_link_flags", + "default_link_flags", + "linker_subsystem_flag", + "linker_param_file", + "msvc_env", + "no_stripping", + ], + tools = [tool(path = ctx.attr.msvc_link_path)], + ) + + cpp_link_dynamic_library_action = action_config( + action_name = ACTION_NAMES.cpp_link_dynamic_library, + implies = [ + "nologo", + "shared_flag", + "linkstamps", + "output_execpath_flags", + "input_param_flags", + "user_link_flags", + "default_link_flags", + "linker_subsystem_flag", + "linker_param_file", + "msvc_env", + "no_stripping", + "has_configured_linker_path", + "def_file", + ], + tools = [tool(path = ctx.attr.msvc_link_path)], + ) + + action_configs = [ + assemble_action, + preprocess_assemble_action, + c_compile_action, + cpp_compile_action, + cpp_link_executable_action, + cpp_link_dynamic_library_action, + cpp_link_nodeps_dynamic_library_action, + cpp_link_static_library_action, + ] + else: + action_configs = [] + + if _use_msvc_toolchain(ctx): + msvc_link_env_feature = feature( + name = "msvc_link_env", + env_sets = [ + env_set( + actions = all_link_actions + + [ACTION_NAMES.cpp_link_static_library], + env_entries = [env_entry(key = "LIB", value = ctx.attr.msvc_env_lib)], + ), + ], + ) + + shared_flag_feature = feature( + name = "shared_flag", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [flag_group(flags = ["/DLL"])], + ), + ], + ) + + determinism_feature = feature( + name = "determinism", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [ + flag_group( + flags = [ + "/wd4117", + "-D__DATE__=\"redacted\"", + "-D__TIMESTAMP__=\"redacted\"", + "-D__TIME__=\"redacted\"", + ] + (["-Wno-builtin-macro-redefined"] if ctx.attr.compiler == "clang-cl" else []), + ), + ], + ), + ], + ) + + sysroot_feature = feature( + name = "sysroot", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + iterate_over = "sysroot", + expand_if_available = "sysroot", + ), + ], + ), + ], + ) + + unfiltered_compile_flags_feature = feature( + name = "unfiltered_compile_flags", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["%{unfiltered_compile_flags}"], + iterate_over = "unfiltered_compile_flags", + expand_if_available = "unfiltered_compile_flags", + ), + ], + ), + ], + ) + + compiler_param_file_feature = feature( + name = "compiler_param_file", + ) + + copy_dynamic_libraries_to_binary_feature = feature( + name = "copy_dynamic_libraries_to_binary", + ) + + input_param_flags_feature = feature( + name = "input_param_flags", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [ + flag_group( + flags = ["/IMPLIB:%{interface_library_output_path}"], + expand_if_available = "interface_library_output_path", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["%{libopts}"], + iterate_over = "libopts", + expand_if_available = "libopts", + ), + ], + ), + flag_set( + actions = all_link_actions + + [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link.object_files", + flag_groups = [flag_group(flags = ["%{libraries_to_link.object_files}"])], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flag_groups = [flag_group(flags = ["%{libraries_to_link.name}"])], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flag_groups = [flag_group(flags = ["%{libraries_to_link.name}"])], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "interface_library", + ), + ), + flag_group( + flag_groups = [ + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_false = "libraries_to_link.is_whole_archive", + ), + flag_group( + flags = ["/WHOLEARCHIVE:%{libraries_to_link.name}"], + expand_if_true = "libraries_to_link.is_whole_archive", + ), + ], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + ], + ) + + fastbuild_feature = feature( + name = "fastbuild", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/Od", "/Z7"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = [ctx.attr.fastbuild_mode_debug_flag, "/INCREMENTAL:NO"], + ), + ], + ), + ], + implies = ["generate_pdb_file"], + ) + + user_compile_flags_feature = feature( + name = "user_compile_flags", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + + archiver_flags_feature = feature( + name = "archiver_flags", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group( + flags = ["/OUT:%{output_execpath}"], + expand_if_available = "output_execpath", + ), + flag_group( + flags = ["/MACHINE:X64"], + ), + ], + ), + ], + ) + + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ctx.attr.default_link_flags)], + ), + ], + ) + + static_link_msvcrt_feature = feature(name = "static_link_msvcrt") + + dynamic_link_msvcrt_debug_feature = feature( + name = "dynamic_link_msvcrt_debug", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/MDd"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["/DEFAULTLIB:msvcrtd.lib"])], + ), + ], + requires = [feature_set(features = ["dbg"])], + ) + + dbg_feature = feature( + name = "dbg", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/Od", "/Z7"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = [ctx.attr.dbg_mode_debug_flag, "/INCREMENTAL:NO"], + ), + ], + ), + ], + implies = ["generate_pdb_file"], + ) + + opt_feature = feature( + name = "opt", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/O2"])], + ), + ], + implies = ["frame_pointer"], + ) + + supports_interface_shared_libraries_feature = feature( + name = "supports_interface_shared_libraries", + enabled = True, + ) + + user_link_flags_feature = feature( + name = "user_link_flags", + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["%{user_link_flags}"], + iterate_over = "user_link_flags", + expand_if_available = "user_link_flags", + ), + ], + ), + ], + ) + + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.lto_backend, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = [ + "/DCOMPILER_MSVC", + "/DNOMINMAX", + "/D_WIN32_WINNT=0x0601", + "/D_CRT_SECURE_NO_DEPRECATE", + "/D_CRT_SECURE_NO_WARNINGS", + "/bigobj", + "/Zm500", + "/EHsc", + "/wd4351", + "/wd4291", + "/wd4250", + "/wd4996", + ], + ), + ], + ), + ], + ) + + msvc_compile_env_feature = feature( + name = "msvc_compile_env", + env_sets = [ + env_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ], + env_entries = [env_entry(key = "INCLUDE", value = ctx.attr.msvc_env_include)], + ), + ], + ) + + preprocessor_defines_feature = feature( + name = "preprocessor_defines", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group( + flags = ["/D%{preprocessor_defines}"], + iterate_over = "preprocessor_defines", + ), + ], + ), + ], + ) + + generate_pdb_file_feature = feature( + name = "generate_pdb_file", + requires = [ + feature_set(features = ["dbg"]), + feature_set(features = ["fastbuild"]), + ], + ) + + output_execpath_flags_feature = feature( + name = "output_execpath_flags", + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["/OUT:%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + ], + ) + + dynamic_link_msvcrt_no_debug_feature = feature( + name = "dynamic_link_msvcrt_no_debug", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/MD"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["/DEFAULTLIB:msvcrt.lib"])], + ), + ], + requires = [ + feature_set(features = ["fastbuild"]), + feature_set(features = ["opt"]), + ], + ) + + disable_assertions_feature = feature( + name = "disable_assertions", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/DNDEBUG"])], + with_features = [with_feature_set(features = ["opt"])], + ), + ], + ) + + has_configured_linker_path_feature = feature(name = "has_configured_linker_path") + + supports_dynamic_linker_feature = feature(name = "supports_dynamic_linker", enabled = True) + + no_stripping_feature = feature(name = "no_stripping") + + linker_param_file_feature = feature( + name = "linker_param_file", + flag_sets = [ + flag_set( + actions = all_link_actions + + [ACTION_NAMES.cpp_link_static_library], + flag_groups = [ + flag_group( + flags = ["@%{linker_param_file}"], + expand_if_available = "linker_param_file", + ), + ], + ), + ], + ) + + ignore_noisy_warnings_feature = feature( + name = "ignore_noisy_warnings", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.cpp_link_static_library], + flag_groups = [flag_group(flags = ["/ignore:4221"])], + ), + ], + ) + + no_legacy_features_feature = feature(name = "no_legacy_features") + + parse_showincludes_feature = feature( + name = "parse_showincludes", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_header_parsing, + ], + flag_groups = [flag_group(flags = ["/showIncludes"])], + ), + ], + ) + + static_link_msvcrt_no_debug_feature = feature( + name = "static_link_msvcrt_no_debug", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/MT"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["/DEFAULTLIB:libcmt.lib"])], + ), + ], + requires = [ + feature_set(features = ["fastbuild"]), + feature_set(features = ["opt"]), + ], + ) + + treat_warnings_as_errors_feature = feature( + name = "treat_warnings_as_errors", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/WX"])], + ), + ], + ) + + windows_export_all_symbols_feature = feature(name = "windows_export_all_symbols") + + no_windows_export_all_symbols_feature = feature(name = "no_windows_export_all_symbols") + + include_paths_feature = feature( + name = "include_paths", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ], + flag_groups = [ + flag_group( + flags = ["/I%{quote_include_paths}"], + iterate_over = "quote_include_paths", + ), + flag_group( + flags = ["/I%{include_paths}"], + iterate_over = "include_paths", + ), + flag_group( + flags = ["/I%{system_include_paths}"], + iterate_over = "system_include_paths", + ), + ], + ), + ], + ) + + linkstamps_feature = feature( + name = "linkstamps", + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["%{linkstamp_paths}"], + iterate_over = "linkstamp_paths", + expand_if_available = "linkstamp_paths", + ), + ], + ), + ], + ) + + targets_windows_feature = feature( + name = "targets_windows", + enabled = True, + implies = ["copy_dynamic_libraries_to_binary"], + ) + + linker_subsystem_flag_feature = feature( + name = "linker_subsystem_flag", + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["/SUBSYSTEM:CONSOLE"])], + ), + ], + ) + + static_link_msvcrt_debug_feature = feature( + name = "static_link_msvcrt_debug", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/MTd"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["/DEFAULTLIB:libcmtd.lib"])], + ), + ], + requires = [feature_set(features = ["dbg"])], + ) + + frame_pointer_feature = feature( + name = "frame_pointer", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/Oy-"])], + ), + ], + ) + + compiler_output_flags_feature = feature( + name = "compiler_output_flags", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.assemble], + flag_groups = [ + flag_group( + flag_groups = [ + flag_group( + flags = ["/Fo%{output_file}", "/Zi"], + expand_if_available = "output_file", + expand_if_not_available = "output_assembly_file", + ), + ], + expand_if_not_available = "output_preprocess_file", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + flag_groups = [ + flag_group( + flags = ["/Fo%{output_file}"], + expand_if_not_available = "output_preprocess_file", + ), + ], + expand_if_available = "output_file", + expand_if_not_available = "output_assembly_file", + ), + flag_group( + flag_groups = [ + flag_group( + flags = ["/Fa%{output_file}"], + expand_if_available = "output_assembly_file", + ), + ], + expand_if_available = "output_file", + ), + flag_group( + flag_groups = [ + flag_group( + flags = ["/P", "/Fi%{output_file}"], + expand_if_available = "output_preprocess_file", + ), + ], + expand_if_available = "output_file", + ), + ], + ), + ], + ) + + nologo_feature = feature( + name = "nologo", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ACTION_NAMES.cpp_link_static_library, + ], + flag_groups = [flag_group(flags = ["/nologo"])], + ), + ], + ) + + smaller_binary_feature = feature( + name = "smaller_binary", + enabled = True, + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [flag_group(flags = ["/Gy", "/Gw"])], + with_features = [with_feature_set(features = ["opt"])], + ), + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["/OPT:ICF", "/OPT:REF"])], + with_features = [with_feature_set(features = ["opt"])], + ), + ], + ) + + compiler_input_flags_feature = feature( + name = "compiler_input_flags", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["/c", "%{source_file}"], + expand_if_available = "source_file", + ), + ], + ), + ], + ) + + def_file_feature = feature( + name = "def_file", + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["/DEF:%{def_file_path}", "/ignore:4070"], + expand_if_available = "def_file_path", + ), + ], + ), + ], + ) + + msvc_env_feature = feature( + name = "msvc_env", + env_sets = [ + env_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ACTION_NAMES.cpp_link_static_library, + ], + env_entries = [ + env_entry(key = "PATH", value = ctx.attr.msvc_env_path), + env_entry(key = "TMP", value = ctx.attr.msvc_env_tmp), + env_entry(key = "TEMP", value = ctx.attr.msvc_env_tmp), + ], + ), + ], + implies = ["msvc_compile_env", "msvc_link_env"], + ) + features = [ + no_legacy_features_feature, + nologo_feature, + has_configured_linker_path_feature, + no_stripping_feature, + targets_windows_feature, + copy_dynamic_libraries_to_binary_feature, + default_compile_flags_feature, + msvc_env_feature, + msvc_compile_env_feature, + msvc_link_env_feature, + include_paths_feature, + preprocessor_defines_feature, + parse_showincludes_feature, + generate_pdb_file_feature, + shared_flag_feature, + linkstamps_feature, + output_execpath_flags_feature, + archiver_flags_feature, + input_param_flags_feature, + linker_subsystem_flag_feature, + user_link_flags_feature, + default_link_flags_feature, + linker_param_file_feature, + static_link_msvcrt_feature, + static_link_msvcrt_no_debug_feature, + dynamic_link_msvcrt_no_debug_feature, + static_link_msvcrt_debug_feature, + dynamic_link_msvcrt_debug_feature, + dbg_feature, + fastbuild_feature, + opt_feature, + frame_pointer_feature, + disable_assertions_feature, + determinism_feature, + treat_warnings_as_errors_feature, + smaller_binary_feature, + ignore_noisy_warnings_feature, + user_compile_flags_feature, + sysroot_feature, + unfiltered_compile_flags_feature, + compiler_param_file_feature, + compiler_output_flags_feature, + compiler_input_flags_feature, + def_file_feature, + windows_export_all_symbols_feature, + no_windows_export_all_symbols_feature, + supports_dynamic_linker_feature, + supports_interface_shared_libraries_feature, + ] + else: + targets_windows_feature = feature( + name = "targets_windows", + implies = ["copy_dynamic_libraries_to_binary"], + enabled = True, + ) + + copy_dynamic_libraries_to_binary_feature = feature(name = "copy_dynamic_libraries_to_binary") + + gcc_env_feature = feature( + name = "gcc_env", + enabled = True, + env_sets = [ + env_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ACTION_NAMES.cpp_link_static_library, + ], + env_entries = [ + env_entry(key = "PATH", value = ctx.attr.tool_bin_path), + ], + ), + ], + ) + + default_compile_flags_feature = feature( + name = "default_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.lto_backend, + ACTION_NAMES.clif_match, + ], + flag_groups = [flag_group(flags = ["-std=gnu++0x"])], + ), + ], + ) + + default_link_flags_feature = feature( + name = "default_link_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [flag_group(flags = ["-lstdc++"])], + ), + ], + ) + + supports_dynamic_linker_feature = feature( + name = "supports_dynamic_linker", + enabled = True, + ) + + if ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "mingw-gcc": + compiler_param_file_feature = feature( + name = "compiler_param_file", + ) + + features = [ + targets_windows_feature, + copy_dynamic_libraries_to_binary_feature, + gcc_env_feature, + default_compile_flags_feature, + compiler_param_file_feature, + default_link_flags_feature, + supports_dynamic_linker_feature, + ] + else: + supports_pic_feature = feature( + name = "supports_pic", + enabled = True, + ) + supports_start_end_lib_feature = feature( + name = "supports_start_end_lib", + enabled = True, + ) + + dbg_feature = feature(name = "dbg") + + opt_feature = feature(name = "opt") + + sysroot_feature = feature( + name = "sysroot", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.lto_backend, + ACTION_NAMES.clif_match, + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + expand_if_available = "sysroot", + ), + ], + ), + ], + ) + + fdo_optimize_feature = feature( + name = "fdo_optimize", + flag_sets = [ + flag_set( + actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile], + flag_groups = [ + flag_group( + flags = [ + "-fprofile-use=%{fdo_profile_path}", + "-fprofile-correction", + ], + expand_if_available = "fdo_profile_path", + ), + ], + ), + ], + provides = ["profile"], + ) + + user_compile_flags_feature = feature( + name = "user_compile_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.lto_backend, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + ], + ) + + features = [ + targets_windows_feature, + copy_dynamic_libraries_to_binary_feature, + gcc_env_feature, + supports_pic_feature, + default_compile_flags_feature, + default_link_flags_feature, + fdo_optimize_feature, + supports_dynamic_linker_feature, + dbg_feature, + opt_feature, + user_compile_flags_feature, + sysroot_feature, + ] + + tool_paths = [ + tool_path(name = name, path = path) + for name, path in ctx.attr.tool_paths.items() + ] + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + action_configs = action_configs, + artifact_name_patterns = artifact_name_patterns, + cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories, + toolchain_identifier = ctx.attr.toolchain_identifier, + host_system_name = ctx.attr.host_system_name, + target_system_name = ctx.attr.target_system_name, + target_cpu = ctx.attr.cpu, + target_libc = ctx.attr.target_libc, + compiler = ctx.attr.compiler, + abi_version = ctx.attr.abi_version, + abi_libc_version = ctx.attr.abi_libc_version, + tool_paths = tool_paths, + ) + +cc_toolchain_config = rule( + implementation = _impl, + attrs = { + "cpu": attr.string(mandatory = True), + "compiler": attr.string(), + "toolchain_identifier": attr.string(), + "host_system_name": attr.string(), + "target_system_name": attr.string(), + "target_libc": attr.string(), + "abi_version": attr.string(), + "abi_libc_version": attr.string(), + "tool_paths": attr.string_dict(), + "cxx_builtin_include_directories": attr.string_list(), + "default_link_flags": attr.string_list(default = []), + "msvc_env_tmp": attr.string(default = "msvc_not_found"), + "msvc_env_path": attr.string(default = "msvc_not_found"), + "msvc_env_include": attr.string(default = "msvc_not_found"), + "msvc_env_lib": attr.string(default = "msvc_not_found"), + "msvc_cl_path": attr.string(default = "vc_installation_error.bat"), + "msvc_ml_path": attr.string(default = "vc_installation_error.bat"), + "msvc_link_path": attr.string(default = "vc_installation_error.bat"), + "msvc_lib_path": attr.string(default = "vc_installation_error.bat"), + "dbg_mode_debug_flag": attr.string(), + "fastbuild_mode_debug_flag": attr.string(), + "tool_bin_path": attr.string(default = "not_found"), + }, + provides = [CcToolchainConfigInfo], +) diff --git a/cc/toolchain_utils.bzl b/cc/toolchain_utils.bzl new file mode 100644 index 0000000..dc97993 --- /dev/null +++ b/cc/toolchain_utils.bzl @@ -0,0 +1,48 @@ +# 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. + +""" +Finds the c++ toolchain. + +Returns the toolchain if enabled, and falls back to a toolchain constructed from +the CppConfiguration. +""" + +def find_cpp_toolchain(ctx): + """ + Finds the c++ toolchain. + + If the c++ toolchain is in use, returns it. Otherwise, returns a c++ + toolchain derived from legacy toolchain selection. + + Args: + ctx: The rule context for which to find a toolchain. + + Returns: + A CcToolchainProvider. + """ + + # Check the incompatible flag for toolchain resolution. + if hasattr(cc_common, "is_cc_toolchain_resolution_enabled_do_not_use") and cc_common.is_cc_toolchain_resolution_enabled_do_not_use(ctx = ctx): + if "@rules_cc//cc:toolchain_type" in ctx.toolchains: + return ctx.toolchains["@rules_cc//cc:toolchain_type"] + fail("In order to use find_cpp_toolchain, you must include the '@rules_cc//cc:toolchain_type' in the toolchains argument to your rule.") + + # Fall back to the legacy implicit attribute lookup. + if hasattr(ctx.attr, "_cc_toolchain"): + return ctx.attr._cc_toolchain[cc_common.CcToolchainInfo] + + # We didn't find anything. + fail("In order to use find_cpp_toolchain, you must define the '_cc_toolchain' attribute on your rule or aspect.") diff --git a/examples/my_c_archive/my_c_archive.bzl b/examples/my_c_archive/my_c_archive.bzl index 5d333c4..baf6d25 100644 --- a/examples/my_c_archive/my_c_archive.bzl +++ b/examples/my_c_archive/my_c_archive.bzl @@ -14,7 +14,7 @@ """Example showing how to create a rule that rules_cc can depend on.""" -load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load("@rules_cc//cc:toolchain_utils.bzl", "find_cpp_toolchain") load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "CPP_LINK_STATIC_LIBRARY_ACTION_NAME") load("//examples/my_c_compile:my_c_compile.bzl", "MyCCompileInfo") diff --git a/examples/my_c_compile/my_c_compile.bzl b/examples/my_c_compile/my_c_compile.bzl index fe58db8..5dacfbc 100644 --- a/examples/my_c_compile/my_c_compile.bzl +++ b/examples/my_c_compile/my_c_compile.bzl @@ -14,7 +14,7 @@ """Example showing how to create a rule that just compiles C sources.""" -load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load("@rules_cc//cc:toolchain_utils.bzl", "find_cpp_toolchain") load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "C_COMPILE_ACTION_NAME") MyCCompileInfo = provider(doc = "", fields = ["object"]) diff --git a/examples/write_cc_toolchain_cpu/write_cc_toolchain_cpu.bzl b/examples/write_cc_toolchain_cpu/write_cc_toolchain_cpu.bzl index e1d253f..9aebf6e 100644 --- a/examples/write_cc_toolchain_cpu/write_cc_toolchain_cpu.bzl +++ b/examples/write_cc_toolchain_cpu/write_cc_toolchain_cpu.bzl @@ -14,7 +14,7 @@ """Example showing how to get CcToolchainInfo in a custom rule.""" -load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load("@rules_cc//cc:toolchain_utils.bzl", "find_cpp_toolchain") def _write_cc_toolchain_cpu_impl(ctx): cc_toolchain = find_cpp_toolchain(ctx)