Skip to content

Commit 8d66111

Browse files
committed
Link on same foreign mods as external definitions and sort by feature
1 parent be2761d commit 8d66111

File tree

4 files changed

+19102
-19263
lines changed

4 files changed

+19102
-19263
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build-util/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ log = "0.4.20"
1515
serde = { version = "1.0.194", features = ["derive"] }
1616
serde_yaml = "0.9.30"
1717
syn = { version = "2.0.46", default-features = false, features = ["parsing", "full", "printing", "visit-mut", "extra-traits"] }
18+
proc-macro2 = { version = "1.0.76", default-features = false }
1819
toml = { version = "0.8.8", default-features = false, features = ["parse"] }
1920
bindgen = "0.69.1"
2021
quote = { version = "1.0.35", default-features = false }

build-util/src/visitors.rs

Lines changed: 140 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use std::{
66
};
77

88
use crate::vita_headers_db::{missing_features_filter, stub_lib_name, VitaDb};
9-
use syn::{visit_mut::VisitMut, ForeignItem, Item, ItemForeignMod, Type};
9+
use syn::{
10+
token, visit_mut::VisitMut, AttrStyle, Attribute, ForeignItem, Ident, Item, ItemForeignMod,
11+
MacroDelimiter, Meta, MetaList, Type,
12+
};
1013

1114
type FeatureSet = BTreeSet<Rc<str>>;
1215

