@@ -6,7 +6,10 @@ use std::{
6
6
} ;
7
7
8
8
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
+ } ;
10
13
11
14
type FeatureSet = BTreeSet < Rc < str > > ;
12
15
@@ -95,7 +98,11 @@ impl Link {
95
98
96
99
impl Link {
97
100
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 ( ) ;
99
106
100
107
// Moves mod items to to the "items_by_features" map
101
108
i. items = mem:: take ( & mut i. items )
@@ -140,8 +147,16 @@ impl Link {
140
147
141
148
i. items
142
149
. 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
+ }
145
160
} ;
146
161
147
162
// Adds feature attributes to mod
@@ -171,26 +186,19 @@ impl Link {
171
186
172
187
Item :: ForeignMod ( foreign_mod)
173
188
} ) ) ;
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
- } ) ) ;
182
189
}
183
190
}
184
191
185
192
pub struct Sort ;
186
193
187
194
impl VisitMut for Sort {
188
195
fn visit_file_mut ( & mut self , i : & mut syn:: File ) {
189
- // Need to visit children first as extern mods need to be identified.
190
196
syn:: visit_mut:: visit_file_mut ( self , i) ;
191
197
192
198
// Sorts items on alphabetical order based on normalized identifier.
193
199
// 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.
194
202
i. items . sort_by_cached_key ( |item| {
195
203
let ( precedence, ident) = match item {
196
204
Item :: ExternCrate ( i) => ( 0 , i. ident . to_string ( ) ) ,
@@ -203,13 +211,9 @@ impl VisitMut for Sort {
203
211
. map ( |i| i. to_string ( ) )
204
212
. unwrap_or_else ( String :: new) ,
205
213
) ,
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 ( ) ) ,
212
214
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 ( ) ) ,
213
217
Item :: Impl ( i) => {
214
218
let ident =
215
219
match & * i. self_ty {
@@ -231,29 +235,20 @@ impl VisitMut for Sort {
231
235
} ;
232
236
( 4 , ident)
233
237
}
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 ( ) ) ,
235
243
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)
252
247
}
253
- Item :: Union ( i) => ( 4 , i. ident . to_string ( ) ) ,
248
+ Item :: Type ( i) => ( 10 , i. ident . to_string ( ) ) ,
254
249
i => {
255
250
log:: warn!( "Unexpected item: {i:?}" ) ;
256
- ( 10 , String :: new ( ) )
251
+ ( 11 , String :: new ( ) )
257
252
}
258
253
} ;
259
254
consider_bindgen ( ( precedence, normalize_str ( & ident) ) )
@@ -279,6 +274,113 @@ fn foreign_item_ident(foreign_item: &ForeignItem) -> String {
279
274
}
280
275
}
281
276
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
+
282
384
fn normalize_str ( input : & str ) -> String {
283
385
input. to_lowercase ( ) . replace ( '_' , "" )
284
386
}
0 commit comments