Merge pull request #237 from armandomontanez:rules-based-toolchain-example

PiperOrigin-RevId: 684024413
Change-Id: I36767bb7452d83749a48342ff447f792979da576
diff --git a/MODULE.bazel b/MODULE.bazel
index 8b5ec56..a9c1d02 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -6,6 +6,7 @@
 
 bazel_dep(name = "bazel_skylib", version = "1.7.1")
 bazel_dep(name = "platforms", version = "0.0.10")
+bazel_dep(name = "protobuf", version = "27.0")
 
 cc_configure = use_extension("//cc:extensions.bzl", "cc_configure_extension")
 use_repo(cc_configure, "local_config_cc", "local_config_cc_toolchains")
diff --git a/cc/WORKSPACE.bzlmod b/cc/WORKSPACE.bzlmod
new file mode 100644
index 0000000..0947211
--- /dev/null
+++ b/cc/WORKSPACE.bzlmod
@@ -0,0 +1,2 @@
+# A completely empty WORKSPACE file to replace the original WORKSPACE content when enabling Bzlmod.
+# No WORKSPACE prefix or suffix are added for this file.
\ No newline at end of file
diff --git a/cc/defs.bzl b/cc/defs.bzl
index a09ec03..404a63e 100644
--- a/cc/defs.bzl
+++ b/cc/defs.bzl
@@ -13,6 +13,7 @@
 # limitations under the License.
 """Starlark rules for building C++ projects."""
 
+load("@protobuf//bazel:cc_proto_library.bzl", _cc_proto_library = "cc_proto_library")
 load("//cc:cc_binary.bzl", _cc_binary = "cc_binary")
 load("//cc:cc_import.bzl", _cc_import = "cc_import")
 load("//cc:cc_library.bzl", _cc_library = "cc_library")
@@ -42,7 +43,7 @@
 objc_library = _objc_library
 objc_import = _objc_import
 
-cc_proto_library = native.cc_proto_library  # For compatibility with current users
+cc_proto_library = _cc_proto_library  # For compatibility with current users
 
 # Toolchain rules
 
diff --git a/cc/toolchains/args/sysroot.bzl b/cc/toolchains/args/sysroot.bzl
index 8662499..6898535 100644
--- a/cc/toolchains/args/sysroot.bzl
+++ b/cc/toolchains/args/sysroot.bzl
@@ -17,23 +17,26 @@
 
 visibility("public")
 
-def cc_sysroot(name, sysroot, args = [], **kwargs):
+_DEFAULT_SYSROOT_ACTIONS = [
+    Label("//cc/toolchains/actions:cpp_compile_actions"),
+    Label("//cc/toolchains/actions:c_compile"),
+    Label("//cc/toolchains/actions:link_actions"),
+]
+
+def cc_sysroot(*, name, sysroot, actions = _DEFAULT_SYSROOT_ACTIONS, args = [], **kwargs):
     """Creates args for a sysroot.
 
     Args:
       name: (str) The name of the target
       sysroot: (bazel_skylib's directory rule) The directory that should be the
         sysroot.
+      actions: (List[Label]) Actions the `--sysroot` flag should be applied to.
       args: (List[str]) Extra command-line args to add.
       **kwargs: kwargs to pass to cc_args.
     """
     cc_args(
         name = name,
-        actions = [
-            Label("//cc/toolchains/actions:cpp_compile_actions"),
-            Label("//cc/toolchains/actions:c_compile"),
-            Label("//cc/toolchains/actions:link_actions"),
-        ],
+        actions = actions,
         args = ["--sysroot={sysroot}"] + args,
         format = {"sysroot": sysroot},
         **kwargs
diff --git a/cc/toolchains/impl/legacy_converter.bzl b/cc/toolchains/impl/legacy_converter.bzl
index 64fea95..6eafc4f 100644
--- a/cc/toolchains/impl/legacy_converter.bzl
+++ b/cc/toolchains/impl/legacy_converter.bzl
@@ -24,11 +24,6 @@
     legacy_tool = "tool",
     legacy_with_feature_set = "with_feature_set",
 )
