diff --git a/cc/toolchains/impl/legacy_converter.bzl b/cc/toolchains/impl/legacy_converter.bzl new file mode 100644 index 0000000..a216f66 --- /dev/null +++ b/cc/toolchains/impl/legacy_converter.bzl @@ -0,0 +1,190 @@ +# Copyright 2024 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. +"""Conversion helper functions to legacy cc_toolchain_config_info.""" + +load( + "//cc:cc_toolchain_config_lib.bzl", + legacy_action_config = "action_config", + legacy_env_entry = "env_entry", + legacy_env_set = "env_set", + legacy_feature = "feature", + legacy_feature_set = "feature_set", + legacy_flag_group = "flag_group", + legacy_flag_set = "flag_set", + legacy_tool = "tool", + legacy_with_feature_set = "with_feature_set", +) +load( + "//cc/toolchains:cc_toolchain_info.bzl", + "ArgsListInfo", + "FeatureInfo", +) + +visibility([ + "//cc/toolchains/...", + "//tests/rule_based_toolchain/...", +]) + +# Note that throughout this file, we sort anything for which the order is +# nondeterministic (eg. depset's .to_list(), dictionary iteration). +# This allows our tests to call equals() on the output, +# and *may* provide better caching properties. + +def _convert_actions(actions): + return sorted([action.name for action in actions.to_list()]) + +def convert_feature_constraint(constraint): + return legacy_with_feature_set( + features = sorted([ft.name for ft in constraint.all_of.to_list()]), + not_features = sorted([ft.name for ft in constraint.none_of.to_list()]), + ) + +def _convert_add_arg(add_arg): + return [legacy_flag_group(flags = list(add_arg.args))] + +def _convert_args(args): + actions = _convert_actions(args.actions) + with_features = [ + convert_feature_constraint(fc) + for fc in args.requires_any_of + ] + + flag_sets = [] + if args.args: + flag_groups = [] + for add_args in args.args: + flag_groups.extend(_convert_add_arg(add_args)) + flag_sets.append(legacy_flag_set( + actions = actions, + with_features = with_features, + flag_groups = flag_groups, + )) + + env_sets = [] + if args.env: + env_sets.append(legacy_env_set( + actions = actions, + with_features = with_features, + env_entries = [ + legacy_env_entry( + key = key, + value = value, + ) + for key, value in args.env.items() + ], + )) + return struct( + flag_sets = flag_sets, + env_sets = env_sets, + ) + +def _convert_args_sequence(args_sequence): + flag_sets = [] + env_sets = [] + for args in args_sequence: + legacy_args = _convert_args(args) + flag_sets.extend(legacy_args.flag_sets) + env_sets.extend(legacy_args.env_sets) + + return struct(flag_sets = flag_sets, env_sets = env_sets) + +def convert_feature(feature): + if feature.external: + return None + + args = _convert_args_sequence(feature.args.args) + + return legacy_feature( + name = feature.name, + enabled = feature.enabled, + flag_sets = args.flag_sets, + env_sets = args.env_sets, + implies = sorted([ft.name for ft in feature.implies.to_list()]), + requires = [ + legacy_feature_set(sorted([ + feature.name + for feature in requirement.features.to_list() + ])) + for requirement in feature.requires_any_of + ], + provides = [ + mutex.name + for mutex in feature.mutually_exclusive + ], + ) + +def convert_tool(tool): + return legacy_tool( + tool = tool.exe, + execution_requirements = list(tool.execution_requirements), + with_features = [ + convert_feature_constraint(fc) + for fc in tool.requires_any_of + ], + ) + +def _convert_action_type_config(atc): + implies = sorted([ft.name for ft in atc.implies.to_list()]) + if atc.args: + implies.append("implied_by_%s" % atc.action_type.name) + + return legacy_action_config( + action_name = atc.action_type.name, + enabled = True, + tools = [convert_tool(tool) for tool in atc.tools], + implies = implies, + ) + +def convert_toolchain(toolchain): + """Converts a rule-based toolchain into the legacy providers. + + Args: + toolchain: CcToolchainConfigInfo: The toolchain config to convert. + Returns: + A struct containing parameters suitable to pass to + cc_common.create_cc_toolchain_config_info. + """ + features = [convert_feature(feature) for feature in toolchain.features] + features.append(convert_feature(FeatureInfo( + # We reserve names starting with implied_by. This ensures we don't + # conflict with the name of a feature the user creates. + name = "implied_by_always_enabled", + enabled = True, + args = ArgsListInfo(args = toolchain.args), + implies = depset([]), + requires_any_of = [], + mutually_exclusive = [], + external = False, + ))) + action_configs = [] + for atc in toolchain.action_type_configs.values(): + # Action configs don't take in an env like they do a flag set. + # In order to support them, we create a feature with the env that the action + # config will enable, and imply it in the action config. + if atc.args: + features.append(convert_feature(FeatureInfo( + name = "implied_by_%s" % atc.action_type.name, + enabled = False, + args = ArgsListInfo(args = atc.args), + implies = depset([]), + requires_any_of = [], + mutually_exclusive = [], + external = False, + ))) + action_configs.append(_convert_action_type_config(atc)) + + return struct( + features = sorted([ft for ft in features if ft != None], key = lambda ft: ft.name), + action_configs = sorted(action_configs, key = lambda ac: ac.action_name), + ) diff --git a/cc/toolchains/impl/toolchain_config.bzl b/cc/toolchains/impl/toolchain_config.bzl index 551b0d0..185f242 100644 --- a/cc/toolchains/impl/toolchain_config.bzl +++ b/cc/toolchains/impl/toolchain_config.bzl @@ -23,6 +23,7 @@ load( "ToolchainConfigInfo", ) load(":collect.bzl", "collect_action_types") +load(":legacy_converter.bzl", "convert_toolchain") load(":toolchain_config_info.bzl", "toolchain_config_info") visibility([ @@ -61,21 +62,54 @@ def _cc_toolchain_config_impl(ctx): args = ctx.attr.args, ) + legacy = convert_toolchain(toolchain_config) + return [ - # TODO: Transform toolchain_config into legacy cc_toolchain_config_info toolchain_config, + cc_common.create_cc_toolchain_config_info( + ctx = ctx, + action_configs = legacy.action_configs, + features = legacy.features, + cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories, + toolchain_identifier = ctx.attr.toolchain_identifier, + target_system_name = ctx.attr.target_system_name, + target_cpu = ctx.attr.target_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, + builtin_sysroot = ctx.attr.sysroot or None, + ), ] cc_toolchain_config = rule( implementation = _cc_toolchain_config_impl, # @unsorted-dict-items attrs = { + # Attributes new to this rule. "action_type_configs": attr.label_list(providers = [ActionTypeConfigSetInfo]), "args": attr.label_list(providers = [ArgsListInfo]), "toolchain_features": attr.label_list(providers = [FeatureSetInfo]), "skip_experimental_flag_validation_for_test": attr.bool(default = False), "_builtin_features": attr.label(default = "//cc/toolchains/features:all_builtin_features"), "_enabled": attr.label(default = "//cc/toolchains:experimental_enable_rule_based_toolchains"), + + # Attributes from create_cc_toolchain_config_info. + # artifact_name_patterns is currently unused. Consider adding it later. + # TODO: Consider making this into a label_list that takes a + # cc_directory_marker rule as input. + "cxx_builtin_include_directories": attr.string_list(), + "toolchain_identifier": attr.string(mandatory = True), + "target_system_name": attr.string(mandatory = True), + "target_cpu": attr.string(mandatory = True), + "target_libc": attr.string(mandatory = True), + "compiler": attr.string(mandatory = True), + "abi_version": attr.string(), + "abi_libc_version": attr.string(), + # tool_paths currently unused. + # TODO: Consider making this into a label that takes a + # cc_directory_marker rule as an input. + "sysroot": attr.string(), }, provides = [ToolchainConfigInfo], ) diff --git a/cc/toolchains/tool.bzl b/cc/toolchains/tool.bzl index 01ea481..797ef52 100644 --- a/cc/toolchains/tool.bzl +++ b/cc/toolchains/tool.bzl @@ -70,7 +70,7 @@ executable label. doc = "Additional files that are required for this tool to run.", ), "execution_requirements": attr.string_list( - doc = "A list of strings that provide hints for execution environment compatibility (e.g. `requires-mac`).", + doc = "A list of strings that provide hints for execution environment compatibility (e.g. `requires-network`).", ), "requires_any_of": attr.label_list( providers = [FeatureConstraintInfo], diff --git a/tests/rule_based_toolchain/features/BUILD b/tests/rule_based_toolchain/features/BUILD index 1d83660..c7ea334 100644 --- a/tests/rule_based_toolchain/features/BUILD +++ b/tests/rule_based_toolchain/features/BUILD @@ -73,6 +73,7 @@ util.helper_target( name = "direct_constraint", all_of = [":simple"], none_of = [":simple2"], + visibility = ["//tests/rule_based_toolchain:__subpackages__"], ) util.helper_target( diff --git a/tests/rule_based_toolchain/features/features_test.bzl b/tests/rule_based_toolchain/features/features_test.bzl index 3406bdd..f0b15fd 100644 --- a/tests/rule_based_toolchain/features/features_test.bzl +++ b/tests/rule_based_toolchain/features/features_test.bzl @@ -11,8 +11,14 @@ # 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. -"""Tests for actions for the rule based toolchain.""" +"""Tests for features for the rule based toolchain.""" +load( + "//cc:cc_toolchain_config_lib.bzl", + legacy_feature_set = "feature_set", + legacy_flag_group = "flag_group", + legacy_flag_set = "flag_set", +) load( "//cc/toolchains:cc_toolchain_info.bzl", "ArgsInfo", @@ -21,6 +27,11 @@ load( "FeatureSetInfo", "MutuallyExclusiveCategoryInfo", ) +load( + "//cc/toolchains/impl:legacy_converter.bzl", + "convert_feature", + "convert_feature_constraint", +) visibility("private") @@ -41,13 +52,28 @@ def _simple_feature_test(env, targets): c_compile_action.files().contains_exactly([_C_COMPILE_FILE]) c_compile_action.args().contains_exactly([targets.c_compile[ArgsInfo]]) + legacy = convert_feature(simple.actual) + env.expect.that_str(legacy.name).equals("feature_name") + env.expect.that_bool(legacy.enabled).equals(False) + env.expect.that_collection(legacy.flag_sets).contains_exactly([ + legacy_flag_set( + actions = ["c_compile"], + with_features = [], + flag_groups = [legacy_flag_group(flags = ["c"])], + ), + ]) + def _feature_collects_requirements_test(env, targets): - env.expect.that_target(targets.requires).provider( - FeatureInfo, - ).requires_any_of().contains_exactly([ + ft = env.expect.that_target(targets.requires).provider(FeatureInfo) + ft.requires_any_of().contains_exactly([ targets.feature_set.label, ]) + legacy = convert_feature(ft.actual) + env.expect.that_collection(legacy.requires).contains_exactly([ + legacy_feature_set(features = ["feature_name", "simple2"]), + ]) + def _feature_collects_implies_test(env, targets): env.expect.that_target(targets.implies).provider( FeatureInfo, @@ -93,10 +119,24 @@ def _feature_constraint_collects_transitive_features_test(env, targets): targets.implies.label, ]) + legacy = convert_feature_constraint(constraint.actual) + env.expect.that_collection(legacy.features).contains_exactly([ + "feature_name", + "requires", + ]) + env.expect.that_collection(legacy.not_features).contains_exactly([ + "simple2", + "implies", + ]) + def _external_feature_is_a_feature_test(env, targets): - env.expect.that_target(targets.builtin_feature).provider( + external_feature = env.expect.that_target(targets.builtin_feature).provider( FeatureInfo, - ).name().equals("builtin_feature") + ) + external_feature.name().equals("builtin_feature") + + # It's not a string, but we don't have a factory for the type. + env.expect.that_str(convert_feature(external_feature.actual)).equals(None) def _feature_can_be_overridden_test(env, targets): overrides = env.expect.that_target(targets.overrides).provider(FeatureInfo) diff --git a/tests/rule_based_toolchain/tool/BUILD b/tests/rule_based_toolchain/tool/BUILD index de026f8..31c2813 100644 --- a/tests/rule_based_toolchain/tool/BUILD +++ b/tests/rule_based_toolchain/tool/BUILD @@ -8,7 +8,8 @@ util.helper_target( name = "tool", data = ["//tests/rule_based_toolchain/testdata:bin"], executable = "//tests/rule_based_toolchain/testdata:bin_wrapper.sh", - execution_requirements = ["requires-mac"], + execution_requirements = ["requires-network"], + requires_any_of = ["//tests/rule_based_toolchain/features:direct_constraint"], ) util.helper_target( diff --git a/tests/rule_based_toolchain/tool/tool_test.bzl b/tests/rule_based_toolchain/tool/tool_test.bzl index 840ae84..8e9b68a 100644 --- a/tests/rule_based_toolchain/tool/tool_test.bzl +++ b/tests/rule_based_toolchain/tool/tool_test.bzl @@ -13,8 +13,13 @@ # limitations under the License. """Tests for the cc_args rule.""" +load( + "//cc:cc_toolchain_config_lib.bzl", + legacy_with_feature_set = "with_feature_set", +) load("//cc/toolchains:cc_toolchain_info.bzl", "ToolInfo") load("//cc/toolchains/impl:collect.bzl", _collect_tools = "collect_tools") +load("//cc/toolchains/impl:legacy_converter.bzl", "convert_tool") load("//tests/rule_based_toolchain:subjects.bzl", "result_fn_wrapper", "subjects") visibility("private") @@ -35,10 +40,22 @@ _BIN = "tests/rule_based_toolchain/testdata/bin" def _tool_test(env, targets): tool = env.expect.that_target(targets.tool).provider(ToolInfo) tool.exe().short_path_equals(_BIN_WRAPPER) + tool.execution_requirements().contains_exactly(["requires-network"]) tool.runfiles().contains_exactly([ _BIN_WRAPPER, _BIN, ]) + tool.requires_any_of().contains_exactly([targets.direct_constraint.label]) + + legacy = convert_tool(tool.actual) + env.expect.that_file(legacy.tool).equals(tool.actual.exe) + env.expect.that_collection(legacy.execution_requirements).contains_exactly(["requires-network"]) + env.expect.that_collection(legacy.with_features).contains_exactly([ + legacy_with_feature_set( + features = ["feature_name"], + not_features = ["simple2"], + ), + ]) def _wrapped_tool_includes_runfiles_test(env, targets): tool = env.expect.that_target(targets.wrapped_tool).provider(ToolInfo) @@ -86,6 +103,7 @@ def _collect_tools_fails_on_non_binary_test(env, targets): ).err() TARGETS = [ + "//tests/rule_based_toolchain/features:direct_constraint", "//tests/rule_based_toolchain/tool:tool", "//tests/rule_based_toolchain/tool:wrapped_tool", "//tests/rule_based_toolchain/testdata:bin_wrapper", diff --git a/tests/rule_based_toolchain/toolchain_config/BUILD b/tests/rule_based_toolchain/toolchain_config/BUILD index 56a0866..f7f194d 100644 --- a/tests/rule_based_toolchain/toolchain_config/BUILD +++ b/tests/rule_based_toolchain/toolchain_config/BUILD @@ -33,13 +33,26 @@ util.helper_target( args = ["c_compile_args"], ) +util.helper_target( + cc_args, + name = "cpp_compile_args", + actions = ["//tests/rule_based_toolchain/actions:cpp_compile"], + args = ["cpp_compile_args"], + env = {"CPP_COMPILE": "1"}, +) + util.helper_target( cc_toolchain_config, name = "collects_files_toolchain_config", action_type_configs = [":compile_config"], args = [":c_compile_args"], + compiler = "gcc-4.1.1", skip_experimental_flag_validation_for_test = True, + target_cpu = "k8", + target_libc = "glibc-2.2.2", + target_system_name = "local", toolchain_features = [":compile_feature"], + toolchain_identifier = "collects_files_toolchain", ) util.helper_target( @@ -68,8 +81,9 @@ util.helper_target( cc_action_type_config, name = "compile_config", action_types = ["//tests/rule_based_toolchain/actions:all_compile"], + args = [":cpp_compile_args"], tools = [ - "//tests/rule_based_toolchain/testdata:bin", + "//tests/rule_based_toolchain/tool:wrapped_tool", ], ) @@ -87,7 +101,7 @@ util.helper_target( action_types = ["//tests/rule_based_toolchain/actions:c_compile"], implies = [":simple_feature"], tools = [ - "//tests/rule_based_toolchain/testdata:bin", + "//tests/rule_based_toolchain/tool:wrapped_tool", ], ) diff --git a/tests/rule_based_toolchain/toolchain_config/toolchain_config_test.bzl b/tests/rule_based_toolchain/toolchain_config/toolchain_config_test.bzl index 83e453f..e188772 100644 --- a/tests/rule_based_toolchain/toolchain_config/toolchain_config_test.bzl +++ b/tests/rule_based_toolchain/toolchain_config/toolchain_config_test.bzl @@ -11,9 +11,20 @@ # 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. -"""Tests for the cc_args rule.""" +"""Tests for the cc_toolchain_config rule.""" +load( + "//cc:cc_toolchain_config_lib.bzl", + legacy_action_config = "action_config", + legacy_env_entry = "env_entry", + legacy_env_set = "env_set", + legacy_feature = "feature", + legacy_flag_group = "flag_group", + legacy_flag_set = "flag_set", + legacy_tool = "tool", +) load("//cc/toolchains:cc_toolchain_info.bzl", "ActionTypeInfo", "ToolchainConfigInfo") +load("//cc/toolchains/impl:legacy_converter.bzl", "convert_toolchain") load("//cc/toolchains/impl:toolchain_config_info.bzl", _toolchain_config_info = "toolchain_config_info") load("//tests/rule_based_toolchain:subjects.bzl", "result_fn_wrapper", "subjects") @@ -24,6 +35,7 @@ toolchain_config_info = result_fn_wrapper(_toolchain_config_info) _COLLECTED_CPP_COMPILE_FILES = [ # From :compile_config's tool "tests/rule_based_toolchain/testdata/bin", + "tests/rule_based_toolchain/testdata/bin_wrapper", # From :compile_feature's args "tests/rule_based_toolchain/testdata/file2", ] @@ -189,6 +201,61 @@ def _toolchain_collects_files_test(env, targets): targets.collects_files_cpp_compile, ).default_outputs().contains_exactly(_COLLECTED_CPP_COMPILE_FILES) + legacy = convert_toolchain(tc.actual) + env.expect.that_collection(legacy.features).contains_exactly([ + legacy_feature( + name = "compile_feature", + enabled = True, + flag_sets = [legacy_flag_set( + actions = ["c_compile", "cpp_compile"], + flag_groups = [ + legacy_flag_group(flags = ["compile_args"]), + ], + )], + ), + legacy_feature( + name = "implied_by_always_enabled", + enabled = True, + flag_sets = [legacy_flag_set( + actions = ["c_compile"], + flag_groups = [ + legacy_flag_group(flags = ["c_compile_args"]), + ], + )], + ), + legacy_feature( + name = "implied_by_cpp_compile", + enabled = False, + flag_sets = [legacy_flag_set( + actions = ["cpp_compile"], + flag_groups = [ + legacy_flag_group(flags = ["cpp_compile_args"]), + ], + )], + env_sets = [legacy_env_set( + actions = ["cpp_compile"], + env_entries = [legacy_env_entry(key = "CPP_COMPILE", value = "1")], + )], + ), + ]).in_order() + + exe = tc.action_type_configs().get( + targets.c_compile[ActionTypeInfo], + ).actual.tools[0].exe + env.expect.that_collection(legacy.action_configs).contains_exactly([ + legacy_action_config( + action_name = "c_compile", + enabled = True, + tools = [legacy_tool(tool = exe)], + ), + legacy_action_config( + action_name = "cpp_compile", + enabled = True, + tools = [legacy_tool(tool = exe)], + implies = ["implied_by_cpp_compile"], + ), + ]).in_order() + TARGETS = [ "//tests/rule_based_toolchain/actions:c_compile", "//tests/rule_based_toolchain/actions:cpp_compile",