Pass feature flags in from aspect hints down to cc_bindings_from_rs.

This is not completely tested, because bazel unit testing is very troublesome, though I can do it. It's my hope I can get away with integration tests when this is actually used, and golden/unit tests for the trivial stuff.

PiperOrigin-RevId: 672700966
Change-Id: Icc5ad759afa077b832747bef22e6639194a63f46
diff --git a/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl b/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
index 06ebc7b..d3c282d 100644
--- a/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
+++ b/cc_bindings_from_rs/bazel_support/cc_bindings_from_rust_rule.bzl
@@ -42,6 +42,10 @@
     "GeneratedBindingsInfo",
 )
 load(
+    "//features:crubit_feature_hint.bzl",
+    "find_crubit_features",
+)
+load(
     "//rs_bindings_from_cc/bazel_support:compile_rust.bzl",
     "compile_rust",
 )
@@ -89,7 +93,7 @@
       rustc_env: `rustc` environment to use when running `cc_bindings_from_rs`
 
     Returns:
-      The GeneratedBindingsInfo provider.
+      A tuple of (GeneratedBindingsInfo, features).
     """
     h_out_file = ctx.actions.declare_file(basename + "_cc_api.h")
     rs_out_file = ctx.actions.declare_file(basename + "_cc_api_impl.rs")
@@ -108,6 +112,13 @@
         for header in dep_bindings_info.headers:
             arg = dep_bindings_info.crate_key + "=" + header.short_path
             crubit_args.add("--bindings-from-dependency", arg)
+        for feature in dep_bindings_info.features:
+            arg = dep_bindings_info.crate_key + "=" + feature
+            crubit_args.add("--crate-feature", arg)
+
+    features = find_crubit_features(target, ctx)
+    for feature in features:
+        crubit_args.add("--crate-feature", "self=" + feature)
 
     outputs = [h_out_file, rs_out_file]
     if ctx.attr._generate_error_report[BuildSettingInfo].value:
@@ -145,11 +156,13 @@
         arguments = [args.process_wrapper_flags, "--", ctx.executable._cc_bindings_from_rs_tool.path, crubit_args, "--", args.rustc_flags, "-Cpanic=abort"],
     )
 
-    return GeneratedBindingsInfo(
+    generated_bindings_info = GeneratedBindingsInfo(
         h_file = h_out_file,
         rust_file = rs_out_file,
     )
 
+    return generated_bindings_info, features
+
 def _make_cc_info_for_h_out_file(ctx, h_out_file, cc_infos):
     """Creates and returns CcInfo for the generated ..._cc_api.h header file.
 
@@ -303,7 +316,7 @@
         skip_expanding_rustc_env = True,
     )
 
-    bindings_info = _generate_bindings(
+    bindings_info, features = _generate_bindings(
         ctx,
         target,
         basename,
@@ -327,6 +340,7 @@
             cc_info = cc_info,
             crate_key = crate_info.name,
             headers = [bindings_info.h_file],
+            features = features,
         ),
         bindings_info,
         OutputGroupInfo(out = depset([bindings_info.h_file, bindings_info.rust_file])),
@@ -388,6 +402,9 @@
         "_generate_error_report": attr.label(
             default = "//cc_bindings_from_rs/bazel_support:generate_error_report",
         ),
+        "_globally_enabled_features": attr.label(
+            default = "//rs_bindings_from_cc/bazel_support:globally_enabled_features",
+        ),
     },
     toolchains = [
         "@rules_rust//rust:toolchain_type",
diff --git a/cc_bindings_from_rs/bazel_support/providers.bzl b/cc_bindings_from_rs/bazel_support/providers.bzl
index c555f46..6ec0dcc 100644
--- a/cc_bindings_from_rs/bazel_support/providers.bzl
+++ b/cc_bindings_from_rs/bazel_support/providers.bzl
@@ -13,6 +13,7 @@
         # flags.
         "crate_key": "String with a crate key to use in --other-crate-bindings",
         "headers": "A list of C++ headers which correspond to this crate.",
+        "features": "A list of features enabled for the bindings for this crate.",
     },
 )
 
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 939f71f..de8f624 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -30,7 +30,7 @@
 use rustc_middle::mir::ConstValue;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty::{self, Ty, TyCtxt}; // See <internal link>/ty.html#import-conventions