@@ -95,7 +98,11 @@ impl Link {
9598

9699
impl Link {
97100
pub fn visit(&self, i: &mut syn::File) {
98-
let mut items_by_features: BTreeMap<FeatureSet, Vec<ForeignItem>> = BTreeMap::new();
101+
let mut items_by_features: BTreeMap<FeatureSet, Vec<ForeignItem>> = self
102+
.stub_libs
103+
.iter()
104+
.map(|stub_lib_name| (BTreeSet::from([stub_lib_name.clone()]), Vec::new()))
105+
.collect();
99106

100107
// Moves mod items to to the "items_by_features" map
101108
i.items = mem::take(&mut i.items)
@@ -140,8 +147,16 @@ impl Link {
140147

141148
i.items
142149
.extend(items_by_features.into_iter().map(|(features, items)| {
143-
let mut foreign_mod: ItemForeignMod = syn::parse_quote! {
144-
extern "C" {}
150+
let mut foreign_mod: ItemForeignMod = if features.len() == 1 {
151+
let feature = features.first().unwrap();
152+
syn::parse_quote! {
153+
#[link(name = #feature, kind = "static")]
154+
extern "C" {}
155+
}
156+
} else {
157+
syn::parse_quote! {
158+
extern "C" {}
159+
}
145160
};
146161

147162
// Adds feature attributes to mod
@@ -171,26 +186,19 @@ impl Link {
171186

172187
Item::ForeignMod(foreign_mod)
173188
}));
174-
175-
i.items.extend(self.stub_libs.iter().map(|stub_lib_name| {
176-
syn::parse_quote! {
177-
#[cfg(feature = #stub_lib_name)]
178-
#[link(name = #stub_lib_name, kind = "static")]
179-
extern "C" {}
180-
}
181-
}));
182189
}
183190
}
184191

185192
pub struct Sort;
186193

187194
impl VisitMut for Sort {
188195
fn visit_file_mut(&mut self, i: &mut syn::File) {
189-
// Need to visit children first as extern mods need to be identified.
190196
syn::visit_mut::visit_file_mut(self, i);
191197

192198
// Sorts items on alphabetical order based on normalized identifier.
193199
// Bindgen items will be moved to the start.
200+
// Note: for simplicity, we rely on sort_by_cached_key being stable and some things
201+
// already being sorted, such as impl blocks already being after their definition.
194202
i.items.sort_by_cached_key(|item| {
195203
let (precedence, ident) = match item {
196204
Item::ExternCrate(i) => (0, i.ident.to_string()),
@@ -203,13 +211,9 @@ impl VisitMut for Sort {
203211
.map(|i| i.to_string())
204212
.unwrap_or_else(String::new),
205213
),
206-
Item::Static(i) => (4, i.ident.to_string()),
207-
Item::Const(i) => (4, i.ident.to_string()),
208-
Item::Trait(i) => (4, i.ident.to_string()),
209-
Item::TraitAlias(i) => (4, i.ident.to_string()),
210-
Item::Type(i) => (4, i.ident.to_string()),
211-
Item::Enum(i) => (4, i.ident.to_string()),
212214
Item::Struct(i) => (4, i.ident.to_string()),
215+
Item::Enum(i) => (4, i.ident.to_string()),
216+
Item::Union(i) => (4, i.ident.to_string()),
213217
Item::Impl(i) => {
214218
let ident =
215219
match &*i.self_ty {
@@ -231,29 +235,20 @@ impl VisitMut for Sort {
231235
};
232236
(4, ident)
233237
}
234-
Item::Fn(i) => (4, i.sig.ident.to_string()),
238+
Item::Const(i) => (5, i.ident.to_string()),
239+
Item::Static(i) => (6, i.ident.to_string()),
240+
Item::Trait(i) => (7, i.ident.to_string()),
241+
Item::TraitAlias(i) => (8, i.ident.to_string()),
242+
Item::Fn(i) => (8, i.sig.ident.to_string()),
235243
Item::ForeignMod(i) => {
236-
// For `extern` blocks, we use the first item's identifier.
237-
// Would be ideal to use feature names instead.
238-
let ident = match &i.items[..] {
239-
// Stub blocks are already internally sorted.
240-
[] => String::new(),
241-
[item, ..] => match item {
242-
ForeignItem::Fn(i) => i.sig.ident.to_string(),
243-
ForeignItem::Static(i) => i.ident.to_string(),
244-
ForeignItem::Type(i) => i.ident.to_string(),
245-
i => {
246-
log::warn!("Unexpected item in foreign mod: {i:?}");
247-
String::new()
248-
}
249-
},
250-
};
251-
(4, ident)
244+
let ident = foreign_mod_ident(i);
245+
246+
(9, ident)
252247
}
253-
Item::Union(i) => (4, i.ident.to_string()),
248+
Item::Type(i) => (10, i.ident.to_string()),
254249
i => {
255250
log::warn!("Unexpected item: {i:?}");
256-
(10, String::new())
251+
(11, String::new())
257252
}
258253
};
259254
consider_bindgen((precedence, normalize_str(&ident)))
@@ -279,6 +274,113 @@ fn foreign_item_ident(foreign_item: &ForeignItem) -> String {
279274
}
280275
}
281276

277+
/// Gets foreign mod identifier as the feature cfgs concatenated by `+`
278+
fn foreign_mod_ident(foreign_mod: &ItemForeignMod) -> String {
279+
let ident = foreign_mod.attrs.iter().find_map(|attribute| {
280+
if attribute.style != AttrStyle::Outer {
281+
return None;
282+
}
283+
284+
let Attribute {
285+
pound_token: token::Pound { .. },
286+
style: AttrStyle::Outer,
287+
bracket_token: token::Bracket { .. },
288+
meta:
289+
Meta::List(MetaList {
290+
path,
291+
delimiter: MacroDelimiter::Paren(token::Paren { .. }),
292+
tokens,
293+
}),
294+
} = attribute
295+
else {
296+
return None;
297+
};
298+
299+
// We're only insterested on `cfg`.
300+
if path.segments.len() != 1 || &path.segments[0].ident != "cfg" {
301+
return None;
302+
}
303+
304+
let mut token_iter = tokens.clone().into_iter();
305+
let first_ident: Ident = syn::parse2(token_iter.next()?.into()).ok()?;
306+
if first_ident == "feature" {
307+
let punct: proc_macro2::Punct = syn::parse2(token_iter.next()?.into()).ok()?;
308+
let literal: proc_macro2::Literal = syn::parse2(token_iter.next()?.into()).ok()?;
309+
if token_iter.next().is_some() || punct.as_char() != '=' {
310+
return None;
311+
}
312+
Some(
313+
literal
314+
.to_string()
315+
.strip_prefix('"')?
316+
.strip_suffix('"')?
317+
.to_owned(),
318+
)
319+
} else if first_ident == "any" {
320+
let group: proc_macro2::Group = syn::parse2(token_iter.next()?.into()).ok()?;
321+
if token_iter.next().is_some() {
322+
return None;
323+
}
324+
325+
enum Step {
326+
Ident,
327+
Equals,
328+
Literal,
329+
Separator,
330+
}
331+
332+
let (features, _) = group.stream().into_iter().try_fold(
333+
(Vec::new(), Step::Ident),
334+
|(mut acc, step), token| match step {
335+
Step::Ident => {
336+
if syn::parse2::<Ident>(token.into()).ok()? != "feature" {
337+
return None;
338+
}
339+
Some((acc, Step::Equals))
340+
}
341+
Step::Equals => {
342+
if syn::parse2::<proc_macro2::Punct>(token.into())
343+
.ok()?
344+
.as_char()
345+
!= '='
346+
{
347+
return None;
348+
}
349+
Some((acc, Step::Literal))
350+
}
351+
Step::Literal => {
352+
let literal: proc_macro2::Literal = syn::parse2(token.into()).ok()?;
353+
acc.push(
354+
literal
355+
.to_string()
356+
.strip_prefix('"')?
357+
.strip_suffix('"')?
358+
.to_owned(),
359+
);
360+
Some((acc, Step::Separator))
361+
}
362+
Step::Separator => {
363+
if syn::parse2::<proc_macro2::Punct>(token.into())
364+
.ok()?
365+
.as_char()
366+
!= ','
367+
{
368+
return None;
369+
}
370+
Some((acc, Step::Ident))
371+
}
372+
},
373+
)?;
374+
375+
features.into_iter().reduce(|acc, f| acc + "+" + &f)
376+
} else {
377+
None
378+
}
379+
});
380+
381+
ident.unwrap_or_else(String::new)
382+
}
383+
282384
fn normalize_str(input: &str) -> String {
283385
input.to_lowercase().replace('_', "")
284386
}

0 commit comments

Comments
 (0)