1use std::{
2 fs,
3 io::{Error, ErrorKind, Result, Write},
4};
5
6use log::info;
7
8use super::{ffi_type::ffi_type, fields, functions, statics};
9use crate::{
10 codegen::general::{self, cfg_condition, version_condition},
11 config::constants,
12 env::Env,
13 file_saver::*,
14 library::*,
15 nameutil::*,
16 traits::*,
17};
18
19pub fn generate(env: &Env) {
20 info!("Generating sys for {}", env.config.library_name);
21
22 let path = env.config.auto_path.join(file_name_sys("lib"));
23
24 info!("Generating file {:?}", path);
25 save_to_file(&path, env.config.make_backup, |w| generate_lib(w, env));
26}
27
28fn generate_lib(w: &mut dyn Write, env: &Env) -> Result<()> {
29 general::start_comments(w, &env.config)?;
30 statics::begin(w)?;
31
32 generate_extern_crates(w, env)?;
33 include_custom_modules(w, env)?;
34 statics::after_extern_crates(w)?;
35
36 if env.config.library_name != "GLib" {
37 statics::use_glib(w)?;
38 }
39 match &*env.config.library_name {
40 "GLib" => statics::only_for_glib(w)?,
41 "GObject" => statics::only_for_gobject(w)?,
42 "Gtk" => statics::only_for_gtk(w)?,
43 _ => (),
44 }
45 writeln!(w)?;
46
47 let ns = env.library.namespace(MAIN_NAMESPACE);
48 let records = prepare(ns);
49 let classes = prepare(ns);
50 let interfaces = prepare(ns);
51 let bitfields = prepare(ns);
52 let enums = prepare(ns);
53 let unions = prepare(ns);
54
55 generate_aliases(w, env, &prepare(ns))?;
56 generate_enums(w, env, &enums)?;
57 generate_constants(w, env, &ns.constants)?;
58 generate_bitfields(w, env, &bitfields)?;
59 generate_unions(w, env, &unions)?;
60 functions::generate_callbacks(w, env, &prepare(ns))?;
61 generate_records(w, env, &records)?;
62 generate_classes_structs(w, env, &classes)?;
63 generate_interfaces_structs(w, env, &interfaces)?;
64
65 if env.namespaces.main().shared_libs.is_empty()
66 && !(records.iter().all(|x| x.functions.is_empty())
67 && classes.iter().all(|x| x.functions.is_empty())
68 && interfaces.iter().all(|x| x.functions.is_empty())
69 && bitfields.iter().all(|x| x.functions.is_empty())
70 && enums.iter().all(|x| x.functions.is_empty())
71 && unions.iter().all(|x| x.functions.is_empty()))
72 {
73 return Err(Error::new(
74 ErrorKind::Other,
75 "No shared library found, but functions were found",
76 ));
77 }
78
79 if !env.namespaces.main().shared_libs.is_empty() {
80 writeln!(w, "extern \"C\" {{")?;
81 functions::generate_enums_funcs(w, env, &enums)?;
82 functions::generate_bitfields_funcs(w, env, &bitfields)?;
83 functions::generate_unions_funcs(w, env, &unions)?;
84 functions::generate_records_funcs(w, env, &records)?;
85 functions::generate_classes_funcs(w, env, &classes)?;
86 functions::generate_interfaces_funcs(w, env, &interfaces)?;
87 functions::generate_other_funcs(w, env, &ns.functions)?;
88
89 writeln!(w, "\n}}")?;
90 }
91
92 Ok(())
93}
94
95fn generate_extern_crates(w: &mut dyn Write, env: &Env) -> Result<()> {
96 for library in &env.config.external_libraries {
97 w.write_all(
98 format!(
99 "use {}_sys as {};\n",
100 library.crate_name.replace('-', "_"),
101 crate_name(&library.namespace)
102 )
103 .as_bytes(),
104 )?;
105 }
106
107 Ok(())
108}
109
110fn include_custom_modules(w: &mut dyn Write, env: &Env) -> Result<()> {
111 let modules = find_modules(env)?;
112 if !modules.is_empty() {
113 writeln!(w)?;
114 for module in &modules {
115 writeln!(w, "mod {module};")?;
116 }
117 writeln!(w)?;
118 for module in &modules {
119 writeln!(w, "pub use {module}::*;")?;
120 }
121 }
122
123 Ok(())
124}
125
126fn find_modules(env: &Env) -> Result<Vec<String>> {
127 let mut vec = Vec::<String>::new();
128 for entry in fs::read_dir(&env.config.auto_path)? {
129 let path = entry?.path();
130 let Some(ext) = path.extension() else {
131 continue;
132 };
133 if ext != "rs" {
134 continue;
135 }
136 let file_stem = path.file_stem().expect("No file name");
137 if file_stem == "lib" {
138 continue;
139 }
140 let file_stem = file_stem
141 .to_str()
142 .expect("Can't convert file name to string")
143 .to_owned();
144 vec.push(file_stem);
145 }
146 vec.sort();
147
148 Ok(vec)
149}
150
151fn prepare<T: Ord>(ns: &Namespace) -> Vec<&T>
152where
153 Type: MaybeRef<T>,
154{
155 let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
156 for typ in ns.types.iter().filter_map(Option::as_ref) {
157 if let Some(x) = typ.maybe_ref() {
158 vec.push(x);
159 }
160 }
161 vec.sort();
162 vec
163}
164
165fn generate_aliases(w: &mut dyn Write, env: &Env, items: &[&Alias]) -> Result<()> {
166 if !items.is_empty() {
167 writeln!(w, "// Aliases")?;
168 }
169 for item in items {
170 let full_name = format!("{}.{}", env.namespaces.main().name, item.name);
171 if !env.type_status_sys(&full_name).need_generate() {
172 continue;
173 }
174 let (comment, c_type) = match ffi_type(env, item.typ, &item.target_c_type) {
175 Ok(x) => ("", x.into_string()),
176 x @ Err(..) => ("//", x.into_string()),
177 };
178 let cfg_condition_ = env
179 .config
180 .objects
181 .get(&full_name)
182 .and_then(|obj| obj.cfg_condition.as_ref());
183 cfg_condition(w, cfg_condition_, false, 0)?;
184 writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type)?;
185 }
186 if !items.is_empty() {
187 writeln!(w)?;
188 }
189
190 Ok(())
191}
192
193fn generate_bitfields(w: &mut dyn Write, env: &Env, items: &[&Bitfield]) -> Result<()> {
194 if !items.is_empty() {
195 writeln!(w, "// Flags")?;
196 }
197 for item in items {
198 let full_name = format!("{}.{}", env.namespaces.main().name, item.name);
199 let config = env.config.objects.get(&full_name);
200 if let Some(false) = config.map(|c| c.status.need_generate()) {
201 continue;
202 }
203 writeln!(w, "pub type {} = c_uint;", item.c_type)?;
204 for member in &item.members {
205 let member_config = config
206 .as_ref()
207 .map_or_else(Vec::new, |c| c.members.matched(&member.name));
208 let version = member_config
209 .iter()
210 .find_map(|m| m.version)
211 .or(member.version);
212
213 if member_config.iter().any(|m| m.status.ignored()) {
214 continue;
215 }
216
217 let val: i64 = member.value.parse().unwrap();
218
219 version_condition(w, env, None, version, false, 0)?;
220 writeln!(
221 w,
222 "pub const {}: {} = {};",
223 member.c_identifier, item.c_type, val as u32,
224 )?;
225 }
226 writeln!(w)?;
227 }
228
229 Ok(())
230}
231
232fn generate_constant_cfg_configure(
233 w: &mut dyn Write,
234 configured_constants: &[&constants::Constant],
235 commented: bool,
236) -> Result<()> {
237 let cfg_condition_ = configured_constants
238 .iter()
239 .find_map(|f| f.cfg_condition.as_ref());
240 cfg_condition(w, cfg_condition_, commented, 1)?;
241 Ok(())
242}
243
244fn generate_constants(w: &mut dyn Write, env: &Env, constants: &[Constant]) -> Result<()> {
245 if !constants.is_empty() {
246 writeln!(w, "// Constants")?;
247 }
248 for constant in constants {
249 let full_name = format!("{}.{}", env.namespaces.main().name, constant.name);
250 let config = env.config.objects.get(&full_name);
251 if let Some(false) = config.map(|c| c.status.need_generate()) {
252 continue;
253 }
254 let (comment, mut type_) = match ffi_type(env, constant.typ, &constant.c_type) {
255 Ok(x) => ("", x.into_string()),
256 x @ Err(..) => ("//", x.into_string()),
257 };
258 let mut value = constant.value.clone();
259 if type_ == "*mut c_char" {
260 type_ = "&[u8]".into();
261 value = format!("b\"{}\\0\"", general::escape_string(&value));
262 } else if type_ == "gboolean" {
263 value = if value == "true" {
264 use_glib_if_needed(env, "GTRUE")
265 } else {
266 use_glib_if_needed(env, "GFALSE")
267 };
268 } else if env
269 .library
270 .type_(constant.typ)
271 .maybe_ref_as::<Bitfield>()
272 .is_some()
273 {
274 let val: i64 = constant.value.parse().unwrap();
275 value = (val as u32).to_string();
276 }
277
278 if let Some(obj) = config {
279 let configured_constants = obj.constants.matched(&full_name);
280 generate_constant_cfg_configure(w, &configured_constants, !comment.is_empty())?;
281 }
282
283 writeln!(
284 w,
285 "{}pub const {}: {} = {};",
286 comment, constant.c_identifier, type_, value
287 )?;
288 }
289 if !constants.is_empty() {
290 writeln!(w)?;
291 }
292
293 Ok(())
294}
295
296fn generate_enums(w: &mut dyn Write, env: &Env, items: &[&Enumeration]) -> Result<()> {
297 if !items.is_empty() {
298 writeln!(w, "// Enums")?;
299 }
300 for item in items {
301 let full_name = format!("{}.{}", env.namespaces.main().name, item.name);
302 let config = env.config.objects.get(&full_name);
303 if let Some(false) = config.map(|c| c.status.need_generate()) {
304 continue;
305 }
306 let cfg_condition_ = env
307 .config
308 .objects
309 .get(&full_name)
310 .and_then(|obj| obj.cfg_condition.as_ref());
311 cfg_condition(w, cfg_condition_, false, 0)?;
312 writeln!(w, "pub type {} = c_int;", item.c_type)?;
313 for member in &item.members {
314 let member_config = config
315 .as_ref()
316 .map_or_else(Vec::new, |c| c.members.matched(&member.name));
317 let version = member_config
318 .iter()
319 .find_map(|m| m.version)
320 .or(member.version);
321
322 if member_config.iter().any(|m| m.status.ignored()) {
323 continue;
324 }
325
326 cfg_condition(w, cfg_condition_, false, 0)?;
327 version_condition(w, env, None, version, false, 0)?;
328 writeln!(
329 w,
330 "pub const {}: {} = {};",
331 member.c_identifier, item.c_type, member.value,
332 )?;
333 }
334 writeln!(w)?;
335 }
336
337 Ok(())
338}
339
340fn generate_unions(w: &mut dyn Write, env: &Env, unions: &[&Union]) -> Result<()> {
341 if !unions.is_empty() {
342 writeln!(w, "// Unions")?;
343 }
344 for union in unions {
345 if union.c_type.is_none() {
346 continue;
347 }
348 let full_name = format!("{}.{}", env.namespaces.main().name, union.name);
349 let config = env.config.objects.get(&full_name);
350
351 if let Some(false) = config.map(|c| c.status.need_generate()) {
352 continue;
353 }
354
355 let align = config.and_then(|c| c.align);
356 let fields = fields::from_union(env, union);
357 generate_from_fields(w, &fields, align)?;
358 }
359 Ok(())
360}
361
362fn generate_debug_impl(w: &mut dyn Write, name: &str, impl_content: &str) -> Result<()> {
363 writeln!(
364 w,
365 "impl ::std::fmt::Debug for {name} {{\n\
366 \tfn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {{\n\
367 \t\t{impl_content}\n\
368 \t}}\n\
369 }}\n"
370 )
371}
372
373fn generate_classes_structs(w: &mut dyn Write, env: &Env, classes: &[&Class]) -> Result<()> {
374 if !classes.is_empty() {
375 writeln!(w, "// Classes")?;
376 }
377 for class in classes {
378 let full_name = format!("{}.{}", env.namespaces.main().name, class.name);
379 let config = env.config.objects.get(&full_name);
380
381 if let Some(false) = config.map(|c| c.status.need_generate()) {
382 continue;
383 }
384
385 let align = config.and_then(|c| c.align);
386 let fields = fields::from_class(env, class);
387 generate_from_fields(w, &fields, align)?;
388 }
389 Ok(())
390}
391
392fn generate_opaque_type(w: &mut dyn Write, name: &str) -> Result<()> {
393 writeln!(
394 w,
395 r#"#[repr(C)]
396#[allow(dead_code)]
397pub struct {name} {{
398 _data: [u8; 0],
399 _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
400}}
401"#
402 )
403}
404
405fn generate_interfaces_structs(
406 w: &mut dyn Write,
407 env: &Env,
408 interfaces: &[&Interface],
409) -> Result<()> {
410 if !interfaces.is_empty() {
411 writeln!(w, "// Interfaces")?;
412 }
413 for interface in interfaces {
414 let full_name = format!("{}.{}", env.namespaces.main().name, interface.name);
415 if !env.type_status_sys(&full_name).need_generate() {
416 continue;
417 }
418 let cfg_condition_ = env
419 .config
420 .objects
421 .get(&full_name)
422 .and_then(|obj| obj.cfg_condition.as_ref());
423 cfg_condition(w, cfg_condition_, false, 0)?;
424 generate_opaque_type(w, &interface.c_type)?;
425 cfg_condition(w, cfg_condition_, false, 0)?;
426 generate_debug_impl(
427 w,
428 &interface.c_type,
429 &format!(
430 "write!(f, \"{name} @ {{self:p}}\")",
431 name = interface.c_type
432 ),
433 )?;
434 }
435 if !interfaces.is_empty() {
436 writeln!(w)?;
437 }
438
439 Ok(())
440}
441
442fn generate_records(w: &mut dyn Write, env: &Env, records: &[&Record]) -> Result<()> {
443 if !records.is_empty() {
444 writeln!(w, "// Records")?;
445 }
446 for record in records {
447 let full_name = format!("{}.{}", env.namespaces.main().name, record.name);
448 let config = env.config.objects.get(&full_name);
449
450 if let Some(false) = config.map(|c| c.status.need_generate()) {
451 continue;
452 }
453
454 if record.c_type == "GHookList" {
455 generate_ghooklist(w)?;
462 } else if record.disguised || record.pointer {
463 generate_disguised(w, env, record)?;
464 } else {
465 let align = config.and_then(|c| c.align);
466 let fields = fields::from_record(env, record);
467 generate_from_fields(w, &fields, align)?;
468 }
469 }
470 Ok(())
471}
472
473fn generate_ghooklist(w: &mut dyn Write) -> Result<()> {
474 w.write_all(
475 br#"#[repr(C)]
476#[derive(Copy, Clone)]
477pub struct GHookList {
478 pub seq_id: c_ulong,
479 #[cfg(any(not(windows), not(target_pointer_width = "64")))]
480 pub hook_size_and_setup: gpointer,
481 #[cfg(all(windows, target_pointer_width = "64"))]
482 pub hook_size_and_setup: c_ulong,
483 pub hooks: *mut GHook,
484 pub dummy3: gpointer,
485 pub finalize_hook: GHookFinalizeFunc,
486 pub dummy: [gpointer; 2],
487}
488
489impl ::std::fmt::Debug for GHookList {
490 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
491 write!(f, "GHookList @ {self:p}")
492 }
493}
494
495"#,
496 )
497}
498
499fn generate_disguised(w: &mut dyn Write, env: &Env, record: &Record) -> Result<()> {
500 let full_name = format!("{}.{}", env.namespaces.main().name, record.name);
501 let cfg_condition_ = env
502 .config
503 .objects
504 .get(&full_name)
505 .and_then(|obj| obj.cfg_condition.as_ref());
506 cfg_condition(w, cfg_condition_, false, 0)?;
507 generate_opaque_type(w, &format!("_{}", record.c_type))?;
508 cfg_condition(w, cfg_condition_, false, 0)?;
509 if record.pointer {
510 writeln!(w, "pub type {name} = *mut _{name};", name = record.c_type)?;
511 } else {
512 writeln!(w, "pub type {name} = _{name};", name = record.c_type)?;
513 }
514 writeln!(w)
515}
516
517fn generate_from_fields(
518 w: &mut dyn Write,
519 fields: &fields::Fields,
520 align: Option<u32>,
521) -> Result<()> {
522 cfg_condition(w, fields.cfg_condition.as_ref(), false, 0)?;
523 if let Some(align) = align {
524 writeln!(w, "#[repr(align({align}))]")?;
525 }
526 let traits = fields.derived_traits().join(", ");
527 if !traits.is_empty() {
528 writeln!(w, "#[derive({traits})]")?;
529 }
530 if fields.external {
531 generate_opaque_type(w, &fields.name)?;
535 } else {
536 writeln!(w, "#[repr(C)]")?;
537
538 if fields.truncated.is_some() {
539 writeln!(w, "#[allow(dead_code)]")?;
540 }
541
542 writeln!(
543 w,
544 "pub {kind} {name} {{",
545 kind = fields.kind,
546 name = &fields.name
547 )?;
548 for field in &fields.fields {
549 writeln!(
550 w,
551 "\tpub {field_name}: {field_type},",
552 field_name = &field.name,
553 field_type = &field.typ
554 )?;
555 }
556 if let Some(ref reason) = fields.truncated {
557 writeln!(w, "\t_truncated_record_marker: c_void,")?;
558 writeln!(w, "\t// {reason}")?;
559 }
560 writeln!(w, "}}\n")?;
561 }
562
563 cfg_condition(w, fields.cfg_condition.as_ref(), false, 0)?;
564 writeln!(
565 w,
566 "impl ::std::fmt::Debug for {name} {{",
567 name = &fields.name
568 )?;
569 writeln!(
570 w,
571 "\tfn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {{"
572 )?;
573 writeln!(
574 w,
575 "\t\tf.debug_struct(&format!(\"{name} @ {{self:p}}\"))",
576 name = &fields.name
577 )?;
578 for field in fields.fields.iter().filter(|f| f.debug) {
579 writeln!(
582 w,
583 "\t\t .field(\"{field_name}\", {field_get})",
584 field_name = &field.name,
585 field_get = &field.access_str()
586 )?;
587 }
588 writeln!(w, "\t\t .finish()")?;
589 writeln!(w, "\t}}")?;
590 writeln!(w, "}}")?;
591 writeln!(w)
592}