Merge pull request #257 from fmeum:validate-library

PiperOrigin-RevId: 691372917
Change-Id: Idebbc95f1e24990ec61d5224fbc28cd238a8fdbd
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index 9e66335..5929c96 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -5,12 +5,14 @@
   - "//examples/..."
   - "//tests/..."
   - "-//examples/custom_toolchain:legacy_selector" # Example only works on Linux
+  - "-//tests/rule_based_toolchain/tool_map:_duplicate_tool_test_subject" # Intentionally broken rule.
 test_targets: &test_targets
   - "//:all"
   - "//cc/..."
   - "//examples/..."
   - "//tests/..."
   - "-//examples/custom_toolchain:legacy_selector" # Example only works on Linux
+  - "-//tests/rule_based_toolchain/tool_map:_duplicate_tool_test_subject" # Intentionally broken rule.
 
 build_targets_bazel_6: &build_targets_bazel_6
   - "//:all"
@@ -20,6 +22,7 @@
   - "-//examples/custom_toolchain:legacy_selector" # Example only works on Linux
   - "-//tests/rule_based_toolchain/..." # proto.encode_text doesn't support None
   - "-//cc:optional_current_cc_toolchain" # Not supported in Bazel 6
+  - "-//tests/rule_based_toolchain/tool_map:_duplicate_tool_test_subject" # Intentionally broken rule.
 test_targets_bazel_6: &test_targets_bazel_6
   - "//:all"
   - "//cc:all"
@@ -28,6 +31,7 @@
   - "-//examples/custom_toolchain:legacy_selector" # Example only works on Linux
   - "-//tests/rule_based_toolchain/..." # proto.encode_text doesn't support None
   - "-//cc:optional_current_cc_toolchain" # Not supported in Bazel 6
+  - "-//tests/rule_based_toolchain/tool_map:_duplicate_tool_test_subject" # Intentionally broken rule.
 
 buildifier:
   version: latest
@@ -75,6 +79,7 @@
       - "//tests/..."
       - "-//examples/custom_toolchain:legacy_selector" # Example only works on Linux
       - "-//tests/system_library:system_library_test" # Fails because of repo setup
+      - "-//tests/rule_based_toolchain/tool_map:_duplicate_tool_test_subject" # Intentionally broken rule.
   macos_head:
     name: MacOS (Bazel HEAD)
     bazel: last_green
diff --git a/CODEOWNERS b/CODEOWNERS
index 85a388b..dd7518c 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1 +1 @@
-* @oquenchil @c-mita @comius @buildbreaker2021
+* @trybka @matts1 @armandomontanez @pzembrod @comius @c-mita @hvadehra
diff --git a/MODULE.bazel b/MODULE.bazel
index cca1a2d..9f7f4aa 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -4,6 +4,7 @@
     compatibility_level = 1,
 )
 
+bazel_dep(name = "bazel_features", version = "1.19.0")
 bazel_dep(name = "bazel_skylib", version = "1.7.1")
 bazel_dep(name = "platforms", version = "0.0.10")
 bazel_dep(name = "protobuf", version = "27.0")
diff --git a/cc/compiler/BUILD b/cc/compiler/BUILD
index eecccaa..2f81d74 100644
--- a/cc/compiler/BUILD
+++ b/cc/compiler/BUILD
@@ -69,3 +69,8 @@
     name = "msvc-cl",
     flag_values = {"@rules_cc//cc/private/toolchain:compiler": "msvc-cl"},
 )
+
+config_setting(
+    name = "emscripten",
+    flag_values = {"@rules_cc//cc/private/toolchain:compiler": "emscripten"},
+)
diff --git a/cc/extensions.bzl b/cc/extensions.bzl
index 1ac80dc..0d73141 100644
--- a/cc/extensions.bzl
+++ b/cc/extensions.bzl
@@ -13,11 +13,15 @@
 # limitations under the License.
 """Module extension for cc auto configuration."""
 
