aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGoogler <noreply@google.com>2024-03-19 17:04:23 -0700
committerCopybara-Service <copybara-worker@google.com>2024-03-19 17:05:03 -0700
commit17143d150dd4ea0e46775468382683eb0a884450 (patch)
treee04d5965d53b6bf90b2dddd4c23941fe177444c1
parentaa19278bbda227204e05e5f2f550e7a1913fb580 (diff)
downloadbazelbuild-rules_cc-17143d150dd4ea0e46775468382683eb0a884450.tar.gz
Add strictly typed variables toolchain rules.
BEGIN_PUBLIC Add strictly typed variables to toolchain rules. This should allow us to implement a proper replacement for flag_group END_PUBLIC PiperOrigin-RevId: 617338607 Change-Id: I7f3058578cb5eb17ecc1aa38d2e1459e0742aee9
-rw-r--r--cc/toolchains/cc_toolchain_info.bzl17
-rw-r--r--cc/toolchains/impl/variables.bzl171
-rw-r--r--cc/toolchains/variables/BUILD481
-rw-r--r--tests/rule_based_toolchain/variables/BUILD52
-rw-r--r--tests/rule_based_toolchain/variables/variables_test.bzl128
5 files changed, 849 insertions, 0 deletions
diff --git a/cc/toolchains/cc_toolchain_info.bzl b/cc/toolchains/cc_toolchain_info.bzl
index 8c0dcd5..42227d4 100644
--- a/cc/toolchains/cc_toolchain_info.bzl
+++ b/cc/toolchains/cc_toolchain_info.bzl
@@ -45,6 +45,23 @@ ActionTypeSetInfo = provider(
},
)
+VariableInfo = provider(
+ """A variable defined by the toolchain""",
+ # @unsorted-dict-items
+ fields = {
+ "name": "(str) The variable name",
+ "actions": "(Optional[depset[ActionTypeInfo]]) The actions this variable is available for",
+ "type": "A type constructed using variables.types.*",
+ },
+)
+
+BuiltinVariablesInfo = provider(
+ doc = "The builtin variables",
+ fields = {
+ "variables": "(dict[str, struct(type=type, actions=Optional[depset[ActionTypeInfo]]) A mapping from variable name to variable metadata.",
+ },
+)
+
NestedArgsInfo = provider(
doc = "A provider representation of Args.add/add_all/add_joined parameters",
# @unsorted-dict-items
diff --git a/cc/toolchains/impl/variables.bzl b/cc/toolchains/impl/variables.bzl
new file mode 100644
index 0000000..6577b34
--- /dev/null
+++ b/cc/toolchains/impl/variables.bzl
@@ -0,0 +1,171 @@
+# 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.
+"""Rules for accessing cc build variables in bazel toolchains safely."""
+
+load("//cc/toolchains:cc_toolchain_info.bzl", "ActionTypeSetInfo", "BuiltinVariablesInfo", "VariableInfo")
+load(":collect.bzl", "collect_action_types", "collect_provider")
+
+visibility([
+ "//cc/toolchains/variables",
+ "//tests/rule_based_toolchain/...",
+])
+
+types = struct(
+ unknown = dict(name = "unknown", repr = "unknown"),
+ void = dict(name = "void", repr = "void"),
+ string = dict(name = "string", repr = "string"),
+ bool = dict(name = "bool", repr = "bool"),
+ # File and directory are basically the same thing as string for now.
+ file = dict(name = "file", repr = "File"),
+ directory = dict(name = "directory", repr = "directory"),
+ option = lambda element: dict(
+ name = "option",
+ elements = element,
+ repr = "Option[%s]" % element["repr"],
+ ),
+ list = lambda elements: dict(
+ name = "list",
+ elements = elements,
+ repr = "List[%s]" % elements["repr"],
+ ),
+ struct = lambda **kv: dict(
+ name = "struct",
+ kv = kv,
+ repr = "struct(%s)" % ", ".join([
+ "{k}={v}".format(k = k, v = v["repr"])
+ for k, v in sorted(kv.items())
+ ]),
+ ),
+)
+
+def _cc_variable_impl(ctx):
+ return [VariableInfo(
+ name = ctx.label.name,
+ type = json.decode(ctx.attr.type),
+ actions = collect_action_types(ctx.attr.actions) if ctx.attr.actions else None,
+ )]
+
+_cc_variable = rule(
+ implementation = _cc_variable_impl,
+ attrs = {
+ "actions": attr.label_list(providers = [ActionTypeSetInfo]),
+ "type": attr.string(mandatory = True),
+ },
+ provides = [VariableInfo],
+)
+
+def cc_variable(name, type, **kwargs):
+ """Defines a variable for both the specified variable, and all nested ones.
+
+ Eg. cc_variable(
+ name = "foo",
+ type = types.list(types.struct(bar = types.string))
+ )
+
+ would define two targets, ":foo" and ":foo.bar"
+
+ Args:
+ name: (str) The name of the outer variable, and the rule.
+ type: The type of the variable, constructed using types above.
+ **kwargs: kwargs to pass to _cc_variable.
+ """
+ _cc_variable(name = name, type = json.encode(type), **kwargs)
+
+def _cc_builtin_variables_impl(ctx):
+ return [BuiltinVariablesInfo(variables = {
+ variable.name: struct(
+ actions = variable.actions,
+ type = variable.type,
+ )
+ for variable in collect_provider(ctx.attr.srcs, VariableInfo)
+ })]
+
+cc_builtin_variables = rule(
+ implementation = _cc_builtin_variables_impl,
+ attrs = {
+ "srcs": attr.label_list(providers = [VariableInfo]),
+ },
+)
+
+def get_type(*, name, variables, overrides, actions, args_label, nested_label, fail):
+ """Gets the type of a variable.
+
+ Args:
+ name: (str) The variable to look up.
+ variables: (dict[str, VariableInfo]) Mapping from variable name to
+ metadata. Top-level variables only
+ overrides: (dict[str, type]) Mapping from variable names to type.
+ Can be used for nested variables.
+ actions: (depset[ActionTypeInfo]) The set of actions for which the
+ variable is requested.
+ args_label: (Label) The label for the args that included the rule that
+ references this variable. Only used for error messages.
+ nested_label: (Label) The label for the rule that references this
+ variable. Only used for error messages.
+ fail: A function to be called upon failure. Use for testing only.
+ Returns:
+ The type of the variable "name".
+ """
+ outer = name.split(".")[0]
+ if outer not in variables:
+ # With a fail function, we actually need to return since the fail
+ # function doesn't propagate.
+ fail("The variable %s does not exist. Did you mean one of the following?\n%s" % (outer, "\n".join(sorted(variables))))
+
+ # buildifier: disable=unreachable
+ return types.void
+
+ if variables[outer].actions != None:
+ valid_actions = variables[outer].actions.to_list()
+ for action in actions:
+ if action not in valid_actions:
+ fail("The variable {var} is inaccessible from the action {action}. This is required because it is referenced in {nested_label}, which is included by {args_label}, which references that action".format(
+ var = outer,
+ nested_label = nested_label,
+ args_label = args_label,
+ action = action.label,
+ ))
+
+ # buildifier: disable=unreachable
+ return types.void
+
+ type = overrides.get(outer, variables[outer].type)
+
+ parent = outer
+ for part in name.split(".")[1:]:
+ full = parent + "." + part
+
+ if type["name"] != "struct":
+ extra_error = ""
+ if type["name"] == "list" and type["elements"]["name"] == "struct":
+ extra_error = " Maybe you meant to use iterate_over."
+
+ fail("Attempted to access %r, but %r was not a struct - it had type %s.%s" % (full, parent, type["repr"], extra_error))
+
+ # buildifier: disable=unreachable
+ return types.void
+
+ if part not in type["kv"] and full not in overrides:
+ attrs = []
+ for attr, value in sorted(type["kv"].items()):
+ attrs.append("%s: %s" % (attr, value["repr"]))
+ fail("Unable to find %r in %r, which had the following attributes:\n%s" % (part, parent, "\n".join(attrs)))
+
+ # buildifier: disable=unreachable
+ return types.void
+
+ type = overrides.get(full, type["kv"][part])
+ parent = full
+
+ return type
diff --git a/cc/toolchains/variables/BUILD b/cc/toolchains/variables/BUILD
new file mode 100644
index 0000000..d93083d
--- /dev/null
+++ b/cc/toolchains/variables/BUILD
@@ -0,0 +1,481 @@
+load("//cc/toolchains/impl:variables.bzl", "cc_builtin_variables", "cc_variable", "types")
+
+package(default_visibility = ["//visibility:public"])
+
+cc_variable(
+ name = "cs_fdo_instrument_path",
+ actions = [
+ "//cc/toolchains/actions:all_cc_link_actions",
+ "//cc/toolchains/actions:all_compile_actions",
+ ],
+ type = types.directory,
+)
+
+cc_variable(
+ name = "def_file_path",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "dependency_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "dependent_module_map_files",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.list(types.file)),
+)
+
+cc_variable(
+ name = "external_include_paths",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.list(types.directory)),
+)
+
+cc_variable(
+ name = "fdo_instrument_path",
+ actions = [
+ "//cc/toolchains/actions:all_cc_link_actions",
+ "//cc/toolchains/actions:all_compile_actions",
+ ],
+ type = types.directory,
+)
+
+cc_variable(
+ name = "fdo_prefetch_hints_path",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "fdo_profile_path",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "force_pic",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ # Provided when --force-pic is passed
+ type = types.option(types.void),
+)
+
+cc_variable(
+ name = "framework_include_paths",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.directory),
+)
+
+cc_variable(
+ name = "gcov_gcno_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "generate_interface_library",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ # "yes" or "no"
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "include",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.file),
+)
+
+cc_variable(
+ name = "include_paths",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.directory),
+)
+
+cc_variable(
+ name = "includes",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.list(types.file)),
+)
+
+cc_variable(
+ name = "input_file",
+ actions = ["//cc/toolchains/actions:strip"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "interface_library_builder_path",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ # Should be a file, but contains the string "ignored" when there's no value.
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "interface_library_input_path",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ # Should be a file, but contains the string "ignored" when there's no value.
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "interface_library_output_path",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ # Should be a file, but contains the string "ignored" when there's no value.
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "is_cc_test",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.bool),
+)
+
+cc_variable(
+ name = "is_using_fission",
+ actions = [
+ "//cc/toolchains/actions:all_cc_link_actions",
+ "//cc/toolchains/actions:all_compile_actions",
+ ],
+ type = types.option(types.void),
+)
+
+cc_variable(
+ name = "legacy_compile_flags",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.string),
+)
+
+cc_variable(
+ name = "legacy_link_flags",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.list(types.string),
+)
+
+cc_variable(
+ name = "libraries_to_link",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.list(types.struct(
+ shared_libraries = types.list(types.struct(
+ name = types.string,
+ is_whole_archive = types.bool,
+ object_files = types.list(types.file),
+ path = types.file,
+ type = types.string,
+ )),
+ ))),
+)
+
+cc_variable(
+ name = "libraries_to_link.shared_libraries",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ # See :libraries_to_link.
+ type = types.unknown,
+)
+
+cc_variable(
+ name = "libraries_to_link.shared_libraries.is_whole_archive",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.string,
+)
+
+cc_variable(
+ name = "libraries_to_link.shared_libraries.name",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.string,
+)
+
+cc_variable(
+ name = "libraries_to_link.shared_libraries.object_files",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.list(types.file),
+)
+
+cc_variable(
+ name = "libraries_to_link.shared_libraries.path",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "libraries_to_link.shared_libraries.type",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.string,
+)
+
+cc_variable(
+ name = "library_search_directories",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.list(types.directory),
+)
+
+cc_variable(
+ name = "linker_param_file",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "linkstamp_paths",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.list(types.directory),
+)
+
+cc_variable(
+ name = "lto_indexing_bitcode_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "module_files",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.list(types.file)),
+)
+
+cc_variable(
+ name = "module_map_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "module_name",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "output_assembly_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "output_execpath",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.directory),
+)
+
+cc_variable(
+ name = "output_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "output_preprocess_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "per_object_debug_info_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "pic",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.void),
+)
+
+cc_variable(
+ name = "preprocessor_defines",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.string),
+)
+
+cc_variable(
+ name = "propellor_optimize_ld_path",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "quote_include_paths",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.directory),
+)
+
+cc_variable(
+ name = "runtime_library_search_directories",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.list(types.directory)),
+)
+
+cc_variable(
+ name = "runtime_solib_name",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "serialized_diagnostics_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "source_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.file,
+)
+
+cc_variable(
+ name = "strip_debug_symbols",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.void),
+)
+
+cc_variable(
+ name = "stripopts",
+ actions = ["//cc/toolchains/actions:strip"],
+ type = types.list(types.string),
+)
+
+cc_variable(
+ name = "sysroot",
+ type = types.directory,
+)
+
+cc_variable(
+ name = "system_include_paths",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.directory),
+)
+
+cc_variable(
+ name = "thinlto_index",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "thinlto_indexing_param_file",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "thinlto_input_bitcode_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "thinlto_merged_object_file",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "thinlto_object_suffix_replace",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "thinlto_output_object_file",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "thinlto_param_file",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.file),
+)
+
+cc_variable(
+ name = "thinlto_prefix_replace",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "unfiltered_compile_flags",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.string),
+)
+
+cc_variable(
+ name = "user_compile_flags",
+ actions = ["//cc/toolchains/actions:all_compile_actions"],
+ type = types.list(types.string),
+)
+
+cc_variable(
+ name = "user_link_flags",
+ actions = ["//cc/toolchains/actions:all_cc_link_actions"],
+ type = types.list(types.string),
+)
+
+cc_builtin_variables(
+ name = "variables",
+ srcs = [
+ ":cs_fdo_instrument_path",
+ ":def_file_path",
+ ":dependency_file",
+ ":dependent_module_map_files",
+ ":external_include_paths",
+ ":fdo_instrument_path",
+ ":fdo_prefetch_hints_path",
+ ":fdo_profile_path",
+ ":force_pic",
+ ":framework_include_paths",
+ ":gcov_gcno_file",
+ ":generate_interface_library",
+ ":include",
+ ":include_paths",
+ ":includes",
+ ":input_file",
+ ":interface_library_builder_path",
+ ":interface_library_input_path",
+ ":interface_library_output_path",
+ ":is_cc_test",
+ ":is_using_fission",
+ ":legacy_compile_flags",
+ ":legacy_link_flags",
+ ":libraries_to_link",
+ ":library_search_directories",
+ ":linker_param_file",
+ ":linkstamp_paths",
+ ":lto_indexing_bitcode_file",
+ ":module_files",
+ ":module_map_file",
+ ":module_name",
+ ":output_assembly_file",
+ ":output_execpath",
+ ":output_file",
+ ":output_preprocess_file",
+ ":per_object_debug_info_file",
+ ":pic",
+ ":preprocessor_defines",
+ ":propellor_optimize_ld_path",
+ ":quote_include_paths",
+ ":runtime_library_search_directories",
+ ":runtime_solib_name",
+ ":serialized_diagnostics_file",
+ ":source_file",
+ ":strip_debug_symbols",
+ ":stripopts",
+ ":sysroot",
+ ":system_include_paths",
+ ":thinlto_index",
+ ":thinlto_indexing_param_file",
+ ":thinlto_input_bitcode_file",
+ ":thinlto_merged_object_file",
+ ":thinlto_object_suffix_replace",
+ ":thinlto_output_object_file",
+ ":thinlto_param_file",
+ ":thinlto_prefix_replace",
+ ":unfiltered_compile_flags",
+ ":user_compile_flags",
+ ":user_link_flags",
+ ],
+ visibility = ["//visibility:public"],
+)
diff --git a/tests/rule_based_toolchain/variables/BUILD b/tests/rule_based_toolchain/variables/BUILD
new file mode 100644
index 0000000..80928c7
--- /dev/null
+++ b/tests/rule_based_toolchain/variables/BUILD
@@ -0,0 +1,52 @@
+load("//cc/toolchains/impl:variables.bzl", "cc_builtin_variables", "cc_variable", "types")
+load("//tests/rule_based_toolchain:analysis_test_suite.bzl", "analysis_test_suite")
+load(":variables_test.bzl", "TARGETS", "TESTS")
+
+cc_variable(
+ name = "str",
+ type = types.string,
+)
+
+cc_variable(
+ name = "str_list",
+ type = types.list(types.string),
+)
+
+cc_variable(
+ name = "str_option",
+ type = types.option(types.string),
+)
+
+cc_variable(
+ name = "struct",
+ actions = ["//tests/rule_based_toolchain/actions:c_compile"],
+ type = types.struct(
+ nested_str = types.string,
+ nested_str_list = types.list(types.string),
+ ),
+)
+
+cc_variable(
+ name = "struct_list",
+ type = types.list(types.struct(
+ nested_str = types.string,
+ nested_str_list = types.list(types.string),
+ )),
+)
+
+cc_builtin_variables(
+ name = "variables",
+ srcs = [
+ ":str",
+ ":str_list",
+ ":str_option",
+ ":struct",
+ ":struct_list",
+ ],
+)
+
+analysis_test_suite(
+ name = "test_suite",
+ targets = TARGETS,
+ tests = TESTS,
+)
diff --git a/tests/rule_based_toolchain/variables/variables_test.bzl b/tests/rule_based_toolchain/variables/variables_test.bzl
new file mode 100644
index 0000000..be15a45
--- /dev/null
+++ b/tests/rule_based_toolchain/variables/variables_test.bzl
@@ -0,0 +1,128 @@
+# 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.
+"""Tests for variables rule."""
+
+load("//cc/toolchains:cc_toolchain_info.bzl", "ActionTypeInfo", "BuiltinVariablesInfo", "VariableInfo")
+load("//cc/toolchains/impl:variables.bzl", "types", _get_type = "get_type")
+load("//tests/rule_based_toolchain:subjects.bzl", "result_fn_wrapper", "subjects")
+
+visibility("private")
+
+get_type = result_fn_wrapper(_get_type)
+
+_ARGS_LABEL = Label("//:args")
+_NESTED_LABEL = Label("//:nested_vars")
+
+def _type(target):
+ return target[VariableInfo].type
+
+def _types_represent_correctly_test(env, targets):
+ env.expect.that_str(_type(targets.str_list)["repr"]).equals("List[string]")
+ env.expect.that_str(_type(targets.str_option)["repr"]).equals("Option[string]")
+ env.expect.that_str(_type(targets.struct)["repr"]).equals("struct(nested_str=string, nested_str_list=List[string])")
+ env.expect.that_str(_type(targets.struct_list)["repr"]).equals("List[struct(nested_str=string, nested_str_list=List[string])]")
+
+def _get_types_test(env, targets):
+ c_compile = targets.c_compile[ActionTypeInfo]
+ cpp_compile = targets.cpp_compile[ActionTypeInfo]
+ variables = targets.variables[BuiltinVariablesInfo].variables
+
+ def expect_type(key, overrides = {}, expr = None, actions = []):
+ return env.expect.that_value(
+ get_type(
+ variables = variables,
+ overrides = overrides,
+ args_label = _ARGS_LABEL,
+ nested_label = _NESTED_LABEL,
+ actions = actions,
+ name = key,
+ ),
+ # It's not a string, it's a complex recursive type, but string
+ # supports .equals, which is all we care about.
+ factory = subjects.result(subjects.str),
+ expr = expr or key,
+ )
+
+ expect_type("unknown").err().contains(
+ """The variable unknown does not exist. Did you mean one of the following?
+str
+str_list
+""",
+ )
+
+ expect_type("str").ok().equals(types.string)
+ expect_type("str.invalid").err().equals("""Attempted to access "str.invalid", but "str" was not a struct - it had type string.""")
+
+ expect_type("str_option").ok().equals(types.option(types.string))
+
+ expect_type("str_list").ok().equals(types.list(types.string))
+
+ expect_type("str_list.invalid").err().equals("""Attempted to access "str_list.invalid", but "str_list" was not a struct - it had type List[string].""")
+
+ expect_type("struct").ok().equals(_type(targets.struct))
+
+ expect_type("struct.nested_str_list").ok().equals(types.list(types.string))
+
+ expect_type("struct_list").ok().equals(_type(targets.struct_list))
+
+ expect_type("struct_list.nested_str_list").err().equals("""Attempted to access "struct_list.nested_str_list", but "struct_list" was not a struct - it had type List[struct(nested_str=string, nested_str_list=List[string])]. Maybe you meant to use iterate_over.""")
+
+ expect_type("struct.unknown").err().equals("""Unable to find "unknown" in "struct", which had the following attributes:
+nested_str: string
+nested_str_list: List[string]""")
+
+ expect_type("struct", actions = [c_compile]).ok()
+ expect_type("struct", actions = [c_compile, cpp_compile]).err().equals(
+ "The variable struct is inaccessible from the action %s. This is required because it is referenced in %s, which is included by %s, which references that action" % (cpp_compile.label, _NESTED_LABEL, _ARGS_LABEL),
+ )
+
+ expect_type("struct.nested_str_list", actions = [c_compile]).ok()
+ expect_type("struct.nested_str_list", actions = [c_compile, cpp_compile]).err()
+
+ # Simulate someone doing iterate_over = struct_list.
+ expect_type(
+ "struct_list",
+ overrides = {"struct_list": _type(targets.struct)},
+ expr = "struct_list_override",
+ ).ok().equals(_type(targets.struct))
+
+ expect_type(
+ "struct_list.nested_str_list",
+ overrides = {"struct_list": _type(targets.struct)},
+ ).ok().equals(types.list(types.string))
+
+ expect_type(
+ "struct_list.nested_str_list",
+ overrides = {
+ "struct_list": _type(targets.struct),
+ "struct_list.nested_str_list": types.string,
+ },
+ ).ok().equals(types.string)
+
+TARGETS = [
+ "//tests/rule_based_toolchain/actions:c_compile",
+ "//tests/rule_based_toolchain/actions:cpp_compile",
+ ":str",
+ ":str_list",
+ ":str_option",
+ ":struct",
+ ":struct_list",
+ ":variables",
+]
+
+# @unsorted-dict-items
+TESTS = {
+ "types_represent_correctly_test": _types_represent_correctly_test,
+ "get_types_test": _get_types_test,
+}