Skip to content

Commit

Permalink
WIP: cxx-qt-gen: add support for passing in CfgEvaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
ahayzen-kdab committed Dec 18, 2024
1 parent f5afefe commit bb42066
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 15 deletions.
7 changes: 5 additions & 2 deletions crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use std::{

use cxx_qt_gen::{
parse_qt_file, write_cpp, write_rust, CppFragment, CxxQtItem, GeneratedCppBlocks,
GeneratedRustBlocks, Parser,
GeneratedRustBlocks, Parser, ParserOpt,
};

// TODO: we need to eventually support having multiple modules defined in a single file. This
Expand Down Expand Up @@ -102,6 +102,9 @@ impl GeneratedCpp {
// The include path we inject needs any prefix (eg the crate name) too
let include_ident = format!("{include_prefix}/{file_ident}");

let mut cxx_qt_opt = ParserOpt::default();
cxx_qt_opt.cfg_evaluator = Box::new(cfg_evaluator::CargoEnvCfgEvaluator);

// Loop through the items looking for any CXX or CXX-Qt blocks
let mut found_bridge = false;
for item in &file.items {
Expand All @@ -128,7 +131,7 @@ impl GeneratedCpp {
}
found_bridge = true;

let parser = Parser::from(m.clone())
let parser = Parser::from(m.clone(), &cxx_qt_opt)
.map_err(GeneratedError::from)
.map_err(to_diagnostic)?;
let generated_cpp = GeneratedCppBlocks::from(&parser)
Expand Down
1 change: 1 addition & 0 deletions crates/cxx-qt-gen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ exclude = ["update_expected.sh"]
rust-version = "1.64.0"

[dependencies]
cxx-gen.workspace = true
proc-macro2.workspace = true
syn.workspace = true
quote.workspace = true
Expand Down
5 changes: 3 additions & 2 deletions crates/cxx-qt-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use generator::{
cpp::{fragment::CppFragment, GeneratedCppBlocks},
rust::GeneratedRustBlocks,
};
pub use parser::Parser;
pub use parser::{Parser, ParserOpt};
pub use syntax::{parse_qt_file, CxxQtFile, CxxQtItem};
pub use writer::{cpp::write_cpp, rust::write_rust};

Expand Down Expand Up @@ -156,7 +156,8 @@ mod tests {
expected_cpp_header: &str,
expected_cpp_source: &str,
) {
let parser = Parser::from(syn::parse_str(input).unwrap()).unwrap();
let opt = ParserOpt::default();
let parser = Parser::from(syn::parse_str(input).unwrap(), opt).unwrap();

let generated_cpp = GeneratedCppBlocks::from(&parser).unwrap();
let (mut header, mut source) =
Expand Down
53 changes: 44 additions & 9 deletions crates/cxx-qt-gen/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::{
syntax::{expr::expr_to_string, path::path_compare_str, safety::Safety},
};
use convert_case::Case;
use cxx_gen::{CfgEvaluator, CfgResult};
use cxxqtdata::ParsedCxxQtData;
use std::collections::BTreeMap;
use syn::{
Expand Down Expand Up @@ -174,6 +175,32 @@ impl PassthroughMod {
}
}

/// Options for C++ code generation.
#[non_exhaustive]
pub struct ParserOpt {
/// Impl for handling conditional compilation attributes.
pub cfg_evaluator: Box<dyn CfgEvaluator>,
}

impl Default for ParserOpt {
fn default() -> Self {
Self {
cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
}
}
}

pub(super) struct UnsupportedCfgEvaluator;

impl CfgEvaluator for UnsupportedCfgEvaluator {
fn eval(&self, name: &str, value: Option<&str>) -> CfgResult {
let _ = name;
let _ = value;
let msg = "cfg attribute is not supported".to_owned();
CfgResult::Undetermined { msg }
}
}

/// A struct representing a module block with CXX-Qt relevant [syn::Item]'s
/// parsed into ParsedCxxQtData, to be used later to generate Rust & C++ code.
///
Expand Down Expand Up @@ -273,7 +300,9 @@ impl Parser {
}

/// Constructs a Parser object from a given [syn::ItemMod] block
pub fn from(mut module: ItemMod) -> Result<Self> {
pub fn from(mut module: ItemMod, _opt: &ParserOpt) -> Result<Self> {
// TODO: start using cfg evaluator in parser phase

let namespace = Self::parse_mod_attributes(&mut module)?;
let (mut cxx_qt_data, module) = Self::parse_module_contents(module, namespace)?;
let type_names = Self::naming_phase(
Expand All @@ -293,6 +322,12 @@ impl Parser {
cxx_qt_data,
})
}

#[cfg(test)]
pub fn from_default_cfg(mut module: ItemMod) -> Result<Self> {
let opt = ParserOpt::default();
Self::from(module, &opt)
}
}

#[cfg(test)]
Expand All @@ -314,7 +349,7 @@ mod tests {
#[cxx_qt::bridge]
mod ffi {}
};
let parser = Parser::from(module).unwrap();
let parser = Parser::from_default_cfg(module).unwrap();

assert!(parser.passthrough_module.items.is_none());
assert!(parser.passthrough_module.docs.is_empty());
Expand All @@ -334,7 +369,7 @@ mod tests {
}
}
};
assert!(Parser::from(module).is_ok()); // Meta::List args in cxx_qt bridge are ignored
assert!(Parser::from_default_cfg(module).is_ok()); // Meta::List args in cxx_qt bridge are ignored

let module: ItemMod = parse_quote! {
#[cxx_qt::bridge(a = b)]
Expand All @@ -344,7 +379,7 @@ mod tests {
}
}
};
assert!(Parser::from(module).is_ok()); // Meta::NameValue args which aren't `namespace` or `cxx_file_stem` are ignored
assert!(Parser::from_default_cfg(module).is_ok()); // Meta::NameValue args which aren't `namespace` or `cxx_file_stem` are ignored
}