-load(
-    "//cc/toolchains:cc_toolchain_info.bzl",
-    "ArgsListInfo",
-    "FeatureInfo",
-)
 
 visibility([
     "//cc/toolchains/...",
@@ -49,11 +44,12 @@
         not_features = sorted([ft.name for ft in constraint.none_of.to_list()]),
     )
 
-def convert_args(args):
+def convert_args(args, strip_actions = False):
     """Converts an ArgsInfo to flag_sets and env_sets.
 
     Args:
         args: (ArgsInfo) The args to convert
+        strip_actions: (bool) Whether to strip the actions from the resulting flag_set.
     Returns:
         struct(flag_sets = List[flag_set], env_sets = List[env_sets])
     """
@@ -66,7 +62,7 @@
     flag_sets = []
     if args.nested != None:
         flag_sets.append(legacy_flag_set(
-            actions = actions,
+            actions = [] if strip_actions else actions,
             with_features = with_features,
             flag_groups = [args.nested.legacy_flag_group],
         ))
@@ -89,11 +85,11 @@
         env_sets = env_sets,
     )
 
-def _convert_args_sequence(args_sequence):
+def _convert_args_sequence(args_sequence, strip_actions = False):
     flag_sets = []
     env_sets = []
     for args in args_sequence:
-        legacy_args = convert_args(args)
+        legacy_args = convert_args(args, strip_actions)
         flag_sets.extend(legacy_args.flag_sets)
         env_sets.extend(legacy_args.env_sets)
 
@@ -137,13 +133,16 @@
         enabled = False,
     )
 
-def _convert_tool_map(tool_map):
+def _convert_tool_map(tool_map, args_by_action):
     action_configs = []
     caps = {}
     for action_type, tool in tool_map.configs.items():
+        action_args = args_by_action.get(action_type.name, default = None)
+        flag_sets = action_args.flag_sets if action_args != None else []
         action_configs.append(legacy_action_config(
             action_name = action_type.name,
             enabled = True,
+            flag_sets = flag_sets,
             tools = [convert_tool(tool)],
             implies = [cap.feature.name for cap in tool.capabilities],
         ))
