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 457c3eb
Show file tree
Hide file tree
Showing 17 changed files with 381 additions and 41 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 @@ -42,7 +42,7 @@ use std::{
};

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

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 = GeneratedOpt::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 Down Expand Up @@ -131,7 +134,7 @@ impl GeneratedCpp {
let parser = Parser::from(m.clone())
.map_err(GeneratedError::from)
.map_err(to_diagnostic)?;
let generated_cpp = GeneratedCppBlocks::from(&parser)
let generated_cpp = GeneratedCppBlocks::from(&parser, &cxx_qt_opt)
.map_err(GeneratedError::from)
.map_err(to_diagnostic)?;
let generated_rust = GeneratedRustBlocks::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
92 changes: 92 additions & 0 deletions crates/cxx-qt-gen/src/generator/cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-FileCopyrightText: CXX Authors
// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
// SPDX-FileContributor: David Tolnay <dtolnay@gmail.com>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::syntax::cfg::{parse_attribute, CfgExpr};
use cxx_gen::{CfgEvaluator, CfgResult};
use quote::quote;
use syn::{Attribute, Error, LitStr};

pub(crate) fn try_eval_attributes(
cfg_evaluator: &dyn CfgEvaluator,
attrs: &[Attribute],
) -> Result<bool, Error> {
// Build a single CfgExpr from the Attributes
let cfg_expr = attrs
.iter()
.map(parse_attribute)
.collect::<Result<Vec<CfgExpr>, Error>>()?
.into_iter()
.reduce(|mut acc, e| {
acc.merge(e);
acc

Check warning on line 24 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L23-L24

Added lines #L23 - L24 were not covered by tests
});

// Evaluate the CfgExpr against the CfgEvaluator
if let Some(cfg_expr) = cfg_expr {
try_eval(cfg_evaluator, &cfg_expr).map_err(|errs| {
errs.into_iter()
.reduce(|mut acc, e| {
acc.combine(e);
acc
})
.expect("There should be at least one error")
})

Check warning on line 36 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L29-L36

Added lines #L29 - L36 were not covered by tests
} else {
Ok(true)
}
}

fn try_eval(cfg_evaluator: &dyn CfgEvaluator, expr: &CfgExpr) -> Result<bool, Vec<Error>> {
match expr {
CfgExpr::Unconditional => Ok(true),
CfgExpr::Eq(ident, string) => {
let key = ident.to_string();
let value = string.as_ref().map(LitStr::value);
match cfg_evaluator.eval(&key, value.as_deref()) {
CfgResult::True => Ok(true),
CfgResult::False => Ok(false),
CfgResult::Undetermined { msg } => {
let span = quote!(#ident #string);
Err(vec![Error::new_spanned(span, msg)])

Check warning on line 53 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L42-L53

Added lines #L42 - L53 were not covered by tests
}
}
}
CfgExpr::All(list) => {
let mut all_errors = Vec::new();
for subexpr in list {
match try_eval(cfg_evaluator, subexpr) {
Ok(true) => {}
Ok(false) => return Ok(false),
Err(errors) => all_errors.extend(errors),

Check warning on line 63 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L57-L63

Added lines #L57 - L63 were not covered by tests
}
}
if all_errors.is_empty() {
Ok(true)

Check warning on line 67 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L66-L67

Added lines #L66 - L67 were not covered by tests
} else {
Err(all_errors)

Check warning on line 69 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L69

Added line #L69 was not covered by tests
}
}
CfgExpr::Any(list) => {
let mut all_errors = Vec::new();
for subexpr in list {
match try_eval(cfg_evaluator, subexpr) {
Ok(true) => return Ok(true),
Ok(false) => {}
Err(errors) => all_errors.extend(errors),

Check warning on line 78 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L72-L78

Added lines #L72 - L78 were not covered by tests
}
}
if all_errors.is_empty() {
Ok(false)

Check warning on line 82 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L81-L82

Added lines #L81 - L82 were not covered by tests
} else {
Err(all_errors)

Check warning on line 84 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L84

Added line #L84 was not covered by tests
}
}
CfgExpr::Not(subexpr) => match try_eval(cfg_evaluator, subexpr) {
Ok(value) => Ok(!value),
Err(errors) => Err(errors),

Check warning on line 89 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L87-L89

Added lines #L87 - L89 were not covered by tests
},
}
}

Check warning on line 92 in crates/cxx-qt-gen/src/generator/cfg.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cfg.rs#L92

Added line #L92 was not covered by tests
19 changes: 14 additions & 5 deletions crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::{
generator::cpp::signal::generate_cpp_signal, naming::TypeNames,
parser::externcxxqt::ParsedExternCxxQt, CppFragment,
generator::{cfg::try_eval_attributes, cpp::signal::generate_cpp_signal, GeneratedOpt},
naming::TypeNames,
parser::externcxxqt::ParsedExternCxxQt,
CppFragment,
};
use std::collections::BTreeSet;
use syn::Result;
Expand All @@ -23,11 +25,17 @@ pub struct GeneratedCppExternCxxQtBlocks {
pub fn generate(
blocks: &[ParsedExternCxxQt],
type_names: &TypeNames,
opt: &GeneratedOpt,
) -> Result<Vec<GeneratedCppExternCxxQtBlocks>> {
let mut out = vec![];

for block in blocks {
for signal in &block.signals {
// Skip if the cfg attributes are not resolved to true
if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &signal.cfgs)? {
continue;

Check warning on line 36 in crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs#L36

Added line #L36 was not covered by tests
}

let mut block = GeneratedCppExternCxxQtBlocks::default();
let qobject_name = type_names.lookup(&signal.qobject_ident)?;
let data = generate_cpp_signal(signal, qobject_name, type_names)?;
Expand Down Expand Up @@ -70,9 +78,10 @@ mod tests {
.unwrap()];

// Unknown types
assert!(generate(&blocks, &TypeNames::default()).is_err());
let opt = GeneratedOpt::default();
assert!(generate(&blocks, &TypeNames::default(), &opt).is_err());

let generated = generate(&blocks, &TypeNames::mock()).unwrap();
let generated = generate(&blocks, &TypeNames::mock(), &opt).unwrap();
assert_eq!(generated.len(), 2);
}

Expand All @@ -97,7 +106,7 @@ mod tests {
let mut type_names = TypeNames::default();
type_names.mock_insert("ObjRust", None, Some("ObjCpp"), Some("mynamespace"));

let generated = generate(&blocks, &type_names).unwrap();
let generated = generate(&blocks, &type_names, &GeneratedOpt::default()).unwrap();
assert_eq!(generated.len(), 1);
}
}
19 changes: 17 additions & 2 deletions crates/cxx-qt-gen/src/generator/cpp/inherit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
use indoc::formatdoc;

use crate::{
generator::cpp::{fragment::CppFragment, qobject::GeneratedCppQObjectBlocks},
generator::{
cfg::try_eval_attributes,
cpp::{fragment::CppFragment, qobject::GeneratedCppQObjectBlocks},
GeneratedOpt,
},
naming::cpp::syn_type_to_cpp_return_type,
naming::TypeNames,
parser::inherit::ParsedInheritedMethod,
Expand All @@ -17,10 +21,16 @@ pub fn generate(
inherited_methods: &[&ParsedInheritedMethod],
base_class: &Option<String>,
type_names: &TypeNames,
opt: &GeneratedOpt,
) -> Result<GeneratedCppQObjectBlocks> {
let mut result = GeneratedCppQObjectBlocks::default();

for &method in inherited_methods {
// Skip if the cfg attributes are not resolved to true
if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &method.cfgs)? {
continue;

Check warning on line 31 in crates/cxx-qt-gen/src/generator/cpp/inherit.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cpp/inherit.rs#L31

Added line #L31 was not covered by tests
}

let return_type = syn_type_to_cpp_return_type(&method.method.sig.output, type_names)?;
// Note that no qobject macro with no base class is an error
//
Expand Down Expand Up @@ -62,7 +72,12 @@ mod tests {
let method = ParsedInheritedMethod::parse(method, Safety::Safe, CaseConversion::none())?;
let inherited_methods = vec![&method];
let base_class = base_class.map(|s| s.to_owned());
generate(&inherited_methods, &base_class, &TypeNames::default())
generate(
&inherited_methods,
&base_class,
&TypeNames::default(),
&GeneratedOpt::default(),
)
}

fn assert_generated_eq(expected: &str, generated: &GeneratedCppQObjectBlocks) {
Expand Down
28 changes: 22 additions & 6 deletions crates/cxx-qt-gen/src/generator/cpp/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::generator::cpp::get_cpp_params;
use crate::{
generator::cpp::{
fragment::{CppFragment, CppNamedType},
qobject::GeneratedCppQObjectBlocks,
generator::{
cfg::try_eval_attributes,
cpp::{
fragment::{CppFragment, CppNamedType},
get_cpp_params,
qobject::GeneratedCppQObjectBlocks,
GeneratedOpt,
},
},
naming::cpp::{syn_return_type_to_cpp_except, syn_type_to_cpp_return_type},
naming::TypeNames,
Expand All @@ -18,9 +22,15 @@ use syn::Result;
pub fn generate_cpp_methods(
invokables: &Vec<&ParsedMethod>,
type_names: &TypeNames,
opt: &GeneratedOpt,
) -> Result<GeneratedCppQObjectBlocks> {
let mut generated = GeneratedCppQObjectBlocks::default();
for &invokable in invokables {
// Skip if the cfg attributes are not resolved to true
if !try_eval_attributes(opt.cfg_evaluator.as_ref(), &invokable.cfgs)? {
continue;

Check warning on line 31 in crates/cxx-qt-gen/src/generator/cpp/method.rs

View check run for this annotation

Codecov / codecov/patch

crates/cxx-qt-gen/src/generator/cpp/method.rs#L31

Added line #L31 was not covered by tests
}

let return_cxx_ty = syn_type_to_cpp_return_type(&invokable.method.sig.output, type_names)?;

let parameters: Vec<CppNamedType> = get_cpp_params(&invokable.method, type_names)?;
Expand Down Expand Up @@ -122,7 +132,12 @@ mod tests {
let mut type_names = TypeNames::mock();
type_names.mock_insert("QColor", None, None, None);

let generated = generate_cpp_methods(&invokables.iter().collect(), &type_names).unwrap();
let generated = generate_cpp_methods(
&invokables.iter().collect(),
&type_names,
&GeneratedOpt::default(),
)
.unwrap();

// methods
assert_eq!(generated.methods.len(), 5);
Expand Down Expand Up @@ -168,7 +183,8 @@ mod tests {
type_names.mock_insert("A", None, Some("A1"), None);
type_names.mock_insert("B", None, Some("B2"), None);

let generated = generate_cpp_methods(&invokables, &type_names).unwrap();
let generated =
generate_cpp_methods(&invokables, &type_names, &GeneratedOpt::default()).unwrap();

// methods
assert_eq!(generated.methods.len(), 1);
Expand Down
16 changes: 11 additions & 5 deletions crates/cxx-qt-gen/src/generator/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ use std::collections::BTreeSet;
use crate::generator::cpp::fragment::CppNamedType;
use crate::naming::cpp::syn_type_to_cpp_type;
use crate::naming::TypeNames;
use crate::{generator::structuring, parser::Parser};
use crate::{
generator::{structuring, GeneratedOpt},
parser::Parser,
};
use externcxxqt::GeneratedCppExternCxxQtBlocks;
use qobject::GeneratedCppQObject;
use syn::{FnArg, ForeignItemFn, Pat, PatIdent, PatType, Result};
Expand All @@ -42,7 +45,7 @@ pub struct GeneratedCppBlocks {

impl GeneratedCppBlocks {
/// Create a [GeneratedCppBlocks] from the given [Parser] object
pub fn from(parser: &Parser) -> Result<GeneratedCppBlocks> {
pub fn from(parser: &Parser, opt: &GeneratedOpt) -> Result<GeneratedCppBlocks> {
let structures = structuring::Structures::new(&parser.cxx_qt_data)?;

let mut includes = BTreeSet::new();
Expand All @@ -66,11 +69,12 @@ impl GeneratedCppBlocks {
qobjects: structures
.qobjects
.iter()
.map(|qobject| GeneratedCppQObject::from(qobject, &parser.type_names))
.map(|qobject| GeneratedCppQObject::from(qobject, &parser.type_names, opt))
.collect::<Result<Vec<GeneratedCppQObject>>>()?,
extern_cxx_qt: externcxxqt::generate(
&parser.cxx_qt_data.extern_cxxqt_blocks,
&parser.type_names,
opt,
)?,
})
}
Expand Down Expand Up @@ -125,7 +129,8 @@ mod tests {
};
let parser = Parser::from(module).unwrap();

let cpp = GeneratedCppBlocks::from(&parser).unwrap();
let opt = GeneratedOpt::default();
let cpp = GeneratedCppBlocks::from(&parser, &opt).unwrap();
assert_eq!(cpp.qobjects.len(), 1);
assert_eq!(cpp.qobjects[0].name.namespace(), None);
}
Expand All @@ -143,7 +148,8 @@ mod tests {
};
let parser = Parser::from(module).unwrap();

let cpp = GeneratedCppBlocks::from(&parser).unwrap();
let opt = GeneratedOpt::default();
let cpp = GeneratedCppBlocks::from(&parser, &opt).unwrap();
assert_eq!(cpp.qobjects[0].name.namespace(), Some("cxx_qt"));
}
}
Loading

0 comments on commit 457c3eb

Please sign in to comment.