add a way to distinguish proc-macro deps (#1420)
diff --git a/BUILD.bazel b/BUILD.bazel
index ff49a9c..d4cbb9a 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,5 +1,5 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
-load("//rust:defs.bzl", "capture_clippy_output", "clippy_flags", "error_format", "extra_exec_rustc_flag", "extra_exec_rustc_flags", "extra_rustc_flag", "extra_rustc_flags")
+load("//rust:defs.bzl", "capture_clippy_output", "clippy_flags", "error_format", "extra_exec_rustc_flag", "extra_exec_rustc_flags", "extra_rustc_flag", "extra_rustc_flags", "is_proc_macro_dep", "is_proc_macro_dep_enabled")
exports_files(["LICENSE"])
@@ -45,6 +45,24 @@
visibility = ["//visibility:public"],
)
+# This setting may be used to identify dependencies of proc-macro-s.
+# This feature is only enabled if `is_proc_macro_dep_enabled` is true.
+# Its value controls the BAZEL_RULES_RUST_IS_PROC_MACRO_DEP environment variable
+# made available to the rustc invocation.
+is_proc_macro_dep(
+ name = "is_proc_macro_dep",
+ build_setting_default = False,
+ visibility = ["//visibility:public"],
+)
+
+# This setting enables the feature to identify dependencies of proc-macro-s,
+# see `is_proc_macro_dep`.
+is_proc_macro_dep_enabled(
+ name = "is_proc_macro_dep_enabled",
+ build_setting_default = False,
+ visibility = ["//visibility:public"],
+)
+
# This setting may be used to pass extra options to rustc from the command line
# in non-exec configuration.
# It applies across all targets whereas the rustc_flags option on targets applies only
diff --git a/rust/defs.bzl b/rust/defs.bzl
index a0560b5..92c8a77 100644
--- a/rust/defs.bzl
+++ b/rust/defs.bzl
@@ -47,6 +47,8 @@
_extra_exec_rustc_flags = "extra_exec_rustc_flags",
_extra_rustc_flag = "extra_rustc_flag",
_extra_rustc_flags = "extra_rustc_flags",
+ _is_proc_macro_dep = "is_proc_macro_dep",
+ _is_proc_macro_dep_enabled = "is_proc_macro_dep_enabled",
)
load(
"//rust/private:rustdoc.bzl",
@@ -116,6 +118,12 @@
extra_exec_rustc_flags = _extra_exec_rustc_flags
# See @rules_rust//rust/private:rustc.bzl for a complete description.
+is_proc_macro_dep = _is_proc_macro_dep
+# See @rules_rust//rust/private:rustc.bzl for a complete description.
+
+is_proc_macro_dep_enabled = _is_proc_macro_dep_enabled
+# See @rules_rust//rust/private:rustc.bzl for a complete description.
+
rust_common = _rust_common
# See @rules_rust//rust/private:common.bzl for a complete description.
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index ce61713..5b02292 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -685,6 +685,12 @@
default = Label("//util/import"),
cfg = "exec",
),
+ "_is_proc_macro_dep": attr.label(
+ default = Label("//:is_proc_macro_dep"),
+ ),
+ "_is_proc_macro_dep_enabled": attr.label(
+ default = Label("//:is_proc_macro_dep_enabled"),
+ ),
"_process_wrapper": attr.label(
doc = "A process wrapper for running rustc on all platforms.",
default = Label("//util/process_wrapper"),
@@ -866,10 +872,38 @@
"""),
)
+def _proc_macro_dep_transition_impl(settings, _attr):
+ if settings["//:is_proc_macro_dep_enabled"]:
+ return {"//:is_proc_macro_dep": True}
+ else:
+ return []
+
+_proc_macro_dep_transition = transition(
+ inputs = ["//:is_proc_macro_dep_enabled"],
+ outputs = ["//:is_proc_macro_dep"],
+ implementation = _proc_macro_dep_transition_impl,
+)
+
rust_proc_macro = rule(
implementation = _rust_proc_macro_impl,
provides = _common_providers,
- attrs = dict(_common_attrs.items()),
+ # Start by copying the common attributes, then override the `deps` attribute
+ # to apply `_proc_macro_dep_transition`. To add this transition we additionally
+ # need to declare `_allowlist_function_transition`, see
+ # https://docs.bazel.build/versions/main/skylark/config.html#user-defined-transitions.
+ attrs = dict(
+ _common_attrs.items(),
+ _allowlist_function_transition = attr.label(default = Label("//tools/allowlists/function_transition_allowlist")),
+ deps = attr.label_list(
+ doc = dedent("""\
+ List of other libraries to be linked to this library target.
+
+ These can be either other `rust_library` targets or `cc_library` targets if
+ linking a native library.
+ """),
+ cfg = _proc_macro_dep_transition,
+ ),
+ ),
fragments = ["cpp"],
host_fragments = ["cpp"],
toolchains = [
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 44b799c..fbd8dd0 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -61,6 +61,34 @@
fields = {"extra_exec_rustc_flags": "List[string] Extra flags to pass to rustc in exec configuration"},
)
+IsProcMacroDepInfo = provider(
+ doc = "Records if this is a transitive dependency of a proc-macro.",
+ fields = {"is_proc_macro_dep": "Boolean"},
+)
+
+def _is_proc_macro_dep_impl(ctx):
+ return IsProcMacroDepInfo(is_proc_macro_dep = ctx.build_setting_value)
+
+is_proc_macro_dep = rule(
+ doc = "Records if this is a transitive dependency of a proc-macro.",
+ implementation = _is_proc_macro_dep_impl,
+ build_setting = config.bool(flag = True),
+)
+
+IsProcMacroDepEnabledInfo = provider(
+ doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
+ fields = {"enabled": "Boolean"},
+)
+
+def _is_proc_macro_dep_enabled_impl(ctx):
+ return IsProcMacroDepEnabledInfo(enabled = ctx.build_setting_value)
+
+is_proc_macro_dep_enabled = rule(
+ doc = "Enables the feature to record if a library is a transitive dependency of a proc-macro.",
+ implementation = _is_proc_macro_dep_enabled_impl,
+ build_setting = config.bool(flag = True),
+)
+
def _get_rustc_env(attr, toolchain, crate_name):
"""Gathers rustc environment variables
@@ -78,7 +106,7 @@
patch, pre = patch.split("-", 1)
else:
pre = ""
- return {
+ result = {
"CARGO_CFG_TARGET_ARCH": toolchain.target_arch,
"CARGO_CFG_TARGET_OS": toolchain.os,
"CARGO_CRATE_NAME": crate_name,
@@ -92,6 +120,12 @@
"CARGO_PKG_VERSION_PATCH": patch,
"CARGO_PKG_VERSION_PRE": pre,
}
+ if hasattr(attr, "_is_proc_macro_dep_enabled") and attr._is_proc_macro_dep_enabled[IsProcMacroDepEnabledInfo].enabled:
+ is_proc_macro_dep = "0"
+ if hasattr(attr, "_is_proc_macro_dep") and attr._is_proc_macro_dep[IsProcMacroDepInfo].is_proc_macro_dep:
+ is_proc_macro_dep = "1"
+ result["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"] = is_proc_macro_dep
+ return result
def get_compilation_mode_opts(ctx, toolchain):
"""Gathers rustc flags for the current compilation mode (opt/debug)
diff --git a/test/unit/is_proc_macro_dep/BUILD.bazel b/test/unit/is_proc_macro_dep/BUILD.bazel
new file mode 100644
index 0000000..ac655a5
--- /dev/null
+++ b/test/unit/is_proc_macro_dep/BUILD.bazel
@@ -0,0 +1,5 @@
+load(":is_proc_macro_dep_test.bzl", "is_proc_macro_dep_test_suite")
+
+is_proc_macro_dep_test_suite(
+ name = "is_proc_macro_dep_test_suite",
+)
diff --git a/test/unit/is_proc_macro_dep/is_proc_macro_dep_test.bzl b/test/unit/is_proc_macro_dep/is_proc_macro_dep_test.bzl
new file mode 100644
index 0000000..eb947bd
--- /dev/null
+++ b/test/unit/is_proc_macro_dep/is_proc_macro_dep_test.bzl
@@ -0,0 +1,169 @@
+"""Unittests for the is_proc_macro_dep setting."""
+
+load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
+load("//rust:defs.bzl", "rust_library", "rust_proc_macro")
+
+DepActionsInfo = provider(
+ "Contains information about dependencies actions.",
+ fields = {"actions": "List[Action]"},
+)
+
+def _collect_dep_actions_aspect_impl(target, ctx):
+ actions = []
+ actions.extend(target.actions)
+ for dep in ctx.rule.attr.deps:
+ actions.extend(dep[DepActionsInfo].actions)
+ return [DepActionsInfo(actions = actions)]
+
+collect_dep_actions_aspect = aspect(
+ implementation = _collect_dep_actions_aspect_impl,
+ attr_aspects = ["deps"],
+)
+
+def _attach_dep_actions_aspect_impl(ctx):
+ return [ctx.attr.dep[DepActionsInfo]]
+
+attach_dep_actions_aspect = rule(
+ implementation = _attach_dep_actions_aspect_impl,
+ attrs = {
+ "dep": attr.label(aspects = [collect_dep_actions_aspect]),
+ },
+)
+
+def _enable_is_proc_macro_dep_transition_impl(_settings, _attr):
+ return {"//:is_proc_macro_dep_enabled": True}
+
+enable_is_proc_macro_dep_transition = transition(
+ inputs = [],
+ outputs = ["//:is_proc_macro_dep_enabled"],
+ implementation = _enable_is_proc_macro_dep_transition_impl,
+)
+
+attach_dep_actions_and_enable_is_proc_macro_dep_aspect = rule(
+ implementation = _attach_dep_actions_aspect_impl,
+ attrs = {
+ "dep": attr.label(aspects = [collect_dep_actions_aspect]),
+ "_allowlist_function_transition": attr.label(default = Label("//tools/allowlists/function_transition_allowlist")),
+ },
+ cfg = enable_is_proc_macro_dep_transition,
+)
+
+def _is_proc_macro_dep_is_not_in_env_for_top_level_action(ctx):
+ env = analysistest.begin(ctx)
+ top_level_action = analysistest.target_under_test(env).actions[0]
+ asserts.false(env, "BAZEL_RULES_RUST_IS_PROC_MACRO_DEP" in top_level_action.env)
+ return analysistest.end(env)
+
+is_proc_macro_dep_is_not_in_env_for_top_level_action_test = analysistest.make(_is_proc_macro_dep_is_not_in_env_for_top_level_action)
+
+def _is_proc_macro_dep_is_false_for_proc_macro(ctx):
+ env = analysistest.begin(ctx)
+ tut = analysistest.target_under_test(env)
+ proc_macro_dep_action = [a for a in tut[DepActionsInfo].actions if str(a) == "action 'Compiling Rust proc-macro proc_macro_crate (1 files)'"][0]
+ asserts.equals(env, proc_macro_dep_action.env["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"], "0")
+ return analysistest.end(env)
+
+is_proc_macro_dep_is_false_for_proc_macro_test = analysistest.make(_is_proc_macro_dep_is_false_for_proc_macro)
+
+def _is_proc_macro_dep_is_false_for_top_level_library(ctx):
+ env = analysistest.begin(ctx)
+ tut = analysistest.target_under_test(env)
+ top_level_library_action = [a for a in tut[DepActionsInfo].actions if str(a) == "action 'Compiling Rust rlib top_level_library (1 files)'"][0]
+ asserts.equals(env, top_level_library_action.env["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"], "0")
+ return analysistest.end(env)
+
+is_proc_macro_dep_is_false_for_top_level_library_test = analysistest.make(_is_proc_macro_dep_is_false_for_top_level_library)
+
+def _is_proc_macro_dep_is_true_for_proc_macro_dep(ctx):
+ env = analysistest.begin(ctx)
+ tut = analysistest.target_under_test(env)
+ proc_macro_dep_action = [a for a in tut[DepActionsInfo].actions if str(a) == "action 'Compiling Rust rlib proc_macro_dep (1 files)'"][0]
+ asserts.equals(env, proc_macro_dep_action.env["BAZEL_RULES_RUST_IS_PROC_MACRO_DEP"], "1")
+ return analysistest.end(env)
+
+is_proc_macro_dep_is_true_for_proc_macro_dep_test = analysistest.make(_is_proc_macro_dep_is_true_for_proc_macro_dep)
+
+def _is_proc_macro_dep_is_not_in_env_for_proc_macro_dep(ctx):
+ env = analysistest.begin(ctx)
+ tut = analysistest.target_under_test(env)
+ proc_macro_dep_action = [a for a in tut[DepActionsInfo].actions if str(a) == "action 'Compiling Rust rlib proc_macro_dep (1 files)'"][0]
+ asserts.true(env, "BAZEL_RULES_RUST_IS_PROC_MACRO_DEP" not in proc_macro_dep_action.env)
+ return analysistest.end(env)
+
+is_proc_macro_dep_is_not_in_env_for_proc_macro_dep_test = analysistest.make(_is_proc_macro_dep_is_not_in_env_for_proc_macro_dep)
+
+def _is_proc_macro_dep_test():
+ """Generate targets and tests."""
+
+ rust_library(
+ name = "proc_macro_dep",
+ srcs = ["proc_macro_dep.rs"],
+ edition = "2018",
+ )
+
+ rust_proc_macro(
+ name = "proc_macro_crate",
+ srcs = ["proc_macro_crate.rs"],
+ deps = ["proc_macro_dep"],
+ edition = "2018",
+ )
+
+ is_proc_macro_dep_is_not_in_env_for_top_level_action_test(
+ name = "is_proc_macro_dep_is_not_in_env_for_top_level_proc_macro",
+ target_under_test = ":proc_macro_crate",
+ )
+
+ is_proc_macro_dep_is_not_in_env_for_top_level_action_test(
+ name = "is_proc_macro_dep_is_not_in_env_for_top_level_library",
+ target_under_test = ":proc_macro_dep",
+ )
+
+ attach_dep_actions_and_enable_is_proc_macro_dep_aspect(
+ name = "proc_macro_crate_enabled_with_actions",
+ dep = ":proc_macro_crate",
+ )
+
+ is_proc_macro_dep_is_false_for_proc_macro_test(
+ name = "is_proc_macro_dep_is_false_for_proc_macro",
+ target_under_test = ":proc_macro_crate_enabled_with_actions",
+ )
+
+ is_proc_macro_dep_is_true_for_proc_macro_dep_test(
+ name = "is_proc_macro_dep_is_true_for_proc_macro_dep",
+ target_under_test = ":proc_macro_crate_enabled_with_actions",
+ )
+
+ rust_library(
+ name = "top_level_library",
+ srcs = ["proc_macro_dep.rs"],
+ edition = "2018",
+ )
+
+ attach_dep_actions_and_enable_is_proc_macro_dep_aspect(
+ name = "top_level_library_enabled_with_actions",
+ dep = ":top_level_library",
+ )
+
+ is_proc_macro_dep_is_false_for_top_level_library_test(
+ name = "is_proc_macro_dep_is_false_for_top_level_library",
+ target_under_test = ":top_level_library_enabled_with_actions",
+ )
+
+def is_proc_macro_dep_test_suite(name):
+ """Entry-point macro called from the BUILD file.
+
+ Args:
+ name: Name of the macro.
+ """
+ _is_proc_macro_dep_test()
+
+ native.test_suite(
+ name = name,
+ tests = [
+ "is_proc_macro_dep_is_not_in_env_for_top_level_proc_macro",
+ "is_proc_macro_dep_is_not_in_env_for_top_level_library",
+ "is_proc_macro_dep_is_false_for_proc_macro",
+ "is_proc_macro_dep_is_false_for_top_level_library",
+ "is_proc_macro_dep_is_true_for_proc_macro_dep",
+ ],
+ )
diff --git a/test/unit/is_proc_macro_dep/proc_macro_crate.rs b/test/unit/is_proc_macro_dep/proc_macro_crate.rs
new file mode 100644
index 0000000..8830ab5
--- /dev/null
+++ b/test/unit/is_proc_macro_dep/proc_macro_crate.rs
@@ -0,0 +1,11 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn make_answer(_item: TokenStream) -> TokenStream {
+ let answer = proc_macro_dep::proc_macro_dep();
+ format!("fn answer() -> u32 {{ {} }}", answer)
+ .parse()
+ .unwrap()
+}
diff --git a/test/unit/is_proc_macro_dep/proc_macro_dep.rs b/test/unit/is_proc_macro_dep/proc_macro_dep.rs
new file mode 100644
index 0000000..3358e72
--- /dev/null
+++ b/test/unit/is_proc_macro_dep/proc_macro_dep.rs
@@ -0,0 +1,3 @@
+pub fn proc_macro_dep() -> i64 {
+ 42
+}