From 45a6bd2da05280d53495f25b8cdd6d0db3be1ae5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 22 Jan 2024 14:21:05 +0100 Subject: [PATCH] Automatically assume that `win32_` and `unix_` should use the related cfg --- src/analysis/constants.rs | 3 +- src/analysis/functions.rs | 4 ++ src/analysis/namespaces.rs | 2 + src/analysis/object.rs | 11 +++- src/analysis/record.rs | 7 ++- src/codegen/sys/fields.rs | 16 ++++-- src/codegen/sys/functions.rs | 15 +++-- src/codegen/sys/lib_.rs | 88 +++++++++++++++++++++++------ src/config/functions.rs | 5 +- src/config/gobjects.rs | 5 +- src/config/members.rs | 5 +- src/config/mod.rs | 59 ++++++++++++++++++++ src/config/virtual_methods.rs | 5 +- src/lib.rs | 1 + src/update_cfgs.rs | 101 ++++++++++++++++++++++++++++++++++ 15 files changed, 279 insertions(+), 48 deletions(-) create mode 100644 src/update_cfgs.rs diff --git a/src/analysis/constants.rs b/src/analysis/constants.rs index 015515468..b9409e6ca 100644 --- a/src/analysis/constants.rs +++ b/src/analysis/constants.rs @@ -1,6 +1,6 @@ use std::borrow::Borrow; -use crate::{config, env::Env, library, nameutil, traits::*, version::Version}; +use crate::{config, env::Env, library, nameutil, traits::*, update_cfgs, version::Version}; #[derive(Debug)] pub struct Info { @@ -49,6 +49,7 @@ pub fn analyze>( let cfg_condition = configured_constants .iter() .find_map(|c| c.cfg_condition.clone()); + let cfg_condition = update_cfgs::get_constant_cfg_condition(&constant.name, &cfg_condition); let name = nameutil::mangle_keywords(&*constant.name).into_owned(); diff --git a/src/analysis/functions.rs b/src/analysis/functions.rs index 8b201e044..70144e4b2 100644 --- a/src/analysis/functions.rs +++ b/src/analysis/functions.rs @@ -35,6 +35,7 @@ use crate::{ }, nameutil, traits::*, + update_cfgs, version::Version, }; @@ -651,6 +652,9 @@ fn analyze_function( let cfg_condition = configured_functions .iter() .find_map(|f| f.cfg_condition.clone()); + let ns = env.library.namespace(ns_id); + let cfg_condition = + update_cfgs::get_cfg_condition(&func.name, &cfg_condition, &ns.symbol_prefixes); let doc_hidden = configured_functions.iter().any(|f| f.doc_hidden); let doc_trait_name = configured_functions .iter() diff --git a/src/analysis/namespaces.rs b/src/analysis/namespaces.rs index f63261aef..ae1996786 100644 --- a/src/analysis/namespaces.rs +++ b/src/analysis/namespaces.rs @@ -14,6 +14,7 @@ pub struct Namespace { pub higher_crate_name: String, pub package_names: Vec, pub symbol_prefixes: Vec, + pub identifier_prefixes: Vec, pub shared_libs: Vec, pub versions: Vec, } @@ -58,6 +59,7 @@ pub fn run(gir: &library::Library) -> Info { higher_crate_name, package_names: ns.package_names.clone(), symbol_prefixes: ns.symbol_prefixes.clone(), + identifier_prefixes: ns.identifier_prefixes.clone(), shared_libs: ns.shared_library.clone(), versions: ns.versions.iter().copied().collect(), }); diff --git a/src/analysis/object.rs b/src/analysis/object.rs index de56bbb00..c4331eec0 100644 --- a/src/analysis/object.rs +++ b/src/analysis/object.rs @@ -12,6 +12,7 @@ use crate::{ library::{self, FunctionKind}, nameutil::*, traits::*, + update_cfgs, }; /// The location of an item within the object @@ -302,6 +303,9 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option imports.add("glib::prelude::*"); } + let ns = env.library.namespace(class_tid.ns_id); + let cfg_condition = + update_cfgs::get_object_cfg_condition(&name, &obj.cfg_condition, &ns.identifier_prefixes); let base = InfoBase { full_name, type_id: class_tid, @@ -311,7 +315,7 @@ pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option imports, version, deprecated_version, - cfg_condition: obj.cfg_condition.clone(), + cfg_condition, concurrency: obj.concurrency, visibility: obj.visibility, }; @@ -428,6 +432,9 @@ pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option Option Option { } } + let ns = env.library.namespace(record_tid.ns_id); + let cfg_condition = + update_cfgs::get_object_cfg_condition(&name, &obj.cfg_condition, &ns.identifier_prefixes); + let base = InfoBase { full_name, type_id: record_tid, @@ -204,7 +209,7 @@ pub fn new(env: &Env, obj: &GObject) -> Option { imports, version, deprecated_version, - cfg_condition: obj.cfg_condition.clone(), + cfg_condition, concurrency: obj.concurrency, visibility: obj.visibility, }; diff --git a/src/codegen/sys/fields.rs b/src/codegen/sys/fields.rs index 0927ae7f5..283314f1a 100644 --- a/src/codegen/sys/fields.rs +++ b/src/codegen/sys/fields.rs @@ -4,6 +4,7 @@ use crate::{ env::Env, library::*, traits::{IntoString, MaybeRefAs}, + update_cfgs, }; pub struct Fields { @@ -164,10 +165,13 @@ fn field_ffi_type(env: &Env, field: &Field) -> Result { } fn get_gobject_cfg_condition(env: &Env, name: &str) -> Option { - let full_name = format!("{}.{}", env.namespaces.main().name, name); - if let Some(obj) = env.config.objects.get(&full_name) { - obj.cfg_condition.clone() - } else { - None - } + let ns = env.namespaces.main(); + let full_name = format!("{}.{}", ns.name, name); + env.config + .objects + .get(&full_name) + .and_then(|obj| { + update_cfgs::get_object_cfg_condition(name, &obj.cfg_condition, &ns.identifier_prefixes) + }) + .or_else(|| update_cfgs::get_object_cfg_condition(name, &None, &ns.identifier_prefixes)) } diff --git a/src/codegen/sys/functions.rs b/src/codegen/sys/functions.rs index bf663cf16..94e9813f6 100644 --- a/src/codegen/sys/functions.rs +++ b/src/codegen/sys/functions.rs @@ -10,6 +10,7 @@ use crate::{ env::Env, library, nameutil, traits::*, + update_cfgs, }; // used as glib:get-type in GLib-2.0.gir @@ -180,13 +181,19 @@ pub fn generate_other_funcs( fn generate_cfg_configure( w: &mut dyn Write, + env: &Env, + function_name: &str, configured_functions: &[&Function], commented: bool, ) -> Result<()> { + let ns = env.namespaces.main(); let cfg_condition_ = configured_functions .iter() - .find_map(|f| f.cfg_condition.as_ref()); - cfg_condition(w, cfg_condition_, commented, 1)?; + .find_map(|f| { + update_cfgs::get_cfg_condition(function_name, &f.cfg_condition, &ns.symbol_prefixes) + }) + .or_else(|| update_cfgs::get_cfg_condition(function_name, &None, &ns.symbol_prefixes)); + cfg_condition(w, cfg_condition_.as_ref(), commented, 1)?; Ok(()) } @@ -224,7 +231,7 @@ fn generate_object_funcs( .max() .flatten(); version_condition(w, env, None, version, false, 1)?; - generate_cfg_configure(w, &configured_functions, false)?; + generate_cfg_configure(w, env, glib_get_type, &configured_functions, false)?; writeln!(w, " pub fn {glib_get_type}() -> GType;")?; } } @@ -254,7 +261,7 @@ fn generate_object_funcs( version_condition(w, env, None, version, commented, 1)?; let name = func.c_identifier.as_ref().unwrap(); - generate_cfg_configure(w, &configured_functions, commented)?; + generate_cfg_configure(w, env, name, &configured_functions, commented)?; writeln!(w, " {comment}pub fn {name}{sig};")?; } diff --git a/src/codegen/sys/lib_.rs b/src/codegen/sys/lib_.rs index 1ee73dbf0..34d252902 100644 --- a/src/codegen/sys/lib_.rs +++ b/src/codegen/sys/lib_.rs @@ -14,6 +14,7 @@ use crate::{ library::*, nameutil::*, traits::*, + update_cfgs, }; pub fn generate(env: &Env) { @@ -164,7 +165,8 @@ fn generate_aliases(w: &mut dyn Write, env: &Env, items: &[&Alias]) -> Result<() writeln!(w, "// Aliases")?; } for item in items { - let full_name = format!("{}.{}", env.namespaces.main().name, item.name); + let ns = env.namespaces.main(); + let full_name = format!("{}.{}", ns.name, item.name); if !env.type_status_sys(&full_name).need_generate() { continue; } @@ -176,8 +178,17 @@ fn generate_aliases(w: &mut dyn Write, env: &Env, items: &[&Alias]) -> Result<() .config .objects .get(&full_name) - .and_then(|obj| obj.cfg_condition.as_ref()); - cfg_condition(w, cfg_condition_, false, 0)?; + .and_then(|obj| { + update_cfgs::get_object_cfg_condition( + &item.name, + &obj.cfg_condition, + &ns.identifier_prefixes, + ) + }) + .or_else(|| { + update_cfgs::get_object_cfg_condition(&item.name, &None, &ns.identifier_prefixes) + }); + cfg_condition(w, cfg_condition_.as_ref(), false, 0)?; writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type)?; } if !items.is_empty() { @@ -224,13 +235,15 @@ fn generate_bitfields(w: &mut dyn Write, env: &Env, items: &[&Bitfield]) -> Resu fn generate_constant_cfg_configure( w: &mut dyn Write, + constant: &Constant, configured_constants: &[&constants::Constant], commented: bool, ) -> Result<()> { let cfg_condition_ = configured_constants .iter() - .find_map(|f| f.cfg_condition.as_ref()); - cfg_condition(w, cfg_condition_, commented, 1)?; + .find_map(|f| update_cfgs::get_constant_cfg_condition(&constant.name, &f.cfg_condition)) + .or_else(|| update_cfgs::get_constant_cfg_condition(&constant.name, &None)); + cfg_condition(w, cfg_condition_.as_ref(), commented, 1)?; Ok(()) } @@ -270,7 +283,12 @@ fn generate_constants(w: &mut dyn Write, env: &Env, constants: &[Constant]) -> R if let Some(obj) = config { let configured_constants = obj.constants.matched(&full_name); - generate_constant_cfg_configure(w, &configured_constants, !comment.is_empty())?; + generate_constant_cfg_configure( + w, + constant, + &configured_constants, + !comment.is_empty(), + )?; } writeln!( @@ -291,7 +309,8 @@ fn generate_enums(w: &mut dyn Write, env: &Env, items: &[&Enumeration]) -> Resul writeln!(w, "// Enums")?; } for item in items { - let full_name = format!("{}.{}", env.namespaces.main().name, item.name); + let ns = env.namespaces.main(); + let full_name = format!("{}.{}", ns.name, item.name); let config = env.config.objects.get(&full_name); if let Some(false) = config.map(|c| c.status.need_generate()) { continue; @@ -300,8 +319,17 @@ fn generate_enums(w: &mut dyn Write, env: &Env, items: &[&Enumeration]) -> Resul .config .objects .get(&full_name) - .and_then(|obj| obj.cfg_condition.as_ref()); - cfg_condition(w, cfg_condition_, false, 0)?; + .and_then(|obj| { + update_cfgs::get_object_cfg_condition( + &item.name, + &obj.cfg_condition, + &ns.identifier_prefixes, + ) + }) + .or_else(|| { + update_cfgs::get_object_cfg_condition(&item.name, &None, &ns.identifier_prefixes) + }); + cfg_condition(w, cfg_condition_.as_ref(), false, 0)?; writeln!(w, "pub type {} = c_int;", item.c_type)?; for member in &item.members { let member_config = config @@ -317,7 +345,7 @@ fn generate_enums(w: &mut dyn Write, env: &Env, items: &[&Enumeration]) -> Resul continue; } - cfg_condition(w, cfg_condition_, false, 0)?; + cfg_condition(w, cfg_condition_.as_ref(), false, 0)?; version_condition(w, env, None, version, false, 0)?; writeln!( w, @@ -404,7 +432,8 @@ fn generate_interfaces_structs( writeln!(w, "// Interfaces")?; } for interface in interfaces { - let full_name = format!("{}.{}", env.namespaces.main().name, interface.name); + let ns = env.namespaces.main(); + let full_name = format!("{}.{}", ns.name, interface.name); if !env.type_status_sys(&full_name).need_generate() { continue; } @@ -412,10 +441,23 @@ fn generate_interfaces_structs( .config .objects .get(&full_name) - .and_then(|obj| obj.cfg_condition.as_ref()); - cfg_condition(w, cfg_condition_, false, 0)?; + .and_then(|obj| { + update_cfgs::get_object_cfg_condition( + &interface.name, + &obj.cfg_condition, + &ns.identifier_prefixes, + ) + }) + .or_else(|| { + update_cfgs::get_object_cfg_condition( + &interface.name, + &None, + &ns.identifier_prefixes, + ) + }); + cfg_condition(w, cfg_condition_.as_ref(), false, 0)?; generate_opaque_type(w, &interface.c_type)?; - cfg_condition(w, cfg_condition_, false, 0)?; + cfg_condition(w, cfg_condition_.as_ref(), false, 0)?; generate_debug_impl( w, &interface.c_type, @@ -490,15 +532,25 @@ impl ::std::fmt::Debug for GHookList { } fn generate_disguised(w: &mut dyn Write, env: &Env, record: &Record) -> Result<()> { - let full_name = format!("{}.{}", env.namespaces.main().name, record.name); + let ns = env.namespaces.main(); + let full_name = format!("{}.{}", ns.name, record.name); let cfg_condition_ = env .config .objects .get(&full_name) - .and_then(|obj| obj.cfg_condition.as_ref()); - cfg_condition(w, cfg_condition_, false, 0)?; + .and_then(|obj| { + update_cfgs::get_object_cfg_condition( + &record.name, + &obj.cfg_condition, + &ns.identifier_prefixes, + ) + }) + .or_else(|| { + update_cfgs::get_object_cfg_condition(&record.name, &None, &ns.identifier_prefixes) + }); + cfg_condition(w, cfg_condition_.as_ref(), false, 0)?; generate_opaque_type(w, &format!("_{}", record.c_type))?; - cfg_condition(w, cfg_condition_, false, 0)?; + cfg_condition(w, cfg_condition_.as_ref(), false, 0)?; if record.pointer { writeln!(w, "pub type {name} = *mut _{name};", name = record.c_type)?; } else { diff --git a/src/config/functions.rs b/src/config/functions.rs index bfaf87c05..f83828f41 100644 --- a/src/config/functions.rs +++ b/src/config/functions.rs @@ -344,10 +344,7 @@ impl Parse for Function { .lookup("version") .and_then(Value::as_str) .and_then(|s| s.parse().ok()); - let cfg_condition = toml - .lookup("cfg_condition") - .and_then(Value::as_str) - .map(ToOwned::to_owned); + let cfg_condition = super::get_cfg_condition(toml, object_name); let parameters = Parameters::parse(toml.lookup("parameter"), object_name); let ret = Return::parse(toml.lookup("return"), object_name); let doc_hidden = toml diff --git a/src/config/gobjects.rs b/src/config/gobjects.rs index 8adedc4c4..b4ce0d018 100644 --- a/src/config/gobjects.rs +++ b/src/config/gobjects.rs @@ -330,10 +330,7 @@ fn parse_object( .lookup("version") .and_then(Value::as_str) .and_then(|s| s.parse().ok()); - let cfg_condition = toml_object - .lookup("cfg_condition") - .and_then(Value::as_str) - .map(ToOwned::to_owned); + let cfg_condition = super::get_object_cfg_condition(toml_object, &name); let generate_trait = toml_object.lookup("trait").and_then(Value::as_bool); let final_type = toml_object .lookup("final_type") diff --git a/src/config/members.rs b/src/config/members.rs index 05ddb9c64..ea1d36c33 100644 --- a/src/config/members.rs +++ b/src/config/members.rs @@ -52,10 +52,7 @@ impl Parse for Member { .lookup("deprecated_version") .and_then(Value::as_str) .and_then(|s| s.parse().ok()); - let cfg_condition = toml - .lookup("cfg_condition") - .and_then(Value::as_str) - .map(ToOwned::to_owned); + let cfg_condition = super::get_cfg_condition(toml, object_name); let status = { if toml diff --git a/src/config/mod.rs b/src/config/mod.rs index 77f9dd8c2..15c2c763e 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -27,3 +27,62 @@ pub use self::{ string_type::StringType, work_mode::WorkMode, }; + +use self::error::TomlHelper; +use log::warn; + +fn get_cfg_condition(toml: &toml::Value, object_name: &str) -> Option { + let cfg_condition = toml.lookup("cfg_condition").and_then(toml::Value::as_str); + let Some(sub_object_name) = object_name.split('.').nth(1) else { + return cfg_condition.map(ToString::to_string); + }; + if sub_object_name.starts_with("win32_") { + match cfg_condition { + Some("windows") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `windows` if name starts with `win32_`"); + Some("window".to_string()) + } + None => Some("window".to_string()), + Some(cfg) => Some(format!("{cfg},windows")), + } + } else if sub_object_name.starts_with("unix_") { + match cfg_condition { + Some("unix") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `unix` if name starts with `unix_`"); + Some("unix".to_string()) + } + None => Some("unix".to_string()), + Some(cfg) => Some(format!("{cfg},unix")), + } + } else { + cfg_condition.map(ToString::to_string) + } +} + +fn get_object_cfg_condition(toml: &toml::Value, object_name: &str) -> Option { + let cfg_condition = toml.lookup("cfg_condition").and_then(toml::Value::as_str); + let Some(sub_object_name) = object_name.split('.').nth(1) else { + return cfg_condition.map(ToString::to_string); + }; + if sub_object_name.starts_with("Win32") || sub_object_name.starts_with("GWin32") { + match cfg_condition { + Some("windows") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `windows` if object name starts with `Win32`"); + Some("windows".to_string()) + } + None => Some("windows".to_string()), + Some(cfg) => Some(format!("{cfg},windows")), + } + } else if sub_object_name.starts_with("Unix") || sub_object_name.starts_with("GUnix") { + match cfg_condition { + Some("unix") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `unix` if object name starts with `Unix`"); + Some("unix".to_string()) + } + None => Some("unix".to_string()), + Some(cfg) => Some(format!("{cfg},unix")), + } + } else { + cfg_condition.map(ToString::to_string) + } +} diff --git a/src/config/virtual_methods.rs b/src/config/virtual_methods.rs index f3e3ac3cb..03062fa31 100644 --- a/src/config/virtual_methods.rs +++ b/src/config/virtual_methods.rs @@ -80,10 +80,7 @@ impl Parse for VirtualMethod { .lookup("version") .and_then(Value::as_str) .and_then(|s| s.parse().ok()); - let cfg_condition = toml - .lookup("cfg_condition") - .and_then(Value::as_str) - .map(ToOwned::to_owned); + let cfg_condition = super::get_cfg_condition(toml, object_name); let parameters = Parameters::parse(toml.lookup("parameter"), object_name); let ret = Return::parse(toml.lookup("return"), object_name); let doc_hidden = toml diff --git a/src/lib.rs b/src/lib.rs index df0d1750e..de581dede 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ mod library_preprocessing; mod nameutil; mod parser; mod traits; +pub(crate) mod update_cfgs; pub mod update_version; mod version; mod visitors; diff --git a/src/update_cfgs.rs b/src/update_cfgs.rs new file mode 100644 index 000000000..d50e5e3ac --- /dev/null +++ b/src/update_cfgs.rs @@ -0,0 +1,101 @@ +use log::warn; + +pub fn get_cfg_condition( + object_name: &str, + cfg_condition: &Option, + identifier_prefixes: &[String], +) -> Option { + let sub_object_name = identifier_prefixes + .iter() + .filter_map(|prefix| object_name.strip_prefix(prefix)) + .find_map(|name| name.strip_prefix('_')) + .unwrap_or(object_name); + + if sub_object_name.starts_with("win32_") { + match cfg_condition.as_deref() { + Some("windows") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `windows` if name starts with `win32_`"); + Some("windows".to_string()) + } + None => Some("windows".to_string()), + Some(cfg) => Some(format!("{cfg},windows")), + } + } else if sub_object_name.starts_with("unix_") { + match cfg_condition.as_deref() { + Some("unix") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `unix` if name starts with `unix_`"); + Some("unix".to_string()) + } + None => Some("unix".to_string()), + Some(cfg) => Some(format!("{cfg},unix")), + } + } else { + cfg_condition.clone() + } +} + +pub fn get_object_cfg_condition( + object_name: &str, + cfg_condition: &Option, + identifier_prefixes: &[String], +) -> Option { + let sub_object_name = identifier_prefixes + .iter() + .filter_map(|prefix| object_name.strip_prefix(prefix)) + .find(|name| { + name.chars() + .next() + .map(|c| c.is_uppercase()) + .unwrap_or(false) + }) + .unwrap_or(object_name); + + if sub_object_name.starts_with("Win32") { + match cfg_condition.as_deref() { + Some("windows") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `windows` if object name starts with `Win32`"); + Some("windows".to_string()) + } + None => Some("windows".to_string()), + Some(cfg) => Some(format!("{cfg},windows")), + } + } else if sub_object_name.starts_with("Unix") || sub_object_name.starts_with("UNIX") { + match cfg_condition.as_deref() { + Some("unix") => { + warn!("\"object {object_name}\": No need to set `cfg_condition` to `unix` if object name starts with `Unix`"); + Some("unix".to_string()) + } + None => Some("unix".to_string()), + Some(cfg) => Some(format!("{cfg},unix")), + } + } else { + cfg_condition.clone() + } +} + +pub fn get_constant_cfg_condition( + const_name: &str, + cfg_condition: &Option, +) -> Option { + if const_name.starts_with("WIN32_") { + match cfg_condition.as_deref() { + Some("windows") => { + warn!("\"object {const_name}\": No need to set `cfg_condition` to `windows` if name starts with `WIN32_`"); + Some("windows".to_string()) + } + None => Some("windows".to_string()), + Some(cfg) => Some(format!("{cfg},windows")), + } + } else if const_name.starts_with("UNIX_") { + match cfg_condition.as_deref() { + Some("unix") => { + warn!("\"object {const_name}\": No need to set `cfg_condition` to `unix` if name starts with `UNIX_`"); + Some("unix".to_string()) + } + None => Some("unix".to_string()), + Some(cfg) => Some(format!("{cfg},unix")), + } + } else { + cfg_condition.clone() + } +}