@@ -165,24 +164,37 @@
         A struct containing parameters suitable to pass to
           cc_common.create_cc_toolchain_config_info.
     """
+
+    # Ordering of arguments is important! Intended argument ordering is:
+    #   1. Arguments listed in `args`.
+    #   2. Legacy/built-in features.
+    #   3. User-defined features.
+    # While we could just attach arguments to a feature, legacy/built-in features will appear
+    # before the user-defined features if we do not bind args directly to the action configs.
+    # For that reason, there's additional logic in this function to ensure that the args are
+    # attached to the action configs directly, as that is the only way to ensure the correct
+    # ordering.
+    args_by_action = {}
+    for a in toolchain.args.by_action:
+        args = args_by_action.setdefault(a.action.name, struct(flag_sets = [], env_sets = []))
+        new_args = _convert_args_sequence(a.args, strip_actions = True)
+        args.flag_sets.extend(new_args.flag_sets)
+        args.env_sets.extend(new_args.env_sets)
+
+    action_configs, cap_features = _convert_tool_map(toolchain.tool_map, args_by_action)
     features = [
         convert_feature(feature, enabled = feature in toolchain.enabled_features)
         for feature in toolchain.features
     ]
-    action_configs, cap_features = _convert_tool_map(toolchain.tool_map)
     features.extend(cap_features)
-    features.append(convert_feature(FeatureInfo(
+
+    features.append(legacy_feature(
         # 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",
+        name = "implied_by_always_enabled_env_sets",
         enabled = True,
-        args = ArgsListInfo(args = toolchain.args),
-        implies = depset([]),
-        requires_any_of = [],
-        mutually_exclusive = [],
-        external = False,
-        allowlist_include_directories = depset(),
-    )))
+        env_sets = _convert_args_sequence(toolchain.args.args).env_sets,
+    ))
 
     cxx_builtin_include_directories = [
         d.path
diff --git a/cc/toolchains/impl/toolchain_config_info.bzl b/cc/toolchains/impl/toolchain_config_info.bzl
index 0fa499d..3c8c65c 100644
--- a/cc/toolchains/impl/toolchain_config_info.bzl
+++ b/cc/toolchains/impl/toolchain_config_info.bzl
@@ -120,7 +120,7 @@
 
     for feature in self.features:
         _validate_feature(feature, known_features, fail = fail)
-    for args in self.args:
+    for args in self.args.args:
         _validate_args(args, known_features, fail = fail)
 
 def _collect_files_for_action_type(action_type, tool_map, features, args):
@@ -176,7 +176,7 @@
         features = features,
         enabled_features = enabled_features,
         tool_map = tool_map[ToolConfigInfo],
-        args = args.args,
+        args = args,
         files = files,
         allowlist_include_directories = allowlist_include_directories,
     )
diff --git a/tests/rule_based_toolchain/toolchain_config/BUILD b/tests/rule_based_toolchain/toolchain_config/BUILD
index 6d894a9..08b5f83 100644
--- a/tests/rule_based_toolchain/toolchain_config/BUILD
+++ b/tests/rule_based_toolchain/toolchain_config/BUILD
@@ -52,6 +52,13 @@
 
 cc_sysroot(
     name = "sysroot",
+    actions = [
+        "//cc/toolchains/actions:cpp_compile_actions",
+        "//cc/toolchains/actions:c_compile",
+        "//cc/toolchains/actions:link_actions",
+        "//tests/rule_based_toolchain/actions:c_compile",
+        "//tests/rule_based_toolchain/actions:cpp_compile",
+    ],
     sysroot = "//tests/rule_based_toolchain/testdata:directory",
 )
 
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 f54fb52..1047203 100644
--- a/tests/rule_based_toolchain/toolchain_config/toolchain_config_test.bzl
+++ b/tests/rule_based_toolchain/toolchain_config/toolchain_config_test.bzl
@@ -212,39 +212,8 @@
             enabled = False,
         ),
         legacy_feature(
-            name = "implied_by_always_enabled",
+            name = "implied_by_always_enabled_env_sets",
             enabled = True,
-            flag_sets = [
-                legacy_flag_set(
-                    actions = [
-                        "c++-compile",
-                        "c++-header-parsing",
-                        "c++-link-dynamic-library",
-                        "c++-link-executable",
-                        "c++-link-nodeps-dynamic-library",
-                        "c++-module-codegen",
-                        "c++-module-compile",
-                        "c-compile",
-                        "clif-match",
-                        "linkstamp-compile",
-                        "lto-backend",
-                        "lto-index-for-dynamic-library",
-                        "lto-index-for-executable",
-                        "lto-index-for-nodeps-dynamic-library",
-                    ],
-                    flag_groups = [
-                        legacy_flag_group(flags = [
-                            "--sysroot=tests/rule_based_toolchain/testdata",
-                        ]),
-                    ],
-                ),
-                legacy_flag_set(
-                    actions = ["c_compile"],
-                    flag_groups = [
-                        legacy_flag_group(flags = ["c_compile_args"]),
-                    ],
-                ),
-            ],
         ),
     ]).in_order()
 
@@ -257,12 +226,35 @@
             enabled = True,
             tools = [legacy_tool(tool = exe)],
             implies = ["supports_pic"],
+            flag_sets = [
+                legacy_flag_set(
+                    flag_groups = [
+                        legacy_flag_group(flags = [
+                            "--sysroot=tests/rule_based_toolchain/testdata",
+                        ]),
+                    ],
+                ),
+                legacy_flag_set(
+                    flag_groups = [
+                        legacy_flag_group(flags = ["c_compile_args"]),
+                    ],
+                ),
+            ],
         ),
         legacy_action_config(
             action_name = "cpp_compile",
             enabled = True,
             tools = [legacy_tool(tool = exe)],
             implies = [],
+            flag_sets = [
+                legacy_flag_set(
+                    flag_groups = [
+                        legacy_flag_group(flags = [
+                            "--sysroot=tests/rule_based_toolchain/testdata",
+                        ]),
+                    ],
+                ),
+            ],
         ),
     ]).in_order()