Skip to content

Commit 5be9275

Browse files
authored
Handle #[cfg(...)] attributes on turbo tasks (vercel/turborepo#8542)
### Description If a task is conditionally compiled, we must conditionally register it. Noticed this when trying to write a test with `#[cfg(test)]`. **Related:** vercel/turborepo#8530 adds support for registering tasks inside of `mod foo { ... }` blocks. ### Testing Instructions Tested as part of vercel/turborepo#8529
1 parent 26834e3 commit 5be9275

File tree

2 files changed

+129
-41
lines changed

2 files changed

+129
-41
lines changed

crates/turbo-tasks-build/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ workspace = true
1515
anyhow = { workspace = true }
1616
cargo-lock = "8.0.2"
1717
glob = "0.3.0"
18+
quote = { workspace = true }
1819
syn = { workspace = true, features = ["full"] }
1920
turbo-tasks-macros-shared = { workspace = true }

crates/turbo-tasks-build/src/lib.rs

Lines changed: 128 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ use std::{
44
fmt::{Display, Write},
55
fs::read_dir,
66
path::{PathBuf, MAIN_SEPARATOR as PATH_SEP},
7+
sync::Arc,
78
};
89

910
use anyhow::{Context, Result};
1011
use glob::glob;
12+
use quote::ToTokens;
1113
use syn::{
1214
parse_quote, Attribute, Ident, Item, ItemEnum, ItemFn, ItemImpl, ItemMacro, ItemMod,
1315
ItemStruct, ItemTrait, TraitItem, TraitItemMethod,
@@ -98,9 +100,18 @@ pub fn generate_register() {
98100

99101
let out_file = out_dir.join(filename);
100102

101-
let mut queue = vec![("".to_string(), entry)];
102-
103-
while let Some((mod_path, file_path)) = queue.pop() {
103+
let mut queue = vec![QueueEntry {
104+
file_path: entry,
105+
mod_path: "".to_string(),
106+
attributes: Vec::new(),
107+
}];
108+
109+
while let Some(QueueEntry {
110+
file_path,
111+
mod_path,
112+
attributes,
113+
}) = queue.pop()
114+
{
104115
println!("cargo:rerun-if-changed={}", file_path.to_string_lossy());
105116
let src = std::fs::read_to_string(&file_path).unwrap();
106117

@@ -110,6 +121,7 @@ pub fn generate_register() {
110121
file_path: &file_path,
111122
prefix: &prefix,
112123
mod_path,
124+
attributes,
113125

114126
register: &mut register_code,
115127
values: &mut values,
@@ -120,24 +132,27 @@ pub fn generate_register() {
120132
{
121133
Ok(file) => {
122134
for item in file.items {
123-
ctx.process_item(item).unwrap();
135+
ctx.process_item(&item).unwrap();
124136
}
125137
}
126138
Err(err) => println!("{}", err),
127139
}
128140
}
129141

130142
let mut values_code = String::new();
131-
for ((mod_path, ident), (global_name, trait_idents)) in values {
143+
for ((mod_path, ident), entry) in values {
144+
for attribute in &entry.attributes {
145+
values_code.push_str(attribute);
146+
}
132147
writeln!(
133148
values_code,
134149
"crate{}::{}({}, #[allow(unused_variables)] |value| {{",
135150
mod_path,
136151
get_register_value_type_ident(&ident),
137-
global_name
152+
entry.global_name,
138153
)
139154
.unwrap();
140-
for trait_ident in trait_idents {
155+
for trait_ident in entry.trait_idents {
141156
writeln!(
142157
values_code,
143158
" crate{}::{}(value);",
@@ -183,21 +198,36 @@ pub fn rerun_if_glob(globs: &str, root: &str) {
183198
/// (mod_path, type_ident)
184199
type ValueKey = (String, Ident);
185200
/// (global_name, trait_register_fns)
186-
type ValueEntry = (String, Vec<Ident>);
201+
struct ValueEntry {
202+
attributes: Vec<Arc<String>>,
203+
global_name: String,
204+
trait_idents: Vec<Ident>,
205+
}
206+
207+
struct QueueEntry {
208+
/// The on-disk path to the file representing this module.
209+
file_path: PathBuf,
210+
/// The `syn::Path`-style representation of the module. Each component is
211+
/// separated by `::`.
212+
mod_path: String,
213+
/// Attributes (`#[cfg(...)]`) applied to the `ItemMod`.
214+
attributes: Vec<Arc<String>>,
215+
}
187216

188217
struct RegisterContext<'a> {
189-
queue: &'a mut Vec<(String, PathBuf)>,
218+
queue: &'a mut Vec<QueueEntry>,
190219

191220
file_path: &'a PathBuf,
192221
mod_path: String,
222+
attributes: Vec<Arc<String>>,
193223
prefix: &'a str,
194224

195225
register: &'a mut String,
196226
values: &'a mut HashMap<ValueKey, ValueEntry>,
197227
}
198228

199229
impl<'a> RegisterContext<'a> {
200-
fn process_item(&mut self, item: Item) -> Result<()> {
230+
fn process_item(&mut self, item: &Item) -> Result<()> {
201231
match item {
202232
Item::Enum(enum_item) => self.process_enum(enum_item),
203233
Item::Fn(fn_item) => self.process_fn(fn_item),
@@ -210,16 +240,24 @@ impl<'a> RegisterContext<'a> {
210240
}
211241
}
212242

213-
fn process_enum(&mut self, enum_item: ItemEnum) -> Result<()> {
214-
if has_attribute(&enum_item.attrs, "value") {
243+
fn process_enum(&mut self, item: &ItemEnum) -> Result<()> {
244+
self.with_cfg_attrs(&item.attrs, move |this| this.process_enum_inner(item))
245+
}
246+
247+
fn process_enum_inner(&mut self, enum_item: &ItemEnum) -> Result<()> {
248+
if has_turbo_attribute(&enum_item.attrs, "value") {
215249
self.add_value(&enum_item.ident);
216250
self.add_value_debug_impl(&enum_item.ident);
217251
}
218252
Ok(())
219253
}
220254

221-
fn process_fn(&mut self, fn_item: ItemFn) -> Result<()> {
222-
if has_attribute(&fn_item.attrs, "function") {
255+
fn process_fn(&mut self, item: &ItemFn) -> Result<()> {
256+
self.with_cfg_attrs(&item.attrs, move |this| this.process_fn_inner(item))
257+
}
258+
259+
fn process_fn_inner(&mut self, fn_item: &ItemFn) -> Result<()> {
260+
if has_turbo_attribute(&fn_item.attrs, "function") {
223261
let ident = &fn_item.sig.ident;
224262
let type_ident = get_native_function_ident(ident);
225263

@@ -228,8 +266,12 @@ impl<'a> RegisterContext<'a> {
228266
Ok(())
229267
}
230268

231-
fn process_impl(&mut self, impl_item: ItemImpl) -> Result<()> {
232-
if has_attribute(&impl_item.attrs, "value_impl") {
269+
fn process_impl(&mut self, item: &ItemImpl) -> Result<()> {
270+
self.with_cfg_attrs(&item.attrs, move |this| this.process_impl_inner(item))
271+
}
272+
273+
fn process_impl_inner(&mut self, impl_item: &ItemImpl) -> Result<()> {
274+
if has_turbo_attribute(&impl_item.attrs, "value_impl") {
233275
let struct_ident = get_type_ident(&impl_item.self_ty).unwrap();
234276

235277
let trait_ident = impl_item
@@ -241,7 +283,7 @@ impl<'a> RegisterContext<'a> {
241283
self.add_value_trait(&struct_ident, trait_ident);
242284
}
243285

244-
for item in impl_item.items {
286+
for item in &impl_item.items {
245287
if let syn::ImplItem::Method(method_item) = item {
246288
// TODO: if method_item.attrs.iter().any(|a|
247289
// is_attribute(a,
@@ -266,46 +308,63 @@ impl<'a> RegisterContext<'a> {
266308
Ok(())
267309
}
268310

269-
fn process_mod(&mut self, mod_item: ItemMod) -> Result<()> {
270-
if let Some((_, items)) = mod_item.content {
271-
let mod_name = mod_item.ident.to_string();
272-
let child_mod_path = format!("{}::{}", self.mod_path, mod_name);
311+
fn process_mod(&mut self, item: &ItemMod) -> Result<()> {
312+
self.with_cfg_attrs(&item.attrs, move |this| this.process_mod_inner(item))
313+
}
314+
315+
fn process_mod_inner(&mut self, mod_item: &ItemMod) -> Result<()> {
316+
let child_mod_name = mod_item.ident.to_string();
317+
let child_mod_path = format!("{}::{}", self.mod_path, child_mod_name);
318+
if let Some((_, items)) = &mod_item.content {
273319
let parent_mod_path = std::mem::replace(&mut self.mod_path, child_mod_path);
274320
for item in items {
275321
self.process_item(item)?;
276322
}
277323
self.mod_path = parent_mod_path;
278324
} else {
279-
let name = mod_item.ident.to_string();
280-
let parent_path = self.file_path.parent().unwrap();
281-
let direct = parent_path.join(format!("{name}.rs"));
325+
let parent_file_path = self.file_path.parent().unwrap();
326+
let direct = parent_file_path.join(format!("{child_mod_name}.rs"));
282327
if direct.exists() {
283-
self.queue
284-
.push((format!("{}::{name}", self.mod_path), direct));
328+
self.queue.push(QueueEntry {
329+
file_path: direct,
330+
mod_path: child_mod_path,
331+
attributes: self.attributes.clone(),
332+
});
285333
} else {
286-
let nested = parent_path.join(&name).join("mod.rs");
334+
let nested = parent_file_path.join(&child_mod_name).join("mod.rs");
287335
if nested.exists() {
288-
self.queue
289-
.push((format!("{}::{name}", self.mod_path), nested));
336+
self.queue.push(QueueEntry {
337+
file_path: nested,
338+
mod_path: child_mod_path,
339+
attributes: self.attributes.clone(),
340+
});
290341
}
291342
}
292343
}
293344
Ok(())
294345
}
295346

296-
fn process_struct(&mut self, struct_item: ItemStruct) -> Result<()> {
297-
if has_attribute(&struct_item.attrs, "value") {
347+
fn process_struct(&mut self, item: &ItemStruct) -> Result<()> {
348+
self.with_cfg_attrs(&item.attrs, move |this| this.process_struct_inner(item))
349+
}
350+
351+
fn process_struct_inner(&mut self, struct_item: &ItemStruct) -> Result<()> {
352+
if has_turbo_attribute(&struct_item.attrs, "value") {
298353
self.add_value(&struct_item.ident);
299354
self.add_value_debug_impl(&struct_item.ident);
300355
}
301356
Ok(())
302357
}
303358

304-
fn process_trait(&mut self, trait_item: ItemTrait) -> Result<()> {
359+
fn process_trait(&mut self, item: &ItemTrait) -> Result<()> {
360+
self.with_cfg_attrs(&item.attrs, move |this| this.process_trait_inner(item))
361+
}
362+
363+
fn process_trait_inner(&mut self, trait_item: &ItemTrait) -> Result<()> {
305364
if let Some(attr) = trait_item
306365
.attrs
307366
.iter()
308-
.find(|a| is_attribute(a, "value_trait"))
367+
.find(|a| is_turbo_attribute(a, "value_trait"))
309368
{
310369
let trait_ident = &trait_item.ident;
311370

@@ -343,13 +402,17 @@ impl<'a> RegisterContext<'a> {
343402
Ok(())
344403
}
345404

346-
fn process_macro(&mut self, macro_item: ItemMacro) -> Result<()> {
405+
fn process_macro(&mut self, item: &ItemMacro) -> Result<()> {
406+
self.with_cfg_attrs(&item.attrs, move |this| this.process_macro_inner(item))
407+
}
408+
409+
fn process_macro_inner(&mut self, macro_item: &ItemMacro) -> Result<()> {
347410
if macro_item
348411
.mac
349412
.path
350413
.is_ident("__turbo_tasks_internal_primitive")
351414
{
352-
let input = macro_item.mac.tokens;
415+
let input = macro_item.mac.tokens.clone();
353416
let input = syn::parse2::<PrimitiveInput>(input).unwrap();
354417

355418
let ty = input.ty;
@@ -365,7 +428,7 @@ impl<'a> RegisterContext<'a> {
365428
.path
366429
.is_ident("__turbo_tasks_internal_generic_type")
367430
{
368-
let input = macro_item.mac.tokens;
431+
let input = macro_item.mac.tokens.clone();
369432
let input = syn::parse2::<GenericTypeInput>(input).unwrap();
370433

371434
let ty = input.ty;
@@ -398,7 +461,11 @@ impl<'a> RegisterContext<'a> {
398461

399462
fn add_value(&mut self, ident: &Ident) {
400463
let key: ValueKey = (self.mod_path.clone(), ident.clone());
401-
let value: ValueEntry = (self.get_global_name(&[ident]), Vec::new());
464+
let value = ValueEntry {
465+
attributes: self.attributes.clone(),
466+
global_name: self.get_global_name(&[ident]),
467+
trait_idents: Vec::new(),
468+
};
402469

403470
assert!(
404471
self.values.insert(key, value).is_none(),
@@ -444,14 +511,17 @@ impl<'a> RegisterContext<'a> {
444511
self.file_path.display()
445512
);
446513
}
447-
entry.unwrap().1.push(trait_ident.clone());
514+
entry.unwrap().trait_idents.push(trait_ident.clone());
448515
}
449516

450517
fn register(
451518
&mut self,
452519
type_ident: impl Display,
453520
global_name: impl Display,
454521
) -> std::fmt::Result {
522+
for attribute in &self.attributes {
523+
self.register.push_str(attribute);
524+
}
455525
writeln!(
456526
self.register,
457527
"crate{}::{}.register({});",
@@ -503,13 +573,24 @@ impl<'a> RegisterContext<'a> {
503573
&["value_default"],
504574
)
505575
}
576+
577+
fn with_cfg_attrs<T>(&mut self, attrs: &[Attribute], func: impl FnOnce(&mut Self) -> T) -> T {
578+
let orig_len = self.attributes.len();
579+
for attr in attrs.iter().filter(|a| is_cfg_attribute(a)) {
580+
self.attributes
581+
.push(Arc::new(attr.to_token_stream().to_string()));
582+
}
583+
let ret = func(self);
584+
self.attributes.truncate(orig_len);
585+
ret
586+
}
506587
}
507588

508-
fn has_attribute(attrs: &[Attribute], name: &str) -> bool {
509-
attrs.iter().any(|a| is_attribute(a, name))
589+
fn has_turbo_attribute(attrs: &[Attribute], name: &str) -> bool {
590+
attrs.iter().any(|a| is_turbo_attribute(a, name))
510591
}
511592

512-
fn is_attribute(attr: &Attribute, name: &str) -> bool {
593+
fn is_turbo_attribute(attr: &Attribute, name: &str) -> bool {
513594
let path = &attr.path;
514595
if path.leading_colon.is_some() {
515596
return false;
@@ -524,6 +605,12 @@ fn is_attribute(attr: &Attribute, name: &str) -> bool {
524605
}
525606
}
526607

608+
fn is_cfg_attribute(attr: &Attribute) -> bool {
609+
attr.path
610+
.get_ident()
611+
.is_some_and(|ident| ident == "cfg" || ident == "cfg_attr")
612+
}
613+
527614
fn parse_attr_args<T>(attr: &Attribute) -> syn::Result<Option<T>>
528615
where
529616
T: syn::parse::Parse,

0 commit comments

Comments
 (0)