Skip to content
Merged
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
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ members = [
"examples/qml_multi_crates/rust/main",
"examples/qml_multi_crates/rust/sub1",
"examples/qml_multi_crates/rust/sub2",
"examples/qml_multi_crates/rust/sub3",
"examples/span-inspector",

"tests/basic_cxx_only/rust",
Expand Down
73 changes: 41 additions & 32 deletions crates/cxx-qt-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ pub use opts::CxxQtBuildersOpts;
pub use opts::QObjectHeaderOpts;

mod qml_modules;
pub use qml_modules::QmlModule;
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::QmlUri;
use quote::ToTokens;
use std::{
collections::HashSet,
Expand Down Expand Up @@ -433,10 +432,12 @@ impl CxxQtBuilder {
/// .files(["src/cxxqt_object.rs"])
/// .build();
/// ```
///
/// Note: This will automatically add the `Qml` Qt module to the build (see [Self::qt_module]).
pub fn new_qml_module(module: QmlModule) -> Self {
let mut builder = Self::new();
builder.qml_module = Some(module);
builder
builder.qt_module("Qml")
}

/// Specify rust file paths to parse through the cxx-qt marco
Expand Down Expand Up @@ -881,8 +882,8 @@ impl CxxQtBuilder {
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());
for file in qml_module.qml_files {
println!("cargo::rerun-if-changed={}", file.path().display());
}

let module_init_key = qml_module_init_key(&qml_module.uri);
Expand Down Expand Up @@ -1110,33 +1111,41 @@ extern "C" bool {init_fun}() {{
// to the generated files without any namespacing.
include_paths.push(header_root.join(&self.include_prefix));

const MAX_INCLUDE_DEPTH: usize = 6;
let crate_header_dir = self.crate_include_root.as_ref().map(|subdir| {
dir::manifest()
.expect("Could not find crate directory!")
.join(subdir)
});
if let Some(crate_header_dir) = crate_header_dir {
utils::best_effort_copy_headers(
crate_header_dir.as_path(),
header_root.join(crate_name()).as_path(),
MAX_INCLUDE_DEPTH,
// Emit rerun-if-changed for this directory as it is be part of the crate root it
// should not contain any generated files which may cause unwanted reruns.
true,
);
}
for include_dir in &self.additional_include_dirs {
utils::best_effort_copy_headers(
include_dir,
header_root.join(crate_name()).as_path(),
MAX_INCLUDE_DEPTH,
// Do not emit rerun-if-changed for this directory as it may not be part of the crate root
// and we do not know if these headers are generated or not.
// If they are generated by the build script, they should not be marked with
// rerun-if-changed, because they would cause unwanted reruns.
false,
);
// Export the generated headers for this crate
{
const MAX_INCLUDE_DEPTH: usize = 6;
let crate_header_dir = self.crate_include_root.as_ref().map(|subdir| {
dir::manifest()
.expect("Could not find crate directory!")
.join(subdir)
});
let crate_header_export_dir = header_root.join(crate_name());
// Make sure to always create the crate header root, as it is automatically added to
// the manifest's exported include prefixes.
std::fs::create_dir_all(&crate_header_export_dir)
.expect("Could not create crate header root directory");
if let Some(crate_header_dir) = crate_header_dir {
utils::best_effort_copy_headers(
crate_header_dir.as_path(),
&crate_header_export_dir,
MAX_INCLUDE_DEPTH,
// Emit rerun-if-changed for this directory as it is be part of the crate root it
// should not contain any generated files which may cause unwanted reruns.
true,
);
}
for include_dir in &self.additional_include_dirs {
utils::best_effort_copy_headers(
include_dir,
&crate_header_export_dir,
MAX_INCLUDE_DEPTH,
// Do not emit rerun-if-changed for this directory as it may not be part of the crate root
// and we do not know if these headers are generated or not.
// If they are generated by the build script, they should not be marked with
// rerun-if-changed, because they would cause unwanted reruns.
false,
);
}
}

Self::setup_cc_builder(&mut self.cc_builder, &include_paths);
Expand Down
26 changes: 18 additions & 8 deletions crates/cxx-qt-build/src/qml_modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

//! This Rust module contains structs for registering QML modules.

use std::path::{Path, PathBuf};

use qt_build_utils::QmlUri;
pub use qt_build_utils::{QmlFile, QmlUri};

/// This is a description of a QML module for building by the [crate::CxxQtBuilder].
///
Expand All @@ -19,7 +17,7 @@ pub struct QmlModule {
pub(crate) uri: QmlUri,
pub(crate) version_major: usize,
pub(crate) version_minor: usize,
pub(crate) qml_files: Vec<PathBuf>,
pub(crate) qml_files: Vec<QmlFile>,
pub(crate) depends: Vec<String>,
}

Expand Down Expand Up @@ -66,14 +64,26 @@ impl QmlModule {
///
/// Additional resources such as images can be added to the Qt resources for the QML module by specifying
/// the `qrc_files` field.
pub fn qml_file(self, file: impl AsRef<Path>) -> Self {
///
/// If the Qml file starts is uppercase, it will be treated as a QML component and registered in the `qmldir` file.
/// See [qt_build_utils::QmlFile] for more information on configuring the behavior of QML files.
///
/// Note that if no version is specified for the QML file, the version of the QML module will
/// be used automatically.
pub fn qml_file(self, file: impl Into<QmlFile>) -> Self {
self.qml_files([file])
}

/// Add multiple QML files to the module, see [Self::qml_file].
pub fn qml_files(mut self, files: impl IntoIterator<Item = impl AsRef<Path>>) -> Self {
self.qml_files
.extend(files.into_iter().map(|p| p.as_ref().to_path_buf()));
pub fn qml_files(mut self, files: impl IntoIterator<Item = impl Into<QmlFile>>) -> Self {
self.qml_files.extend(files.into_iter().map(|p| {
let qml_file = p.into();
if qml_file.get_version().is_none() {
qml_file.version(self.version_major, self.version_minor)
} else {
qml_file
}
}));
self
}
}
33 changes: 18 additions & 15 deletions crates/qt-build-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ mod platform;
pub use platform::QtPlatformLinker;

mod qml;
pub use qml::{QmlDirBuilder, QmlPluginCppBuilder, QmlUri};
pub use qml::{QmlDirBuilder, QmlFile, QmlPluginCppBuilder, QmlUri};

mod qrc;
pub use qrc::{QResource, QResourceFile, QResources};
Expand Down Expand Up @@ -156,7 +156,7 @@ impl QtBuild {
version_major: usize,
version_minor: usize,
plugin_name: &str,
qml_files: &[impl AsRef<Path>],
qml_files: &[QmlFile],
depends: impl IntoIterator<Item = impl Into<String>>,
) -> QmlModuleRegistrationFiles {
let qml_uri_dirs = uri.as_dirs();
Expand All @@ -176,16 +176,19 @@ impl QtBuild {
// Generate qmldir file
let qmldir_file_path = qml_module_dir.join("qmldir");
{
let qml_type_files = qml_files.iter().filter(|path| {
// Qt by default only includes uppercase files in the qmldir file.
// Mirror this behavior.
path.as_ref()
.file_name()
.and_then(OsStr::to_str)
.and_then(|file_name| file_name.chars().next())
.map(char::is_uppercase)
.unwrap_or_default()
});
let qml_type_files = qml_files
.iter()
.filter(|file| {
// Qt by default only includes uppercase files in the qmldir file.
// Mirror this behavior.
file.path()
.file_name()
.and_then(OsStr::to_str)
.and_then(|file_name| file_name.chars().next())
.map(char::is_uppercase)
.unwrap_or_default()
})
.cloned();
let mut file = File::create(&qmldir_file_path).expect("Could not create qmldir file");
QmlDirBuilder::new(uri.clone())
.depends(depends)
Expand Down Expand Up @@ -226,8 +229,8 @@ impl QtBuild {
.file(QResourceFile::new(resolved).alias(path.display().to_string()))
}

for path in qml_files {
resource = resource_add_path(resource, path.as_ref());
for file in qml_files {
resource = resource_add_path(resource, file.path());
}
resource
})
Expand All @@ -250,7 +253,7 @@ impl QtBuild {
let mut qml_resource_paths = Vec::new();
for file in qml_files {
let result = QtToolQmlCacheGen::new(self.qt_installation.as_ref())
.compile(qml_cache_args.clone(), file);
.compile(qml_cache_args.clone(), file.path());
qmlcachegen_file_paths.push(result.qml_cache_path);
qml_resource_paths.push(result.qml_resource_path);
}
Expand Down
3 changes: 3 additions & 0 deletions crates/qt-build-utils/src/qml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ pub use qmlplugincpp::QmlPluginCppBuilder;

mod qmluri;
pub use qmluri::QmlUri;

mod qmlfile;
pub use qmlfile::QmlFile;
45 changes: 32 additions & 13 deletions crates/qt-build-utils/src/qml/qmldir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::QmlUri;
use crate::qml::{QmlFile, QmlUri};

use std::ffi::OsStr;
use std::io;
use std::path::{Path, PathBuf};

/// QML module definition files builder
///
Expand All @@ -18,7 +17,7 @@ pub struct QmlDirBuilder {
plugin: Option<(bool, String)>,
type_info: Option<String>,
uri: QmlUri,
qml_files: Vec<PathBuf>,
qml_files: Vec<QmlFile>,
}

impl QmlDirBuilder {
Expand Down Expand Up @@ -66,23 +65,39 @@ impl QmlDirBuilder {

for qml_file in &self.qml_files {
let is_qml_file = qml_file
.path()
.extension()
.map(|ext| ext.eq_ignore_ascii_case("qml"))
.unwrap_or_default();

if !is_qml_file {
panic!("QML file does not end with .qml: {}", qml_file.display(),);
panic!(
"QML file does not end with .qml: {}",
qml_file.path().display(),
);
}

let path = qml_file.display();
let path = qml_file.path().display();
let qml_component_name = qml_file
.path()
.file_stem()
.and_then(OsStr::to_str)
.expect("Could not get qml file stem");

let singleton = if qml_file.is_singleton() {
"singleton "
} else {
""
};

// Qt6 simply uses version 254.0 if no specific version is provided
// Until we support versions of individal qml files, we will use 254.0
writeln!(writer, "{qml_component_name} 254.0 {path}",)
let version = if let Some((major, minor)) = qml_file.get_version() {
format!("{}.{}", major, minor)
} else {
"254.0".to_string()
};

writeln!(writer, "{singleton}{qml_component_name} {version} {path}",)
.expect("Could not write qmldir file");
}

Expand Down Expand Up @@ -139,11 +154,9 @@ impl QmlDirBuilder {
}

/// Declares a list of .qml files that are part of the module.
pub fn qml_files(mut self, qml_files: impl IntoIterator<Item = impl AsRef<Path>>) -> Self {
self.qml_files = qml_files
.into_iter()
.map(|p| p.as_ref().to_owned())
.collect();
pub fn qml_files(mut self, qml_files: impl IntoIterator<Item = impl Into<QmlFile>>) -> Self {
self.qml_files
.extend(qml_files.into_iter().map(|p| p.into()));
self
}

Expand Down Expand Up @@ -176,7 +189,11 @@ mod test {
.depends(["QtQuick", "com.kdab.a"])
.plugin("P", true)
.type_info("T")
.qml_files(&["qml/Test.qml"])
.qml_files(["qml/Test.qml"])
.qml_files([QmlFile::from("qml/MySingleton.qml")
.singleton(true)
.version(1, 0)])
.qml_files([QmlFile::from("../AnotherFile.qml").version(2, 123)])
.write(&mut result)
.unwrap();
assert_eq!(
Expand All @@ -189,6 +206,8 @@ depends QtQuick
depends com.kdab.a
prefer :/qt/qml/com/kdab/
Test 254.0 qml/Test.qml
singleton MySingleton 1.0 qml/MySingleton.qml
AnotherFile 2.123 ../AnotherFile.qml
"
);
}
Expand Down
Loading
Loading