Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ compile_commands.json

# clangd cache
.cache/

*.qmlls.ini
17 changes: 10 additions & 7 deletions crates/cxx-qt-build/src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use qt_build_utils::QmlUri;

use crate::{crate_name, module_name_from_uri};
use crate::crate_name;
use std::io::Result;
use std::{
env, fs,
Expand Down Expand Up @@ -64,27 +64,30 @@ pub(crate) fn module_target(module_uri: &QmlUri) -> PathBuf {
out()
// Use a short name due to the Windows file path limit!
.join("cxxqtqml")
.join(module_name_from_uri(module_uri))
.join(module_uri.as_dirs())
})
}

/// The export directory, namespaced by QML module
///
/// In conctrast to the crate_export directory, this is `Some` for downstream dependencies as well.
/// In contrast to the crate_export directory, this is `Some` for downstream dependencies as well.
/// This allows CMake to import QML modules from dependencies.
///
/// TODO: This may conflict if two dependencies are building QML modules with the same name!
/// We should probably include a lockfile here to avoid this.
pub(crate) fn module_export(module_uri: &QmlUri) -> Option<PathBuf> {
// In contrast to crate_export, we don't need to check for the specific crate here.
// QML modules should always be exported.
module_export_qml_modules().map(|dir| dir.join(module_uri.as_dirs()))
}

pub(crate) fn module_export_qml_modules() -> Option<PathBuf> {
// In contrast to crate_export, we don't need to check for the specific crate here.
// QML modules should always be exported.
env::var("CXX_QT_EXPORT_DIR")
.ok()
.map(PathBuf::from)
.map(|dir| {
dir.join("qml_modules")
.join(module_name_from_uri(module_uri))
})
.map(|dir| dir.join("qml_modules"))
}

/// The target directory or another directory where we can write files that will be shared
Expand Down
57 changes: 44 additions & 13 deletions crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub use qml_modules::QmlModule;
pub use qt_build_utils::MocArguments;
use qt_build_utils::MocProducts;
use qt_build_utils::QResources;
use qt_build_utils::QmlLsIniBuilder;
use qt_build_utils::QmlUri;
use quote::ToTokens;
use semver::Version;
Expand Down Expand Up @@ -297,13 +298,6 @@ fn generate_cxxqt_cpp_files(
generated_file_paths
}

pub(crate) fn module_name_from_uri(module_uri: &QmlUri) -> String {
// Note: We need to make sure this matches the conversion done in CMake!
// TODO: Replace with as_dirs so qmlls/qmllint can resolve the path
// TODO: This needs an update to cxx-qt-cmake
module_uri.as_underscores()
}

pub(crate) fn crate_name() -> String {
env::var("CARGO_PKG_NAME").unwrap()
}
Expand All @@ -325,7 +319,7 @@ fn crate_init_key() -> String {
}

fn qml_module_init_key(module_uri: &QmlUri) -> String {
format!("qml_module_{}", module_name_from_uri(module_uri))
format!("qml_module_{}", module_uri.as_underscores())
}

