diff --git a/CHANGELOG.md b/CHANGELOG.md index 2884a2140..9f648e51a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `#[qobject]` attribute is now optional on types in `extern "RustQt"` - `#[qobject]` attribute is now required on types in `extern "C++Qt"` - Folder structure of Rust bridges is now considered in the same way as CXX in `CxxQtBuilder` +- All `#[cxx_qt::bridge]` macros must specify a `cxx_file_stem` until `Span::source_file()` is stable ### Fixed diff --git a/book/src/bridge/extern_rustqt.md b/book/src/bridge/extern_rustqt.md index a3bbe2ae8..0d928b7c7 100644 --- a/book/src/bridge/extern_rustqt.md +++ b/book/src/bridge/extern_rustqt.md @@ -23,7 +23,9 @@ mod ffi { The `extern "RustQt"` section of a CXX bridge declares Rust types and signatures to be made available to Qt and C++. -The CXX code generator uses your `extern "Rust"` section(s) to produce a C++ header file containing the corresponding C++ declarations. The generated header has a file name matching the module ident or the `cxx_file_stem` field in the `#[cxx_qt::bridge]` attribute and with a `.cxxqt.h` file extension. +The CXX code generator uses your `extern "Rust"` section(s) to produce a C++ header file containing the corresponding C++ declarations. The generated header has a file name of the `cxx_file_stem` field in the `#[cxx_qt::bridge]` attribute and with a `.cxxqt.h` file extension. + +> Note that once there is support for `source_file` or similar in the `Span` macro we want to support copying the file name like CXX. A bridge module may contain zero or more `extern "RustQt"` blocks. diff --git a/book/src/bridge/index.md b/book/src/bridge/index.md index 3cd4da9d6..e9adedf44 100644 --- a/book/src/bridge/index.md +++ b/book/src/bridge/index.md @@ -30,7 +30,7 @@ By default, the name of the generated C++ header file will be the name of the mo This can cause issues as the module is normally called `ffi` or `qobject`, so collisions would occur. -The `cxx_file_stem` option allow a file name to be specified to avoid collisions. +The `cxx_file_stem` option allows a file name to be specified to avoid collisions. ```rust,ignore {{#include ../../../examples/qml_features/rust/src/types.rs:book_cxx_file_stem}} diff --git a/book/src/getting-started/5-cmake-integration.md b/book/src/getting-started/5-cmake-integration.md index 881c67e90..ca3eadb82 100644 --- a/book/src/getting-started/5-cmake-integration.md +++ b/book/src/getting-started/5-cmake-integration.md @@ -38,11 +38,15 @@ You can add as much C++ code as you want in addition to this. For every `#[cxx_qt::bridge]` that we define in Rust, CXX-Qt will generate a corresponding C++ header file. They will always be in the `cxx-qt-gen/` include path and use the snake_case naming convention. -The name of the header file will be the name of the Rust module of your `#[cxx_qt::bridge]`, followed by `.cxxqt.h`. -So in our case: `#include cxx-qt-gen/qobject.cxxqt.h` +The name of the header file will be the `cxx_file_stem` of your `#[cxx_qt::bridge]`, followed by `.cxxqt.h`. +So in our case: `#include cxx-qt-gen/src/cxxqt_object.cxxqt.h` > Note that the [`cxx_file_stem`](../bridge/index.md#cxx_file_stem) option can be specified in the bridge macro to choose the file name. +> Note that once `Span::source_file()` is stable the `cxx_file_stem` will be optional and the file name will be the default like CXX> + +> Note any folders relative to the fargo manifest are considered hence the `src` folder. + Including the generated header allows accessing the `MyObject` C++ class, just like any other C++ class. Inherit from it, connect signals and slots to it, put it in a QVector, do whatever you want with it. That's the power of CXX-Qt. diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 7edf6d5cd..1a5e0a77e 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -130,13 +130,9 @@ impl GeneratedCpp { .map_err(to_diagnostic)?; let rust_tokens = write_rust(&generated_rust); // Use the relative path with the cxx_file_stem - // - // TODO: ideally CXX-Qt would also use the file name - // but it uses the module or cxx_file_stem for now - // https://github.com/KDAB/cxx-qt/pull/200/commits/4861c92e66c3a022d3f0dedd9f8fd20db064b42b file_ident = relative_path .as_ref() - .with_file_name(parser.cxx_file_stem) + .with_file_name(parser.cxx_file_stem()) .to_str() .unwrap() .to_owned(); @@ -261,7 +257,7 @@ fn generate_cxxqt_cpp_files( let mut generated_file_paths: Vec = Vec::with_capacity(rs_source.len()); for rs_path in rs_source { - let cpp_directory = format!("{}/cxx-qt-gen/src", env::var("OUT_DIR").unwrap()); + let cpp_directory = format!("{}/cxx-qt-gen", env::var("OUT_DIR").unwrap()); let path = format!("{manifest_dir}/{}", rs_path.as_ref().display()); println!("cargo:rerun-if-changed={path}"); diff --git a/crates/cxx-qt-gen/src/generator/cpp/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/mod.rs index 0dd8028e7..6e540cd19 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/mod.rs @@ -59,7 +59,7 @@ impl GeneratedCppBlocks { Ok(GeneratedCppBlocks { forward_declares, includes, - cxx_file_stem: parser.cxx_file_stem.clone(), + cxx_file_stem: parser.cxx_file_stem(), qobjects: parser .cxx_qt_data .qobjects @@ -86,7 +86,7 @@ mod tests { #[test] fn test_generated_cpp_blocks() { let module: ItemMod = parse_quote! { - #[cxx_qt::bridge] + #[cxx_qt::bridge(cxx_file_stem = "ffi")] mod ffi { extern "RustQt" { #[qobject] @@ -124,7 +124,7 @@ mod tests { #[test] fn test_generated_cpp_blocks_namespace() { let module: ItemMod = parse_quote! { - #[cxx_qt::bridge(namespace = "cxx_qt")] + #[cxx_qt::bridge(cxx_file_stem = "ffi", namespace = "cxx_qt")] mod ffi { extern "RustQt" { #[qobject] diff --git a/crates/cxx-qt-gen/src/generator/rust/mod.rs b/crates/cxx-qt-gen/src/generator/rust/mod.rs index b77f804f2..981b5f645 100644 --- a/crates/cxx-qt-gen/src/generator/rust/mod.rs +++ b/crates/cxx-qt-gen/src/generator/rust/mod.rs @@ -79,7 +79,7 @@ impl GeneratedRustBlocks { /// Generate the include line for this parsed block fn generate_include(parser: &Parser) -> Result { - let import_path = format!("{}.cxxqt.h", parser.cxx_file_stem); + let import_path = format!("{}.cxxqt.h", parser.cxx_file_stem()); syn::parse2(quote! { unsafe extern "C++" { diff --git a/crates/cxx-qt-gen/src/parser/mod.rs b/crates/cxx-qt-gen/src/parser/mod.rs index 150b9f5f8..39cec569c 100644 --- a/crates/cxx-qt-gen/src/parser/mod.rs +++ b/crates/cxx-qt-gen/src/parser/mod.rs @@ -36,13 +36,13 @@ pub struct Parser { /// all type names that were found in this module, including CXX types pub(crate) type_names: TypeNames, /// The stem of the file that the CXX headers for this module will be generated into - pub cxx_file_stem: String, + pub(crate) cxx_file_stem: Option, } impl Parser { - fn parse_mod_attributes(module: &mut ItemMod) -> Result<(Option, String)> { + fn parse_mod_attributes(module: &mut ItemMod) -> Result<(Option, Option)> { let mut namespace = None; - let mut cxx_file_stem = module.ident.to_string(); + let mut cxx_file_stem = None; // Remove the cxx_qt::bridge attribute if let Some(attr) = attribute_take_path(&mut module.attrs, &["cxx_qt", "bridge"]) { @@ -58,13 +58,21 @@ impl Parser { namespace = Some(expr_to_string(&name_value.value)?); // Parse any custom file stem } else if name_value.path.is_ident("cxx_file_stem") { - cxx_file_stem = expr_to_string(&name_value.value)?; + cxx_file_stem = Some(expr_to_string(&name_value.value)?); } } _others => {} } } } + + // Temporary until Span::source_file() is stable + if cxx_file_stem.is_none() { + return Err(Error::new( + module.span(), + "All cxx_qt::bridge macros specify a cxx_file_stem until Span::source_file() is stable https://github.com/rust-lang/rust/issues/54725", + )); + } } else { return Err(Error::new( module.span(), @@ -127,6 +135,17 @@ impl Parser { cxx_file_stem, }) } + + /// Determine the file stem to use for this macro + pub fn cxx_file_stem(&self) -> String { + if let Some(cxx_file_stem) = self.cxx_file_stem.as_ref() { + cxx_file_stem.clone() + } else { + // TODO: ideally CXX-Qt would also use the file name but it uses the cxx_file_stem for now + // https://github.com/KDAB/cxx-qt/pull/200/commits/4861c92e66c3a022d3f0dedd9f8fd20db064b42b + unimplemented!("Missing cxx_file_stem, once Span::source_file() is stable we can determine the file name") + } + } } #[cfg(test)] diff --git a/examples/qml_minimal/rust/src/cxxqt_object.rs b/examples/qml_minimal/rust/src/cxxqt_object.rs index 60d7ec8ab..f6a2e99e7 100644 --- a/examples/qml_minimal/rust/src/cxxqt_object.rs +++ b/examples/qml_minimal/rust/src/cxxqt_object.rs @@ -9,7 +9,7 @@ // ANCHOR: book_bridge_macro /// The bridge definition for our QObject -#[cxx_qt::bridge] +#[cxx_qt::bridge(cxx_file_stem = "cxxqt_object")] pub mod qobject { // ANCHOR_END: book_bridge_macro