+load("@bazel_features//:features.bzl", "bazel_features")
 load("//cc/private/toolchain:cc_configure.bzl", "cc_autoconf", "cc_autoconf_toolchains")
 
 def _cc_configure_extension_impl(ctx):
     cc_autoconf_toolchains(name = "local_config_cc_toolchains")
     cc_autoconf(name = "local_config_cc")
-    return ctx.extension_metadata(reproducible = True)
+    if bazel_features.external_deps.extension_metadata_has_reproducible:
+        return ctx.extension_metadata(reproducible = True)
+    else:
+        return None
 
 cc_configure_extension = module_extension(implementation = _cc_configure_extension_impl)
diff --git a/cc/toolchains/README.md b/cc/toolchains/README.md
index 42266b7..fc8f1ed 100644
--- a/cc/toolchains/README.md
+++ b/cc/toolchains/README.md
@@ -1,333 +1,7 @@
-# Writing a custom rule_based C++ toolchain with rule-based definition.
+# Toolchain rules
+This directory contains a suite of rules for defining C/C++ toolchain
+configurations.
 
-Work in progress!
-
-This document serves two purposes:
-* Until complete, this serves as an agreement for the final user-facing API. 
-* Once complete, this will serve as onboarding documentation.
-
-This section will be removed once complete.
-
-## Step 1: Define tools
-A tool is simply a binary. Just like any other bazel binary, a tool can specify
-additional files required to run.
-
-We can use any bazel binary as an input to anything that requires tools. In the
-example below, you could use both clang and ld as tools.
-
-```
-# @sysroot//:BUILD
-cc_tool(
-    name = "clang",
-    exe = ":bin/clang",
-    execution_requirements = ["requires-mem:24g"],
-    data = [...],
-)
-
-sh_binary(
-    name = "ld",
-    srcs = ["ld_wrapper.sh"],
-    data = [":bin/ld"],
-)
-    
-```
-
-## Step 2: Generate action configs from those tools
-An action config is a mapping from action to:
-
-* A list of tools, (the first one matching the execution requirements is used).
-* A list of args and features that are always enabled for the action
-* A set of additional files required for the action
-
-Each action can only be specified once in the toolchain. Specifying multiple
-actions in a single `cc_action_type_config` is just a shorthand for specifying the
-same config for every one of those actions.
-
-If you're already familiar with how to define toolchains, the additional files
-is a replacement for `compile_files`, `link_files`, etc.
-
-Additionally, to replace `all_files`, we add `cc_additional_files_for_actions`.
-This allows you to specify that particular files are required for particular
-actions.
-
-We provide `additional_files` on the `cc_action_type_config` as a shorthand for 
-specifying `cc_additional_files_for_actions`
-
-Warning: Implying a feature that is not listed directly in the toolchain will throw
-an error. This is to ensure you don't accidentally add a feature to the
-toolchain.
-
-```
-cc_action_type_config(
-    name  = "c_compile",
-    actions = ["@rules_cc//actions:all_c_compile"],
-    tools = ["@sysroot//:clang"],
-    args = [":my_args"],
-    implies = [":my_feature"],
-    additional_files = ["@sysroot//:all_header_files"],
-)
-
-cc_additional_files_for_actions(
-    name = "all_action_files",
-    actions = ["@rules_cc//actions:all_actions"],
-    additional_files = ["@sysroot//:always_needed_files"]
-)
-```
-
-## Step 3: Define some arguments
-Arguments are our replacement for `flag_set` and `env_set`. To add arguments to
-our tools, we take heavy inspiration from bazel's
-[`Args`](https://bazel.build/rules/lib/builtins/Args) type. We provide the same
-API, with the following caveats:
-* `actions` specifies which actions the arguments apply to (same as `flag_set`).
-* `requires_any_of` is equivalent to `with_features` on the `flag_set`.
-* `args` may be used instead of `add` if your command-line is only strings.
-* `env` may be used to add environment variables to the arguments. Environment
-  variables set by later args take priority.
-* By default, all inputs are automatically added to the corresponding actions.
-  `additional_files` specifies files that are required for an action when using
-  that argument.
-
-```
-cc_args(
-    name = "inline",
-    actions = ["@rules_cc//actions:all_cpp_compile_actions"],
-    args = ["--foo"],
-    requires_any_of = [":feature"]
-    env = {"FOO": "bar"},
-    additional_files = [":file"],
-)
-```
-
-For more complex use cases, we use the same API as `Args`. Values are either:
-* A list of files (or a single file for `cc_add_args`).
-* Something returning `CcVariableInfo`, which is equivalent to a list of strings.
-
-```
-cc_variable(
-  name = "bar_baz",
-  values = ["bar", "baz"],
-)
-
-# Expands to CcVariableInfo(values = ["x86_64-unknown-linux-gnu"])
-custom_variable_rule(
-  name = "triple",
-  ...
-)
-
-# Taken from https://bazel.build/rules/lib/builtins/Args#add
-cc_add_args(
-    name = "single",
-    arg_name = "--platform",
-    value = ":triple", # Either a single file or a cc_variable
-    format = "%s",
-)
-
-# Taken from https://bazel.build/rules/lib/builtins/Args#add_all
-cc_add_args_all(
-    name = "multiple",
-    arg_name = "--foo",
-    values = [":file", ":file_set"], # Either files or cc_variable.
-    # map_each not supported. Write a custom rule if you want that.
-    format_each = "%s",
-    before_each = "--foo",
-    omit_if_empty = True,
-    uniquify = False,
-    # Expand_directories not yet supported.
-    terminate_with = "foo",
-)
-
-# Taken from https://bazel.build/rules/lib/builtins/Args#add_joined
-cc_add_args_joined(
-    name = "joined",
-    arg_name = "--foo",
-    values = [":file", ":file_set"], # Either files or cc_variable.
-    join_with = ",",
-    # map_each not supported. Write a custom rule if you want that.
-    format_each = "%s",
-    format_joined = "--foo=%s",
-    omit_if_empty = True,
-    uniquify = False,
-    # Expand_directories not yet supported.
-)
-
-cc_args(
-    name = "complex",
-    actions = ["@rules_cc//actions:c_compile"],
-    add = [":single", ":multiple", ":joined"],
-)
-
-cc_args_list(
-    name = "all_flags",
-    args = [":inline", ":complex"],
-)
-```
-
-## Step 4: Define some features
-A feature is a set of args and configurations that can be enabled or disabled.
-
-Although the existing toolchain recommends using features to avoid duplication
-of definitions, we recommend avoiding using features unless you want the user to
-be able to enable / disable the feature themselves. This is because we provide
-alternatives such as `cc_args_list` to allow combining arguments and
-specifying them on each action in the action config.
-
-```
-cc_feature(
-    name = "my_feature",
-    feature_name = "my_feature",
-    args = [":all_args"],
-    implies = [":other_feature"],
-)
-```
-
-## Step 5: Generate the toolchain
-The `cc_toolchain` macro:
-
-* Performs validation on the inputs (eg. no two action configs for a single
-  action)
-* Converts the type-safe providers to the unsafe ones in
-  `cc_toolchain_config_lib.bzl`
-* Generates a set of providers for each of the filegroups respectively
-* Generates the appropriate `native.cc_toolchain` invocation.
-
-```
-cc_toolchain(
-    name = "toolchain",
-    features = [":my_feature"]
-    unconditional_args = [":all_warnings"],
-    action_type_configs = [":c_compile"],
-    additional_files = [":all_action_files"],
-)
-```
-
-# Ancillary components for type-safe toolchains.
-## Well-known features
-Well-known features will be defined in `@rules_cc//features/well_known:*`.
-Any feature with `feature_name` in the well known features will have to specify
-overrides.
-
-`cc_toolchain` is aware of the builtin / well-known features. In order to
-ensure that a user understands that this overrides the builtin opt feature (I
-originally thought that it added extra flags to opt, but you still got the
-default ones, so that can definitely happen), and to ensure that they don't
-accidentally do so, we will force them to explicitly specify that it overrides
-the builtin one. This is essentially just an acknowledgement of "I know what
-I'm doing".
-
-Warning: Specifying two features with the same name is an error, unless one
-overrides the other. 
-
-```
-cc_feature(
-    name = "opt",
-    ...,
-    overrides = "@rules_cc//features/well_known:opt",
-)
-```
-
-In addition to well-known features, we could also consider in future iterations
-to also use known features for partial migrations, where you still imply a
-feature that's still defined by the legacy API:
-
-```
-# Implementation
-def cc_legacy_features(name, features):
-  for feature in features:
-    cc_known_feature(name = name + "_" + feature.name)
-  cc_legacy_features(name = name, features = FEATURES)
-
-
-# Build file
-FOO = feature(name = "foo", args=[arg_group(...)])
-FEATURES = [FOO]
-cc_legacy_features(name = "legacy_features", features = FEATURES)
-
-cc_feature(name = "bar", implies = [":legacy_features_foo"])
-
-cc_toolchain(
-  name = "toolchain",
-  legacy_features = ":legacy_features",
-  features = [":bar"],
-)
-```
-
-## Mutual exclusion
-Features can be mutually exclusive.
-
-We allow two approaches to mutual exclusion - via features or via categories.
-
-The existing toolchain uses `provides` for both of these. We rename it so that
-it makes more sense semantically.
-
-```
-cc_feature(
-   name = "incompatible_with_my_feature",
-   feature_name = "bar",
-   mutually_exclusive = [":my_feature"],
-)
-
-
-# This is an example of how we would define compilation mode.
-# Since it already exists, this wouldn't work.
-cc_mutual_exclusion_category(
-    name = "compilation_mode",
-)
-
-cc_feature(
-    name = "opt",
-    ...
-    mutually_exclusive = [":compilation_mode"],
-)
-cc_feature(
-    name = "dbg",
-    ...
-    mutually_exclusive = [":compilation_mode"],
-)
-```
-
-## Feature requirements
-Feature requirements can come in two formats.
-
-For example:
-
-* Features can require some subset of features to be enabled.
-* Arguments can require some subset of features to be enabled, but others to be
-  disabled.
-
-This is very confusing for toolchain authors, so we will simplify things with
-the use of providers:
-
-* `cc_feature` will provide `feature`, `feature_set`, and `with_feature`
-* `cc_feature_set` will provide `feature_set` and `with_feature`.
-* `cc_feature_constraint` will provide `with_features` only.
-
-We will rename all `with_features` and `requires` to `requires_any_of`, to make
-it very clear that only one of the requirements needs to be met.
-
-```
-cc_feature_set(
-    name = "my_feature_set",
-    all_of = [":my_feature"],
-)
-
-cc_feature_constraint(
-    name = "my_feature_constraint",
-    all_of = [":my_feature"],
-    none_of = [":my_other_feature"],
-)
-
-cc_args(
-   name = "foo",
-   # All of these provide with_feature.
-   requires_any_of = [":my_feature", ":my_feature_set", ":my_feature_constraint"]
-)
-
-# my_feature_constraint would be an error here.
-cc_feature(
-   name = "foo",
-   # Both of these provide feature_set.
-   requires_any_of = [":my_feature", ":my_feature_set"]
-   implies = [":my_other_feature", :my_other_feature_set"],
-)
-```
+For a living example, see
+[`//examples/rule_based_toolchain`](https://github.com/bazelbuild/rules_cc/tree/main/examples/rule_based_toolchain/).
+For the full API, see [`//third_party/bazel_rules/docs:toolchain_api.md`](https://github.com/bazelbuild/rules_cc/tree/main/docs/toolchain_api.md).