/// Run cxx-qt's C++ code generator on Rust modules marked with the `cxx_qt::bridge` macro, compile
Expand Down Expand Up @@ -856,8 +850,11 @@ impl CxxQtBuilder {
// Extract qml_modules out of self so we don't have to hold onto `self` for the duration of
// the loop.
if let Some(qml_module) = self.qml_module.take() {
dir::clean(dir::module_target(&qml_module.uri))
.expect("Failed to clean qml module export directory!");
// TODO: clean the old module target
// however if there is a sub uri this cleans that too
// so we should only remove files and not sub folders?
// dir::clean(dir::module_target(&qml_uri))
// .expect("Failed to clean qml module export directory!");
Comment on lines +853 to +857
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we now produce a/b/qmldir etc if we also had a sub qml module of a/b/c/qmldir that is removed by the clean of the other module, so we would only remove the files from within the export directory and not any sub folders?


// Check that all rust files are within the same directory
//
Expand Down Expand Up @@ -919,8 +916,9 @@ impl CxxQtBuilder {
// TODO: This will be passed to the `optional plugin ...` part of the qmldir
// We don't load any shared libraries, so the name shouldn't matter
// But make sure it still works
&module_name_from_uri(&qml_module.uri),
&qml_module.uri.as_underscores(),
&qml_module.qml_files,
&qml_module.depends,
);
if let Some(qmltyperegistrar) = qml_module_registration_files.qmltyperegistrar {
cc_builder.file(qmltyperegistrar);
Expand All @@ -942,22 +940,55 @@ impl CxxQtBuilder {
for qmlcachegen_file in qml_module_registration_files.qmlcachegen {
cc_builder.file(qmlcachegen_file);
}
// This is required, as described here: plugin_builder
// This is required, as described here: https://doc.qt.io/qt-6/plugins-howto.html#creating-static-plugins
cc_builder.define("QT_STATICPLUGIN", None);

// If any of the files inside the qml module change, then trigger a rerun
for path in qml_module.qml_files {
println!("cargo::rerun-if-changed={}", path.display());
}

// Export the .qmltypes and qmldir files into a stable path, so that tools like
// qmllint/qmlls can find them.
let plugin_dir = dir::module_export(&qml_module.uri);
if let Some(plugin_dir) = &plugin_dir {
std::fs::create_dir_all(plugin_dir).expect("Could not create plugin directory");
std::fs::copy(
qml_module_registration_files.qmltypes,
plugin_dir.join("plugin.qmltypes"),
)
.expect("Could not copy plugin.qmltypes to export directory");
std::fs::copy(
qml_module_registration_files.qmldir,
plugin_dir.join("qmldir"),
)
.expect("Could not copy qmldir to export directory");
}

// Create a .qmlls.ini file with the build dir set similar to QT_QML_GENERATE_QMLLS_INI
if let (Some(qml_modules_dir), Some(manifest_dir)) =
(dir::module_export_qml_modules(), dir::manifest())
{
// TODO: manifest dir is not enough as QML files might be in a parent
// so this should likely be an argument given to cxx-qt-build
// that optionally exports?
let mut file = File::create(manifest_dir.parent().unwrap().join(".qmlls.ini"))
.expect("Could not create qmlls.ini file");
QmlLsIniBuilder::new()
.build_dir(qml_modules_dir)
.no_cmake_calls(true)
.write(&mut file)
.expect("Could not write qmlls.ini")
}

let module_init_key = qml_module_init_key(&qml_module.uri);
let private_initializers = [qml_module_registration_files.plugin_init];
let public_initializer =
Self::generate_public_initializer(&private_initializers, &module_init_key);
self.build_initializers(
&private_initializers,
&public_initializer,
dir::module_export(&qml_module.uri).map(|dir| dir.join("plugin_init.o")),
plugin_dir.map(|dir| dir.join("plugin_init.o")),
&module_init_key,
);

Expand Down
14 changes: 14 additions & 0 deletions crates/cxx-qt-build/src/qml_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct QmlModule {
pub(crate) version_major: usize,
pub(crate) version_minor: usize,
pub(crate) qml_files: Vec<PathBuf>,
pub(crate) depends: Vec<String>,
}

impl QmlModule {
Expand All @@ -32,9 +33,22 @@ impl QmlModule {
version_major: 1,
version_minor: 0,
qml_files: Vec::new(),
depends: Vec::new(),
}
}

/// Add a QML module dependency
pub fn depend(mut self, depend: impl Into<String>) -> Self {
self.depends.push(depend.into());
self
}

/// Add multiple QML module dependencies
pub fn depends<T: Into<String>>(mut self, depends: impl IntoIterator<Item = T>) -> Self {
self.depends.extend(depends.into_iter().map(Into::into));
self
}

/// Add a version to the QML module.
pub fn version(mut self, version_major: usize, version_minor: usize) -> Self {
self.version_major = version_major;
Expand Down
4 changes: 2 additions & 2 deletions crates/cxx-qt-gen/src/generator/cpp/cxxqttype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn generate(qobject_idents: &QObjectNames) -> Result<GeneratedCppQObjectBloc

result
.base_classes
.push(format!("::rust::cxxqt1::CxxQtType<{rust_struct}>"));
.push(format!("private ::rust::cxxqt1::CxxQtType<{rust_struct}>"));

Ok(result)
}
Expand All @@ -42,7 +42,7 @@ mod tests {
assert_eq!(generated.base_classes.len(), 1);
assert_eq!(
generated.base_classes[0],
"::rust::cxxqt1::CxxQtType<MyObjectRust>"
"private ::rust::cxxqt1::CxxQtType<MyObjectRust>"
);
}
}
13 changes: 8 additions & 5 deletions crates/cxx-qt-gen/src/generator/cpp/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ impl GeneratedCppQObject {
// CODECOV_EXCLUDE_STOP
};

generated.blocks.base_classes.push(base_class.clone());
generated
.blocks
.base_classes
.push(format!("public {base_class}"));

// Add the CxxQtType rust and rust_mut methods
generated
Expand Down Expand Up @@ -227,10 +230,10 @@ mod tests {
assert_eq!(cpp.namespace_internals, "cxx_qt_MyObject");

assert_eq!(cpp.blocks.base_classes.len(), 2);
assert_eq!(cpp.blocks.base_classes[0], "QObject");
assert_eq!(cpp.blocks.base_classes[0], "public QObject");
assert_eq!(
cpp.blocks.base_classes[1],
"::rust::cxxqt1::CxxQtType<MyObjectRust>"
"private ::rust::cxxqt1::CxxQtType<MyObjectRust>"
);
assert_eq!(cpp.blocks.metaobjects.len(), 0);
}
Expand Down Expand Up @@ -266,10 +269,10 @@ mod tests {
.unwrap();
assert_eq!(cpp.namespace_internals, "cxx_qt::cxx_qt_MyObject");
assert_eq!(cpp.blocks.base_classes.len(), 2);
assert_eq!(cpp.blocks.base_classes[0], "QStringListModel");
assert_eq!(cpp.blocks.base_classes[0], "public QStringListModel");
assert_eq!(
cpp.blocks.base_classes[1],
"::rust::cxxqt1::CxxQtType<MyObjectRust>"
"private ::rust::cxxqt1::CxxQtType<MyObjectRust>"
);
assert_eq!(cpp.blocks.metaobjects.len(), 0);
}
Expand Down
9 changes: 5 additions & 4 deletions crates/cxx-qt-gen/src/generator/cpp/threading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ pub fn generate(qobject_idents: &QObjectNames) -> Result<(String, GeneratedCppQO
.includes
.insert("#include <cxx-qt/threading.h>".to_owned());

result
.base_classes
.push(format!("::rust::cxxqt1::CxxQtThreading<{cpp_class}>"));
// TODO: this should probably be private too?
result.base_classes.push(format!(
"public ::rust::cxxqt1::CxxQtThreading<{cpp_class}>"
));

let class_initializer = format!("::rust::cxxqt1::CxxQtThreading<{cpp_class}>(this)");

Expand Down Expand Up @@ -90,7 +91,7 @@ mod tests {
assert_eq!(generated.base_classes.len(), 1);
assert_eq!(
generated.base_classes[0],
"::rust::cxxqt1::CxxQtThreading<MyObject>"
"public ::rust::cxxqt1::CxxQtThreading<MyObject>"
);
}
}
16 changes: 14 additions & 2 deletions crates/cxx-qt-gen/src/writer/cpp/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,18 @@ fn qobjects_header(generated: &GeneratedCppBlocks) -> Vec<String> {

{public_methods}
{private_methods}

private:
template<typename Inner, typename Outer>
friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer);

template<typename Inner, typename Outer>
friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer);
}};

{qobject_assert}"#,
// Note that there is always a base class as we always have CxxQtType
base_classes = qobject.blocks.base_classes.iter().map(|base| format!("public {base}")).collect::<Vec<String>>().join(", "),
base_classes = qobject.blocks.base_classes.join(", "),
metaobjects = qobject.blocks.metaobjects.join("\n "),
public_methods = create_block("public", &qobject.blocks.methods.iter().filter_map(pair_as_header).collect::<Vec<String>>()),
private_methods = create_block("private", &qobject.blocks.private_methods.iter().filter_map(pair_as_header).collect::<Vec<String>>()),
Expand Down Expand Up @@ -276,7 +283,7 @@ class MyObject;



class MyObject : public MyBase, public ::rust::cxxqt1::CxxQtType<MyObjectRust>
class MyObject : public MyBase, private ::rust::cxxqt1::CxxQtType<MyObjectRust>
{

public:
Expand All @@ -287,7 +294,12 @@ public:
public:
explicit MyObject();

private:
template<typename Inner, typename Outer>
friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer);

template<typename Inner, typename Outer>
friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer);
};


Expand Down
Loading
Loading