diff --git a/cc/private/toolchain/unix_cc_toolchain_config.bzl b/cc/private/toolchain/unix_cc_toolchain_config.bzl index 86480f8d..53e65800 100644 --- a/cc/private/toolchain/unix_cc_toolchain_config.bzl +++ b/cc/private/toolchain/unix_cc_toolchain_config.bzl @@ -31,6 +31,7 @@ load( ) load("@rules_cc//cc/common:cc_common.bzl", "cc_common") load("@rules_cc//cc/toolchains:cc_toolchain_config_info.bzl", "CcToolchainConfigInfo") +load("@rules_cc//cc:cc_toolchain_config_lib.bzl", "FeatureInfo") def _target_os_version(ctx): platform_type = ctx.fragments.apple.single_arch_platform.platform_type @@ -1931,6 +1932,8 @@ def _impl(ctx): if symbol_check: features.append(symbol_check) + features.extend([extra_feature[FeatureInfo] for extra_feature in ctx.attr.extra_features]) + return cc_common.create_cc_toolchain_config_info( ctx = ctx, features = features, @@ -1955,6 +1958,7 @@ cc_toolchain_config = rule( "abi_libc_version": attr.string(mandatory = True), "abi_version": attr.string(mandatory = True), "archive_flags": attr.string_list(), + "extra_features": attr.label_list(providers = [FeatureInfo], default = []), "builtin_sysroot": attr.string(), "compile_flags": attr.string_list(), "compiler": attr.string(mandatory = True), diff --git a/cc/toolchains/feature_legacy.bzl b/cc/toolchains/feature_legacy.bzl new file mode 100644 index 00000000..70999df8 --- /dev/null +++ b/cc/toolchains/feature_legacy.bzl @@ -0,0 +1,102 @@ +load("//cc:cc_toolchain_config_lib.bzl", "EnvSetInfo", "FeatureInfo", "FeatureSetInfo", "FlagGroupInfo", "FlagSetInfo", "WithFeatureSetInfo") + +def _cc_flag_group_legacy_impl(ctx): + return [ + FlagGroupInfo( + flags = ctx.attr.flags, + flag_groups = [flag_group[FlagGroupInfo] for flag_group in ctx.attr.flag_groups], + # string does not accept None as default, because of that we need to convert empty string to None + iterate_over = ctx.attr.iterate_over if ctx.attr.iterate_over else None, + expand_if_available = ctx.attr.expand_if_available if ctx.attr.expand_if_available else None, + expand_if_not_available = ctx.attr.expand_if_not_available if ctx.attr.expand_if_not_available else None, + expand_if_true = ctx.attr.expand_if_true if ctx.attr.expand_if_true else None, + expand_if_false = ctx.attr.expand_if_false if ctx.attr.expand_if_false else None, + expand_if_equal = ctx.attr.expand_if_equal if ctx.attr.expand_if_equal else None, + type_name = "flag_group", + ), + ] + +cc_flag_group_legacy = rule( + implementation = _cc_flag_group_legacy_impl, + attrs = { + "flags": attr.string_list( + default = [], + ), + "flag_groups": attr.label_list( + providers = [FlagGroupInfo], + default = [], + ), + "iterate_over": attr.string(), + "expand_if_available": attr.string(), + "expand_if_not_available": attr.string(), + "expand_if_true": attr.string(), + "expand_if_false": attr.string(), + "expand_if_equal": attr.string(), + }, +) + +def _cc_flag_set_legacy_impl(ctx): + return [ + FlagSetInfo( + actions = ctx.attr.actions, + with_features = [feature[WithFeatureSetInfo] for feature in ctx.attr.with_features], + flag_groups = [flag_group[FlagGroupInfo] for flag_group in ctx.attr.flag_groups], + type_name = "flag_set", + ), + ] + +cc_flag_set_legacy = rule( + implementation = _cc_flag_set_legacy_impl, + attrs = { + "actions": attr.string_list( + default = [], + ), + "with_features": attr.label_list( + providers = [WithFeatureSetInfo], + default = [], + ), + "flag_groups": attr.label_list( + providers = [FlagGroupInfo], + default = [], + ), + }, +) + +def _cc_feature_legacy_impl(ctx): + return [ + FeatureInfo( + name = ctx.label.name, + enabled = ctx.attr.enabled, + flag_sets = [flag_set[FlagSetInfo] for flag_set in ctx.attr.flag_sets], + env_sets = [env_set[EnvSetInfo] for env_set in ctx.attr.env_sets], + requires = [require[FeatureSetInfo] for require in ctx.attr.requires], + implies = ctx.attr.implies, + provides = ctx.attr.provides, + type_name = "feature", + ), + ] + +cc_feature_legacy = rule( + implementation = _cc_feature_legacy_impl, + attrs = { + "enabled": attr.bool(default = False), + "flag_sets": attr.label_list( + providers = [FlagSetInfo], + default = [], + ), + "env_sets": attr.label_list( + providers = [EnvSetInfo], + default = [], + ), + "requires": attr.label_list( + providers = [FeatureSetInfo], + default = [], + ), + "implies": attr.string_list( + default = [], + ), + "provides": attr.string_list( + default = [], + ), + }, +) diff --git a/examples/inject_extra_features/BUILD b/examples/inject_extra_features/BUILD new file mode 100644 index 00000000..41b737c8 --- /dev/null +++ b/examples/inject_extra_features/BUILD @@ -0,0 +1,6 @@ +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") + +cc_binary( + name = "main", + srcs = ["main.cpp"], +) diff --git a/examples/inject_extra_features/main.cpp b/examples/inject_extra_features/main.cpp new file mode 100644 index 00000000..107cc39f --- /dev/null +++ b/examples/inject_extra_features/main.cpp @@ -0,0 +1,4 @@ +int main() +{ + return 32; +} diff --git a/examples/inject_extra_features/toolchain/BUILD b/examples/inject_extra_features/toolchain/BUILD new file mode 100644 index 00000000..5b324b55 --- /dev/null +++ b/examples/inject_extra_features/toolchain/BUILD @@ -0,0 +1,193 @@ +# 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-BSD unixes. + +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") +load("@rules_cc//cc/toolchains:cc_toolchain.bzl", "cc_toolchain") +load("@rules_cc//cc/toolchains:feature_legacy.bzl", "cc_feature_legacy", "cc_flag_group_legacy", "cc_flag_set_legacy") +load(":cc_toolchain_config.bzl", "cc_toolchain_config") + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "empty", + srcs = [], +) + +filegroup( + name = "cc_wrapper", + srcs = ["cc_wrapper.sh"], +) + +filegroup( + name = "validate_static_library", + srcs = ["validate_static_library.sh"], +) + +filegroup( + name = "deps_scanner_wrapper", + srcs = ["deps_scanner_wrapper.sh"], +) + +filegroup( + name = "compiler_deps", + srcs = glob( + ["extra_tools/**"], + allow_empty = True, + ) + [ + ":builtin_include_directory_paths", + ":cc_wrapper", + ":deps_scanner_wrapper", + ":validate_static_library", + ], +) + +cc_flag_group_legacy( + name = "foo_flag_group", + flags = ["-foo"], +) + +cc_flag_set_legacy( + name = "foo_flag_set", + 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, + ], + flag_groups = [":foo_flag_group"], +) + +cc_feature_legacy( + name = "foo", + flag_sets = [":foo_flag_set"], +) + +toolchain( + name = "my_linux_toolchain", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + toolchain = ":cc-compiler-k8", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", +) + +cc_toolchain( + name = "cc-compiler-k8", + all_files = ":compiler_deps", + ar_files = ":compiler_deps", + as_files = ":compiler_deps", + compiler_files = ":compiler_deps", + dwp_files = ":empty", + linker_files = ":compiler_deps", + module_map = None, + objcopy_files = ":empty", + strip_files = ":empty", + supports_header_parsing = 1, + supports_param_files = 1, + toolchain_config = ":local", + toolchain_identifier = "local", +) + +cc_toolchain_config( + name = "local", + abi_libc_version = "local", + abi_version = "local", + compile_flags = [ + "-fstack-protector", + "-Wall", + "-Wunused-but-set-parameter", + "-Wno-free-nonheap-object", + "-fno-omit-frame-pointer", + ], + compiler = "gcc", + conly_flags = [], + coverage_compile_flags = ["--coverage"], + coverage_link_flags = ["--coverage"], + cpu = "k8", + cxx_builtin_include_directories = [ + "/usr/lib/gcc/x86_64-linux-gnu/10/include", + "/usr/local/include", + "/usr/include/x86_64-linux-gnu", + "/usr/include", + "/usr/include/c++/10", + "/usr/include/x86_64-linux-gnu/c++/10", + "/usr/include/c++/10/backward", + ], + cxx_flags = ["-std=c++17"], + dbg_compile_flags = ["-g"], + extra_features = [":foo"], + host_system_name = "local", + link_flags = [ + "-fuse-ld=gold", + "-B/usr/bin", + "-Wl,-no-as-needed", + "-Wl,-z,relro,-z,now", + "-pass-exit-codes", + ], + link_libs = [ + "-Wl,--push-state,-as-needed", + "-lstdc++", + "-Wl,--pop-state", + "-Wl,--push-state,-as-needed", + "-lm", + "-Wl,--pop-state", + ], + opt_compile_flags = [ + "-g0", + "-O2", + "-D_FORTIFY_SOURCE=1", + "-DNDEBUG", + "-ffunction-sections", + "-fdata-sections", + ], + opt_link_flags = ["-Wl,--gc-sections"], + supports_start_end_lib = True, + target_libc = "local", + target_system_name = "local", + tool_paths = { + "ar": "/usr/bin/ar", + "ld": "/usr/bin/ld", + "cpp": "/usr/bin/cpp-10", + "gcc": "/usr/bin/gcc-10", + "dwp": "/usr/bin/dwp", + "gcov": "/usr/bin/gcov-10", + "nm": "/usr/bin/nm", + "objcopy": "/usr/bin/objcopy", + "objdump": "/usr/bin/objdump", + "strip": "/usr/bin/strip", + "c++filt": "/usr/bin/c++filt", + "cpp-module-deps-scanner": "deps_scanner_wrapper.sh", + "parse_headers": "cc_wrapper.sh", + "validate_static_library": "validate_static_library.sh", + }, + toolchain_identifier = "local", + unfiltered_compile_flags = [ + "-fno-canonical-system-headers", + "-Wno-builtin-macro-redefined", + "-D__DATE__=\"redacted\"", + "-D__TIMESTAMP__=\"redacted\"", + "-D__TIME__=\"redacted\"", + ], +) diff --git a/examples/inject_extra_features/toolchain/builtin_include_directory_paths b/examples/inject_extra_features/toolchain/builtin_include_directory_paths new file mode 100644 index 00000000..53e002e5 --- /dev/null +++ b/examples/inject_extra_features/toolchain/builtin_include_directory_paths @@ -0,0 +1,13 @@ +This file is generated by cc_configure and contains builtin include directories +that /usr/bin/gcc reported. This file is a dependency of every compilation action and +changes to it will be reflected in the action cache key. When some of these +paths change, Bazel will make sure to rerun the action, even though none of +declared action inputs or the action commandline changes. + +/usr/lib/gcc/x86_64-linux-gnu/10/include +/usr/local/include +/usr/include/x86_64-linux-gnu +/usr/include +/usr/include/c++/10 +/usr/include/x86_64-linux-gnu/c++/10 +/usr/include/c++/10/backward diff --git a/examples/inject_extra_features/toolchain/cc_toolchain_config.bzl b/examples/inject_extra_features/toolchain/cc_toolchain_config.bzl new file mode 100644 index 00000000..708cf701 --- /dev/null +++ b/examples/inject_extra_features/toolchain/cc_toolchain_config.bzl @@ -0,0 +1,61 @@ +# buildifier: disable=bzl-visibility +load( + "@rules_cc//cc/private/toolchain:unix_cc_toolchain_config.bzl", + unix_cc_toolchain_config = "cc_toolchain_config", +) + +# Macro for calling cc_toolchain_config from @bazel_tools with setting the +# right paths and flags for the tools. +def cc_toolchain_config( + name, + abi_libc_version, + abi_version, + compile_flags, + compiler, + conly_flags, + coverage_compile_flags, + coverage_link_flags, + cpu, + cxx_builtin_include_directories, + cxx_flags, + dbg_compile_flags, + host_system_name, + link_flags, + link_libs, + opt_compile_flags, + opt_link_flags, + supports_start_end_lib, + target_libc, + target_system_name, + tool_paths, + toolchain_identifier, + unfiltered_compile_flags, + extra_features = []): + unix_cc_toolchain_config( + name = name, + abi_libc_version = abi_libc_version, + abi_version = abi_version, + archive_flags = [], + compile_flags = compile_flags, + compiler = compiler, + conly_flags = conly_flags, + coverage_compile_flags = coverage_compile_flags, + coverage_link_flags = coverage_link_flags, + cpu = cpu, + cxx_builtin_include_directories = cxx_builtin_include_directories, + cxx_flags = cxx_flags, + dbg_compile_flags = dbg_compile_flags, + fastbuild_compile_flags = [], + host_system_name = host_system_name, + link_flags = link_flags, + link_libs = link_libs, + extra_features = extra_features, + opt_compile_flags = opt_compile_flags, + opt_link_flags = opt_link_flags, + supports_start_end_lib = supports_start_end_lib, + target_libc = target_libc, + target_system_name = target_system_name, + tool_paths = tool_paths, + toolchain_identifier = toolchain_identifier, + unfiltered_compile_flags = unfiltered_compile_flags, + ) diff --git a/examples/inject_extra_features/toolchain/cc_wrapper.sh b/examples/inject_extra_features/toolchain/cc_wrapper.sh new file mode 100644 index 00000000..4f0e2353 --- /dev/null +++ b/examples/inject_extra_features/toolchain/cc_wrapper.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env 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 + +OUTPUT= + +function parse_option() { + local -r opt="$1" + if [[ "${OUTPUT}" = "1" ]]; then + OUTPUT=$opt + elif [[ "$opt" = "-o" ]]; then + # output is coming + OUTPUT=1 + fi +} + +# let parse the option list +for i in "$@"; do + if [[ "$i" = @* && -r "${i:1}" ]]; then + while IFS= read -r opt + do + parse_option "$opt" + done < "${i:1}" || exit 1 + else + parse_option "$i" + fi +done + +# Set-up the environment + + +# Call the C++ compiler +/usr/bin/gcc-13 "$@" + +# Generate an empty file if header processing succeeded. +if [[ "${OUTPUT}" == *.h.processed ]]; then + echo -n > "${OUTPUT}" +fi diff --git a/examples/inject_extra_features/toolchain/deps_scanner_wrapper.sh b/examples/inject_extra_features/toolchain/deps_scanner_wrapper.sh new file mode 100644 index 00000000..eaab49d3 --- /dev/null +++ b/examples/inject_extra_features/toolchain/deps_scanner_wrapper.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# +# Ship the environment to the C++ action +# +set -eu + +# Set-up the environment + + +# Call the C++ compiler + +/usr/bin/gcc-10 -E -x c++ -fmodules-ts -fdeps-file=out.tmp -fdeps-format=p1689r5 "$@" >"$DEPS_SCANNER_OUTPUT_FILE" diff --git a/examples/inject_extra_features/toolchain/validate_static_library.sh b/examples/inject_extra_features/toolchain/validate_static_library.sh new file mode 100644 index 00000000..6c959e4a --- /dev/null +++ b/examples/inject_extra_features/toolchain/validate_static_library.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# +# Copyright 2023 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. +set -euo pipefail + +# Find all duplicate symbols in the given static library: +# 1. Use nm to list all global symbols in the library in POSIX format: +# libstatic.a[my_object.o]: my_function T 1234 abcd +# 2. Use sed to transform the output to a format that can be sorted by symbol +# name and is readable by humans: +# my_object.o: T my_function +# By using the `t` and `d` commands, lines for symbols of type U (undefined) +# as well as V and W (weak) and their local lowercase variants are removed. +# 3. Use sort to sort the lines by symbol name. +# 4. Use uniq to only keep the lines corresponding to duplicate symbols. +# 5. Use c++filt to demangle the symbol names. +# c++filt is applied to the duplicated symbols instead of using the -C flag +# of nm because it is not in POSIX and demangled names may not be unique +# (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35201). +DUPLICATE_SYMBOLS=$( + "/usr/bin/nm" -A -g -P "$1" | + sed -E -e 's/.*\[([^][]+)\]: (.+) ([A-TX-Z]) [a-f0-9]+ [a-f0-9]+/\1: \3 \2/g' -e t -e d | + LC_ALL=C sort -k 3 | + LC_ALL=C uniq -D -f 2 | + "/usr/bin/c++filt") +if [[ -n "$DUPLICATE_SYMBOLS" ]]; then + >&2 echo "Duplicate symbols found in $1:" + >&2 echo "$DUPLICATE_SYMBOLS" + exit 1 +else + touch "$2" +fi