I'm trying to build out a proc macro that takes input in the following form to build a data structure:
ecs_world! {
ecs_archetype!(
MyArchetype1,
ComponentA,
ComponentB,
#[cfg(server)] ComponentC,
);
#[cfg(client)]
ecs_archetype!(
MyArchetype2,
ComponentD,
ComponentE,
);
}
I need to support #[cfg(...)] attributes in two different positions: one on the top level of an inner macro, and one on any of its parameters. I also want the proc macro to be aware of the shape of the final data structure after cfgs are evaluated in order to perform certain queries later that require knowledge of how the data structure was built at the callsite.
I'm grappling with how to do this in a reasonable way. I know the traditional way to handle this is for the proc macro output to generate all possible outcomes ahead of time, but I can't do that here because that space explodes combinatorically due to potential nesting of arbitrarily unique cfg arguments.
Fundamentally, it seems like I need a way for the proc macro to be able to evaluate the #[cfg] attributes in the scope of the calling crate. I don't believe there's any formal support for doing so, but I'm aware of a few tricks for achieving it. One of them is to create essentially a macro callback of the form:
#[cfg(foo)]
macro_rules! check_for_foo {
() => foo_true!(),
}
#[cfg(not(foo))]
macro_rules! check_for_foo {
() => foo_false!(),
}
check_for_foo!()
This requires the macro to be a freestanding macro though, and can't be done in the context of another macro parse since you can't force eager evaluation.
I could create a stateful proc-macro here, where I have nested and ordered proc macros, some of which use these callbacks to switch their execution, like so:
#[cfg(server)]
macro_rules! maybe_step_2 {
(($tail:tt)*) => { step_2!($($tail)*) }
}
#[cfg(not(server))]
macro_rules! maybe_step_2 {
(($tail:tt)*) => { $($tail)* }
}
step_1!(maybe_step_2!(step_3!()));
However, I know proc macro ordering is very loosely defined and brittle. Are there any tricks or rules I could take advantage of in this case? It seems that currently in practice function-like proc macro execution is actually performed in linear order for a given file, and some crates already take advantage of this implementation-specific behavior. I'm willing to do so as well if all else fails, but I'd like something a little more robust if possible. At the very least I'd rather do this than use a build.rs script for example.
Any ideas on how I could move forward on this?