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
29 changes: 22 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 @@ -42,6 +42,18 @@ pub(crate) fn clean(path: impl AsRef<Path>) -> Result<()> {
Ok(())
}

// Clean files from a directory but not folders
pub(crate) fn clean_files(path: impl AsRef<Path>) -> Result<()> {
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
if !path.is_dir() {
std::fs::remove_file(path)?;
}
}
Ok(())
}

/// The target directory, namespaced by crate
pub(crate) fn crate_target() -> PathBuf {
let path = target();
Expand All @@ -64,27 +76,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
53 changes: 41 additions & 12 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::{QmlFile, QmlModule, QmlUri};
pub use qt_build_utils::MocArguments;
use qt_build_utils::MocProducts;
use qt_build_utils::QResources;
use qt_build_utils::QmlLsIniBuilder;
use quote::ToTokens;
use std::{
collections::HashSet,
Expand Down Expand Up @@ -295,13 +296,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 @@ -323,7 +317,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 @@ -791,7 +785,9 @@ 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))
// Only clean files from the module directory, as we may have
// sub uris exported into sub folders
dir::clean_files(dir::module_target(&qml_module.uri))
.expect("Failed to clean qml module export directory!");

// Check that all rust files are within the same directory
Expand Down Expand Up @@ -854,7 +850,7 @@ 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,
);
Expand All @@ -878,22 +874,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 file in qml_module.qml_files {
println!("cargo::rerun-if-changed={}", file.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
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
34 changes: 31 additions & 3 deletions crates/cxx-qt-gen/src/writer/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ mod tests {
},
has_qobject_macro: true,
blocks: GeneratedCppQObjectBlocks {
base_classes: vec!["QStringListModel".to_owned()],
base_classes: vec!["public QStringListModel".to_owned()],
includes: {
let mut includes = BTreeSet::<String>::default();
includes.insert("#include <test>".to_owned());
Expand Down Expand Up @@ -233,7 +233,7 @@ mod tests {
namespace_internals: "cxx_qt::cxx_qt_first_object".to_owned(),
has_qobject_macro: true,
blocks: GeneratedCppQObjectBlocks {
base_classes: vec!["QStringListModel".to_owned()],
base_classes: vec!["public QStringListModel".to_owned()],
includes: {
let mut includes = BTreeSet::<String>::default();
includes.insert("#include <test>".to_owned());
Expand Down Expand Up @@ -276,7 +276,7 @@ mod tests {
namespace_internals: "cxx_qt::cxx_qt_second_object".to_owned(),
has_qobject_macro: true,
blocks: GeneratedCppQObjectBlocks {
base_classes: vec!["QStringListModel".to_owned()],
base_classes: vec!["public QStringListModel".to_owned()],
includes: {
let mut includes = BTreeSet::<String>::default();
includes.insert("#include <test>".to_owned());
Expand Down Expand Up @@ -374,6 +374,13 @@ mod tests {
void privateMethod() const;
void privateMethod();


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);
};

static_assert(::std::is_base_of<QObject, MyObject>::value, "MyObject must inherit from QObject");
Expand Down Expand Up @@ -428,6 +435,13 @@ mod tests {
Q_SIGNAL void countChanged();



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);
};

static_assert(::std::is_base_of<QObject, FirstObject>::value, "FirstObject must inherit from QObject");
Expand Down Expand Up @@ -455,6 +469,13 @@ mod tests {
private:
void privateMethod() const;


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);
};

static_assert(::std::is_base_of<QObject, SecondObject>::value, "SecondObject must inherit from QObject");
Expand Down Expand Up @@ -506,6 +527,13 @@ mod tests {
void privateMethod() const;
void privateMethod();


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);
};

static_assert(::std::is_base_of<QObject, MyObject>::value, "MyObject must inherit from QObject");
Expand Down
Loading
Loading