#[test]
Expand All @@ -357,7 +392,7 @@ mod tests {
}
}
};
let parser = Parser::from(module).unwrap();
let parser = Parser::from_default_cfg(module).unwrap();
assert_eq!(parser.passthrough_module.items.unwrap().len(), 1);
assert!(parser.passthrough_module.docs.is_empty());
assert_eq!(parser.passthrough_module.module_ident, "ffi");
Expand All @@ -382,7 +417,7 @@ mod tests {
}
}
};
let parser = Parser::from(module.clone()).unwrap();
let parser = Parser::from_default_cfg(module.clone()).unwrap();

assert!(parser.passthrough_module.items.is_none());
assert!(parser.passthrough_module.docs.is_empty());
Expand Down Expand Up @@ -428,7 +463,7 @@ mod tests {
}
}
};
let parser = Parser::from(module.clone()).unwrap();
let parser = Parser::from_default_cfg(module.clone()).unwrap();

assert_eq!(parser.passthrough_module.items.unwrap().len(), 1);
assert_eq!(parser.passthrough_module.docs.len(), 1);
Expand All @@ -441,7 +476,7 @@ mod tests {
#[test]
fn test_parser_invalid() {
assert_parse_errors! {
Parser::from =>
Parser::from_default_cfg =>

{
// Non-string namespace
Expand Down Expand Up @@ -494,7 +529,7 @@ mod tests {
}
}
};
let parser = Parser::from(module).unwrap();
let parser = Parser::from_default_cfg(module).unwrap();
assert_eq!(parser.type_names.num_types(), 22);
assert_eq!(
parser
Expand Down
6 changes: 4 additions & 2 deletions crates/cxx-qt-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemMod};

use cxx_qt_gen::{write_rust, GeneratedRustBlocks, Parser};
use cxx_qt_gen::{write_rust, GeneratedRustBlocks, Parser, ParserOpt};

/// A procedural macro which generates a QObject for a struct inside a module.
///
Expand Down Expand Up @@ -119,7 +119,9 @@ pub fn qobject(_args: TokenStream, _input: TokenStream) -> TokenStream {

// Take the module and C++ namespace and generate the rust code
fn extract_and_generate(module: ItemMod) -> TokenStream {
Parser::from(module)
let opt = ParserOpt::default();
// TODO: do we need cfg eval here?
Parser::from(module, &opt)
.and_then(|parser| GeneratedRustBlocks::from(&parser))
.map(|generated_rust| write_rust(&generated_rust, None))
.unwrap_or_else(|err| err.to_compile_error())
Expand Down

0 comments on commit bb42066

Please sign in to comment.