-use rustc_span::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
+use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_target::abi::{
     Abi, AddressSpace, FieldsShape, Integer, Layout, Pointer, Primitive, Scalar,
@@ -66,7 +66,8 @@
         #[input]
         fn crate_name_to_include_paths(&self) -> Rc<HashMap<Rc<str>, Vec<CcInclude>>>;
 
-        /// A map from a crate name to the features enabled on that crate.
+        /// A map from a crate name to the features enabled on that crate. The special name `self`
+        /// refers to the current crate.
         // TODO(b/271857814): A crate name might not be globally unique - the key needs to also cover
         // a "hash" of the crate version and compilation flags.
         #[input]
@@ -125,9 +126,22 @@
 
     let top_comment = {
         let crate_name = tcx.crate_name(LOCAL_CRATE);
+        let crubit_features = {
+            let mut crubit_features: Vec<&str> = crate_features(db, LOCAL_CRATE)
+                .into_iter()
+                .map(|feature| feature.short_name())
+                .collect();
+            crubit_features.sort();
+            if crubit_features.is_empty() {
+                "<none>".to_string()
+            } else {
+                crubit_features.join(", ")
+            }
+        };
         let txt = format!(
             "Automatically @generated C++ bindings for the following Rust crate:\n\
-             {crate_name}"
+             {crate_name}\n\
+             Features: {crubit_features}"
         );
         quote! { __COMMENT__ #txt __NEWLINE__ }
     };
@@ -164,6 +178,19 @@
     Ok(Output { h_body, rs_body })
 }
 
+fn crate_features(
+    db: &dyn BindingsGenerator,
+    krate: CrateNum,
+) -> flagset::FlagSet<crubit_feature::CrubitFeature> {
+    let crate_features = db.crate_name_to_features();
+    let features = if krate == LOCAL_CRATE {
+        crate_features.get("self")
+    } else {
+        crate_features.get(db.tcx().crate_name(krate).as_str())
+    };
+    features.copied().unwrap_or_default()
+}
+
 #[derive(Clone, Debug, Default)]
 struct CcPrerequisites {
     /// Set of `#include`s that a `CcSnippet` depends on.  For example if
@@ -4035,7 +4062,8 @@
         test_generated_bindings(test_src, |bindings| {
             let bindings = bindings.unwrap();
             let expected_comment_txt = "Automatically @generated C++ bindings for the following Rust crate:\n\
-                 rust_out";
+                 rust_out\n\
+                 Features: <none>";
             assert_cc_matches!(
                 bindings.h_body,
                 quote! {
diff --git a/cc_bindings_from_rs/cc_bindings_from_rs.rs b/cc_bindings_from_rs/cc_bindings_from_rs.rs
index df50db9..a2ff39f 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -375,6 +375,7 @@
                 "{}\n{}\n{}",
 r#"// Automatically @generated C++ bindings for the following Rust crate:
 // test_crate
+// Features: <none>
 
 // clang-format off
 #pragma once
@@ -409,6 +410,7 @@
             &rs_body,
             r#"// Automatically @generated C++ bindings for the following Rust crate:
 // test_crate
+// Features: <none>
 
 #![allow(improper_ctypes_definitions)]
 
diff --git a/cc_bindings_from_rs/cmdline.rs b/cc_bindings_from_rs/cmdline.rs
index 44995c5..f792695 100644
--- a/cc_bindings_from_rs/cmdline.rs
+++ b/cc_bindings_from_rs/cmdline.rs
@@ -51,7 +51,8 @@
     /// Feature flags enabled for a given crate. Keys are crate names, and
     /// values are feature flags. All crates must have their features fully
     /// specified, and consistently across rustc invocations, or the
-    /// behavior is undefined.
+    /// behavior is undefined. As a special case, the crate name `self`
+    /// refers to the current crate, whose bindings are being generated.
     ///
     /// Example: "--crate-feature=foo=experimental".
     #[clap(long = "crate-feature", value_parser = parse_crate_feature,
@@ -273,7 +274,7 @@
           [aliases: bindings-from-dependency]
 
       --crate-feature <CRATE_NAME=CRUBIT_FEATURE>
-          Feature flags enabled for a given crate. Keys are crate names, and values are feature flags. All crates must have their features fully specified, and consistently across rustc invocations, or the behavior is undefined.
+          Feature flags enabled for a given crate. Keys are crate names, and values are feature flags. All crates must have their features fully specified, and consistently across rustc invocations, or the behavior is undefined. As a special case, the crate name `self` refers to the current crate, whose bindings are being generated.
           
           Example: "--crate-feature=foo=experimental".
 
diff --git a/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api.h b/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api.h
index 1e4cf89..750ce1f 100644
--- a/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api.h
+++ b/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api.h
@@ -4,6 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // struct_with_conflicting_fields_and_member_functions_rust
+// Features: experimental, supported
 
 // clang-format off
 #pragma once
diff --git a/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api_impl.rs b/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api_impl.rs
index 7ec0563..24ac14b 100644
--- a/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/golden/struct_with_conflicting_fields_and_member_functions_cc_api_impl.rs
@@ -4,6 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // struct_with_conflicting_fields_and_member_functions_rust
+// Features: experimental, supported
 
 #![allow(improper_ctypes_definitions)]
 
diff --git a/cc_bindings_from_rs/test/golden/type_aliases_cc_api.h b/cc_bindings_from_rs/test/golden/type_aliases_cc_api.h
index 8f94096..7a05a1a 100644
--- a/cc_bindings_from_rs/test/golden/type_aliases_cc_api.h
+++ b/cc_bindings_from_rs/test/golden/type_aliases_cc_api.h
@@ -4,6 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // type_aliases_rust
+// Features: experimental, supported
 
 // clang-format off
 #pragma once
diff --git a/cc_bindings_from_rs/test/golden/type_aliases_cc_api_impl.rs b/cc_bindings_from_rs/test/golden/type_aliases_cc_api_impl.rs
index dd022fb..a15cd1b 100644
--- a/cc_bindings_from_rs/test/golden/type_aliases_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/golden/type_aliases_cc_api_impl.rs
@@ -4,6 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // type_aliases_rust
+// Features: experimental, supported
 
 #![allow(improper_ctypes_definitions)]
 
diff --git a/cc_bindings_from_rs/test/golden/uses_cc_api.h b/cc_bindings_from_rs/test/golden/uses_cc_api.h
index b807fbd..76228d3 100644
--- a/cc_bindings_from_rs/test/golden/uses_cc_api.h
+++ b/cc_bindings_from_rs/test/golden/uses_cc_api.h
@@ -4,6 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // uses_rust
+// Features: experimental, supported
 
 // clang-format off
 #pragma once
diff --git a/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs b/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs
index 5605457..00ac247 100644
--- a/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs
+++ b/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs
@@ -4,6 +4,7 @@
 
 // Automatically @generated C++ bindings for the following Rust crate:
 // uses_rust
+// Features: experimental, supported
 
 #![allow(improper_ctypes_definitions)]
 
diff --git a/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl b/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl
index 28cda46..7143fa3 100644
--- a/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl
+++ b/rs_bindings_from_cc/bazel_support/rust_bindings_from_cc_utils.bzl
@@ -145,6 +145,7 @@
             cc_info = cc_info,
             crate_key = dep_variant_info.crate_info.name,
             headers = public_hdrs,
+            features = [],
         ),
     ]