From c2eedba592e013075638e37d102f588343559794 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Sat, 1 Jul 2023 15:10:33 +0200 Subject: [PATCH 01/11] Correctly interpret QT_INSTALL_PREFIX from prl files. --- crates/cxx-qt-build/Cargo.toml | 1 + crates/cxx-qt-build/src/lib.rs | 2 +- crates/cxx-qt-lib/build.rs | 25 +++++---- crates/qt-build-utils/Cargo.toml | 4 ++ crates/qt-build-utils/src/lib.rs | 67 +++++++++++++++-------- crates/qt-build-utils/src/parse_cflags.rs | 21 ++++--- 6 files changed, 76 insertions(+), 44 deletions(-) diff --git a/crates/cxx-qt-build/Cargo.toml b/crates/cxx-qt-build/Cargo.toml index 7849a40c3..0f0cfc672 100644 --- a/crates/cxx-qt-build/Cargo.toml +++ b/crates/cxx-qt-build/Cargo.toml @@ -27,3 +27,4 @@ codespan-reporting = "0.11" default = ["qt_gui", "qt_qml"] qt_gui = ["cxx-qt-lib-headers/qt_gui"] qt_qml = ["cxx-qt-lib-headers/qt_qml"] +include_qt_objects = ["qt-build-utils/include_qt_objects"] diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 2e39b0788..cceea8a1d 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -400,7 +400,7 @@ impl CxxQtBuilder { let mut qtbuild = qt_build_utils::QtBuild::new(self.qt_modules.into_iter().collect()) .expect("Could not find Qt installation"); - qtbuild.cargo_link_libraries(); + qtbuild.cargo_link_libraries(&mut self.cc_builder); // Write cxx-qt-lib and cxx headers cxx_qt_lib_headers::write_headers(format!("{header_root}/cxx-qt-lib")); diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index e73e853ae..c4dce8563 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -15,18 +15,6 @@ fn main() { qt_modules.push("Qml".to_owned()); } - let qtbuild = qt_build_utils::QtBuild::new(qt_modules).expect("Could not find Qt installation"); - qtbuild.cargo_link_libraries(); - // Required for tests - qt_build_utils::setup_linker(); - - // Find the Qt version and tell the Rust compiler - // this allows us to have conditional Rust code - println!( - "cargo:rustc-cfg=qt_version_major=\"{}\"", - qtbuild.version().major - ); - let mut rust_bridges = vec![ "core/qbytearray", "core/qcoreapplication", @@ -173,6 +161,8 @@ fn main() { println!("cargo:rerun-if-changed=src/{bridge}.rs"); } + let qtbuild = qt_build_utils::QtBuild::new(qt_modules).expect("Could not find Qt installation"); + for include_path in qtbuild.include_paths() { cxx_build::CFG .exported_header_dirs @@ -182,6 +172,17 @@ fn main() { let mut builder = cxx_build::bridges(rust_bridges.iter().map(|bridge| format!("src/{bridge}.rs"))); + qtbuild.cargo_link_libraries(&mut builder); + // Required for tests + qt_build_utils::setup_linker(); + + // Find the Qt version and tell the Rust compiler + // this allows us to have conditional Rust code + println!( + "cargo:rustc-cfg=qt_version_major=\"{}\"", + qtbuild.version().major + ); + let mut cpp_files = vec![ "core/qbytearray", "core/qcoreapplication", diff --git a/crates/qt-build-utils/Cargo.toml b/crates/qt-build-utils/Cargo.toml index fd7777086..437ad09c5 100644 --- a/crates/qt-build-utils/Cargo.toml +++ b/crates/qt-build-utils/Cargo.toml @@ -13,5 +13,9 @@ description = "Build script helper for linking Qt libraries and using moc code g repository.workspace = true [dependencies] +cc = "1.0.74" versions = "4.1.0" thiserror = "1.0" + +[features] +include_qt_objects = [] diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index d7bb39004..a16786f8c 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -306,8 +306,43 @@ impl QtBuild { .to_string() } + fn cargo_link_qt_library( + &self, + builder: &mut cc::Build, + name: &str, + prefix_path: &str, + lib_path: &str, + link_lib: &str, + prl_path: &str, + ) { + println!("cargo:rustc-link-lib={link_lib}"); + + match std::fs::read_to_string(&prl_path) { + Ok(prl) => { + for line in prl.lines() { + if let Some(line) = line.strip_prefix("QMAKE_PRL_LIBS = ") { + parse_cflags::parse_libs_cflags( + builder, + name, + line.replace(r"$$[QT_INSTALL_LIBS]", &lib_path) + .replace(r"$$[QT_INSTALL_PREFIX]", &prefix_path) + .as_bytes(), + ); + } + } + } + Err(e) => { + println!( + "cargo:warning=Could not open {} file to read libraries to link: {}", + &prl_path, e + ); + } + } + } + /// Tell Cargo to link each Qt module. - pub fn cargo_link_libraries(&self) { + pub fn cargo_link_libraries(&self, builder: &mut cc::Build) { + let prefix_path = self.qmake_query("QT_INSTALL_PREFIX"); let lib_path = self.qmake_query("QT_INSTALL_LIBS"); println!("cargo:rustc-link-search={lib_path}"); @@ -350,28 +385,14 @@ impl QtBuild { ) }; - println!("cargo:rustc-link-lib={link_lib}"); - - match std::fs::read_to_string(&prl_path) { - Ok(prl) => { - for line in prl.lines() { - if let Some(line) = line.strip_prefix("QMAKE_PRL_LIBS = ") { - parse_cflags::parse_libs_cflags( - &format!("Qt{}{qt_module}", self.version.major), - line.replace(r"$$[QT_INSTALL_LIBS]", &lib_path) - .replace(r"$$[QT_INSTALL_PREFIX]", &lib_path) - .as_bytes(), - ); - } - } - } - Err(e) => { - println!( - "cargo:warning=Could not open {} file to read libraries to link: {}", - &prl_path, e - ); - } - } + self.cargo_link_qt_library( + builder, + &format!("Qt{}{qt_module}", self.version.major), + &prefix_path, + &lib_path, + &link_lib, + &prl_path, + ); } } diff --git a/crates/qt-build-utils/src/parse_cflags.rs b/crates/qt-build-utils/src/parse_cflags.rs index 0346380af..7c838ee82 100644 --- a/crates/qt-build-utils/src/parse_cflags.rs +++ b/crates/qt-build-utils/src/parse_cflags.rs @@ -103,7 +103,7 @@ fn split_flags(link_args: &[u8]) -> Vec { words } -pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8]) { +pub(crate) fn parse_libs_cflags(_builder: &mut cc::Build, name: &str, link_args: &[u8]) { let mut is_msvc = false; let target = env::var("TARGET"); if let Ok(target) = &target { @@ -167,13 +167,18 @@ pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8]) { if let (Some(dir), Some(file_name), Ok(target)) = (path.parent(), path.file_name(), &target) { - match extract_lib_from_filename(target, &file_name.to_string_lossy()) { - Some(lib_basename) => { - println!("cargo:rustc-link-search={}", dir.display()); - println!("cargo:rustc-link-lib={lib_basename}"); - } - None => { - println!("cargo:warning=File path {} found in .prl file for {name}, but could not extract library base name to pass to linker command line", path.display()); + if file_name.to_string_lossy().ends_with(".o") { + #[cfg(feature = "include_qt_objects")] + _builder.object(path); + } else { + match extract_lib_from_filename(target, &file_name.to_string_lossy()) { + Some(lib_basename) => { + println!("cargo:rustc-link-search={}", dir.display()); + println!("cargo:rustc-link-lib={lib_basename}"); + } + None => { + println!("cargo:warning=File path {} found in .prl file for {name}, but could not extract library base name to pass to linker command line", path.display()); + } } } } From 8b4cc701079c68c3d2e9e323f2b618a1735df329 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Sat, 1 Jul 2023 15:12:43 +0200 Subject: [PATCH 02/11] Some prl files do not follow the assumed pattern. Instead look for the files by iterating over the directory entries. --- crates/qt-build-utils/src/lib.rs | 35 ++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index a16786f8c..557c14613 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -340,6 +340,36 @@ impl QtBuild { } } + /// Some prl files don't follow a consistent naming scheme. Try to find it by looking at all files in lib_path. + fn find_qt_module_prl(&self, lib_path: &str, prefix: &str, version_major: u32, qt_module: &str) -> String { + match Path::new(lib_path).read_dir() { + Ok(lib_dir) => { + for entry in lib_dir { + match entry { + Ok(entry) => { + let file_name = entry.file_name(); + let file_name = file_name.to_string_lossy(); + let prl_file = file_name.ends_with(".prl"); + let matches_module = file_name + .contains(&format!("Qt{}{}", self.version.major, qt_module)); + if prl_file && matches_module { + return entry.path().display().to_string(); + } + } + Err(e) => { + println!("cargo:warning=Could not read {} entry: {}", lib_path, e); + } + } + } + } + Err(e) => { + println!("cargo:warning=Could not read dir {}: {}", lib_path, e); + } + } + + format!("{}/{}Qt{}{}.prl",lib_path, prefix, version_major, qt_module) + } + /// Tell Cargo to link each Qt module. pub fn cargo_link_libraries(&self, builder: &mut cc::Build) { let prefix_path = self.qmake_query("QT_INSTALL_PREFIX"); @@ -378,10 +408,7 @@ impl QtBuild { } else { ( format!("Qt{}{qt_module}", self.version.major), - format!( - "{}/{}Qt{}{}.prl", - lib_path, prefix, self.version.major, qt_module - ), + self.find_qt_module_prl(&lib_path, prefix, self.version.major, qt_module), ) }; From 210e6e2d21828854f3bc83b051537e16bc641252 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Sat, 1 Jul 2023 15:14:22 +0200 Subject: [PATCH 03/11] The libqwasm.prl file must be parsed for the emscripten target. --- crates/qt-build-utils/src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index 557c14613..b0690ca4a 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -421,6 +421,23 @@ impl QtBuild { &prl_path, ); } + + let emscripten_targeted = match env::var("CARGO_CFG_TARGET_OS") { + Ok(val) => val == "emscripten", + Err(_) => false, + }; + if emscripten_targeted { + let platforms_path = format!("{}/platforms", self.qmake_query("QT_INSTALL_PLUGINS")); + println!("cargo:rustc-link-search={platforms_path}"); + self.cargo_link_qt_library( + builder, + "qwasm", + &prefix_path, + &lib_path, + "qwasm", + &format!("{platforms_path}/libqwasm.prl"), + ); + } } /// Get the include paths for Qt, including Qt module subdirectories. This is intended From 669d080fe84a7bdbde1180e9ea27483ebef7a2d2 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Tue, 4 Jul 2023 08:23:22 +0200 Subject: [PATCH 04/11] Applied cargo fmt and clippy. --- crates/qt-build-utils/src/lib.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index b0690ca4a..35fb5ac51 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -317,15 +317,15 @@ impl QtBuild { ) { println!("cargo:rustc-link-lib={link_lib}"); - match std::fs::read_to_string(&prl_path) { + match std::fs::read_to_string(prl_path) { Ok(prl) => { for line in prl.lines() { if let Some(line) = line.strip_prefix("QMAKE_PRL_LIBS = ") { parse_cflags::parse_libs_cflags( builder, name, - line.replace(r"$$[QT_INSTALL_LIBS]", &lib_path) - .replace(r"$$[QT_INSTALL_PREFIX]", &prefix_path) + line.replace(r"$$[QT_INSTALL_LIBS]", lib_path) + .replace(r"$$[QT_INSTALL_PREFIX]", prefix_path) .as_bytes(), ); } @@ -341,7 +341,13 @@ impl QtBuild { } /// Some prl files don't follow a consistent naming scheme. Try to find it by looking at all files in lib_path. - fn find_qt_module_prl(&self, lib_path: &str, prefix: &str, version_major: u32, qt_module: &str) -> String { + fn find_qt_module_prl( + &self, + lib_path: &str, + prefix: &str, + version_major: u32, + qt_module: &str, + ) -> String { match Path::new(lib_path).read_dir() { Ok(lib_dir) => { for entry in lib_dir { @@ -367,7 +373,10 @@ impl QtBuild { } } - format!("{}/{}Qt{}{}.prl",lib_path, prefix, version_major, qt_module) + format!( + "{}/{}Qt{}{}.prl", + lib_path, prefix, version_major, qt_module + ) } /// Tell Cargo to link each Qt module. From de469c0b2f884be23f893babdf752644ef9a7a7d Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Wed, 5 Jul 2023 09:41:16 +0200 Subject: [PATCH 05/11] Applied requested builder changes --- crates/qt-build-utils/src/lib.rs | 8 ++++---- crates/qt-build-utils/src/parse_cflags.rs | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index 35fb5ac51..e95bfcac9 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -308,12 +308,12 @@ impl QtBuild { fn cargo_link_qt_library( &self, - builder: &mut cc::Build, name: &str, prefix_path: &str, lib_path: &str, link_lib: &str, prl_path: &str, + builder: &mut cc::Build, ) { println!("cargo:rustc-link-lib={link_lib}"); @@ -322,11 +322,11 @@ impl QtBuild { for line in prl.lines() { if let Some(line) = line.strip_prefix("QMAKE_PRL_LIBS = ") { parse_cflags::parse_libs_cflags( - builder, name, line.replace(r"$$[QT_INSTALL_LIBS]", lib_path) .replace(r"$$[QT_INSTALL_PREFIX]", prefix_path) .as_bytes(), + builder, ); } } @@ -422,12 +422,12 @@ impl QtBuild { }; self.cargo_link_qt_library( - builder, &format!("Qt{}{qt_module}", self.version.major), &prefix_path, &lib_path, &link_lib, &prl_path, + builder, ); } @@ -439,12 +439,12 @@ impl QtBuild { let platforms_path = format!("{}/platforms", self.qmake_query("QT_INSTALL_PLUGINS")); println!("cargo:rustc-link-search={platforms_path}"); self.cargo_link_qt_library( - builder, "qwasm", &prefix_path, &lib_path, "qwasm", &format!("{platforms_path}/libqwasm.prl"), + builder, ); } } diff --git a/crates/qt-build-utils/src/parse_cflags.rs b/crates/qt-build-utils/src/parse_cflags.rs index 7c838ee82..4ef38fb36 100644 --- a/crates/qt-build-utils/src/parse_cflags.rs +++ b/crates/qt-build-utils/src/parse_cflags.rs @@ -103,7 +103,7 @@ fn split_flags(link_args: &[u8]) -> Vec { words } -pub(crate) fn parse_libs_cflags(_builder: &mut cc::Build, name: &str, link_args: &[u8]) { +pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8], _builder: &mut cc::Build) { let mut is_msvc = false; let target = env::var("TARGET"); if let Ok(target) = &target { @@ -168,6 +168,12 @@ pub(crate) fn parse_libs_cflags(_builder: &mut cc::Build, name: &str, link_args: (path.parent(), path.file_name(), &target) { if file_name.to_string_lossy().ends_with(".o") { + // Cargo doesn't have a means to directly specify an object to link, + // so use the cc crate to specify it instead. + // TODO: pass file path directly when link-arg library type is stabilized + // https://github.com/rust-lang/rust/issues/99427#issuecomment-1562092085 + // TODO: remove builder argument when it's not used anymore to link object files. + // also remove the dependency on cc when this is done #[cfg(feature = "include_qt_objects")] _builder.object(path); } else { From cebe9711c434f336d15cc98512d79cccfeeb47f4 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Wed, 5 Jul 2023 09:41:57 +0200 Subject: [PATCH 06/11] Expanded documentation about fnd_qt_module --- crates/qt-build-utils/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index e95bfcac9..e7c080b52 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -341,6 +341,8 @@ impl QtBuild { } /// Some prl files don't follow a consistent naming scheme. Try to find it by looking at all files in lib_path. + /// The android libraries adds its architecture at the end of it prl files eg liQt6Core_{arch}.prl. + /// The arch placeholder is somewhat different for each architecture preventing the use of a simple format. fn find_qt_module_prl( &self, lib_path: &str, From 599afec244d4c7c93d0bd07b87b131886df69ad8 Mon Sep 17 00:00:00 2001 From: Be Wilson Date: Wed, 5 Jul 2023 16:29:30 -0500 Subject: [PATCH 07/11] deduplicate linked .o files --- crates/cxx-qt-build/Cargo.toml | 1 - crates/qt-build-utils/src/parse_cflags.rs | 33 ++++++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/crates/cxx-qt-build/Cargo.toml b/crates/cxx-qt-build/Cargo.toml index 0f0cfc672..7849a40c3 100644 --- a/crates/cxx-qt-build/Cargo.toml +++ b/crates/cxx-qt-build/Cargo.toml @@ -27,4 +27,3 @@ codespan-reporting = "0.11" default = ["qt_gui", "qt_qml"] qt_gui = ["cxx-qt-lib-headers/qt_gui"] qt_qml = ["cxx-qt-lib-headers/qt_qml"] -include_qt_objects = ["qt-build-utils/include_qt_objects"] diff --git a/crates/qt-build-utils/src/parse_cflags.rs b/crates/qt-build-utils/src/parse_cflags.rs index 4ef38fb36..0a5fbbcd6 100644 --- a/crates/qt-build-utils/src/parse_cflags.rs +++ b/crates/qt-build-utils/src/parse_cflags.rs @@ -8,7 +8,9 @@ //! It has been decoupled from the pkg-config crate because qt-build-utils reads Qt's .prl files instead, which //! does not require a pkg-config executable to be available. -use std::env; +use std::{collections::HashSet, env, sync::OnceLock}; + +static mut LINKED_OBJECT_FILES: OnceLock> = OnceLock::new(); /// Extract the &str to pass to cargo:rustc-link-lib from a filename (just the file name, not including directories) /// using target-specific logic. @@ -103,7 +105,7 @@ fn split_flags(link_args: &[u8]) -> Vec { words } -pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8], _builder: &mut cc::Build) { +pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8], builder: &mut cc::Build) { let mut is_msvc = false; let target = env::var("TARGET"); if let Ok(target) = &target { @@ -162,20 +164,27 @@ pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8], _builder: &mut cc: if path.is_file() { // Cargo doesn't have a means to directly specify a file path to link, // so split up the path into the parent directory and library name. - // TODO: pass file path directly when link-arg library type is stabilized - // https://github.com/rust-lang/rust/issues/99427 if let (Some(dir), Some(file_name), Ok(target)) = (path.parent(), path.file_name(), &target) { if file_name.to_string_lossy().ends_with(".o") { - // Cargo doesn't have a means to directly specify an object to link, - // so use the cc crate to specify it instead. - // TODO: pass file path directly when link-arg library type is stabilized - // https://github.com/rust-lang/rust/issues/99427#issuecomment-1562092085 - // TODO: remove builder argument when it's not used anymore to link object files. - // also remove the dependency on cc when this is done - #[cfg(feature = "include_qt_objects")] - _builder.object(path); + let path_string = path.to_string_lossy().to_string(); + unsafe { + // Linking will fail with duplicate symbol errors if the same .o file is linked twice. + // Many of Qt's .prl files repeat listing .o files that other .prl files also list. + let already_linked_object_files = + LINKED_OBJECT_FILES.get_or_init(|| HashSet::new()); + if !already_linked_object_files.contains(&path_string) { + // Cargo doesn't have a means to directly specify an object to link, + // so use the cc crate to specify it instead. + // TODO: pass file path directly when link-arg library type is stabilized + // https://github.com/rust-lang/rust/issues/99427#issuecomment-1562092085 + // TODO: remove builder argument when it's not used anymore to link object files. + // also remove the dependency on cc when this is done + builder.object(path); + } + LINKED_OBJECT_FILES.get_mut().unwrap().insert(path_string); + } } else { match extract_lib_from_filename(target, &file_name.to_string_lossy()) { Some(lib_basename) => { From 459b3a4d2d8ad2ab5c730be290feb10f0004d119 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Sat, 8 Jul 2023 21:10:09 +0200 Subject: [PATCH 08/11] cargo_link_libraries is invoked by two crates cxx-qt and cxx-qt-build. when static linking only one of them is allowed to to add object files else duplicate symbol errors will be produced. --- crates/cxx-qt-build/src/lib.rs | 2 +- crates/cxx-qt-lib/build.rs | 2 +- crates/qt-build-utils/Cargo.toml | 3 -- crates/qt-build-utils/src/lib.rs | 8 ++--- crates/qt-build-utils/src/parse_cflags.rs | 38 +++++++++++++---------- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index cceea8a1d..9b6f28e0b 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -400,7 +400,7 @@ impl CxxQtBuilder { let mut qtbuild = qt_build_utils::QtBuild::new(self.qt_modules.into_iter().collect()) .expect("Could not find Qt installation"); - qtbuild.cargo_link_libraries(&mut self.cc_builder); + qtbuild.cargo_link_libraries(Some(&mut self.cc_builder)); // Write cxx-qt-lib and cxx headers cxx_qt_lib_headers::write_headers(format!("{header_root}/cxx-qt-lib")); diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index c4dce8563..d7cedb11d 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -172,7 +172,7 @@ fn main() { let mut builder = cxx_build::bridges(rust_bridges.iter().map(|bridge| format!("src/{bridge}.rs"))); - qtbuild.cargo_link_libraries(&mut builder); + qtbuild.cargo_link_libraries(None); // Required for tests qt_build_utils::setup_linker(); diff --git a/crates/qt-build-utils/Cargo.toml b/crates/qt-build-utils/Cargo.toml index 437ad09c5..6c6a830d8 100644 --- a/crates/qt-build-utils/Cargo.toml +++ b/crates/qt-build-utils/Cargo.toml @@ -16,6 +16,3 @@ repository.workspace = true cc = "1.0.74" versions = "4.1.0" thiserror = "1.0" - -[features] -include_qt_objects = [] diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index e7c080b52..136e80784 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -313,7 +313,7 @@ impl QtBuild { lib_path: &str, link_lib: &str, prl_path: &str, - builder: &mut cc::Build, + builder: &mut Option<&mut cc::Build>, ) { println!("cargo:rustc-link-lib={link_lib}"); @@ -382,7 +382,7 @@ impl QtBuild { } /// Tell Cargo to link each Qt module. - pub fn cargo_link_libraries(&self, builder: &mut cc::Build) { + pub fn cargo_link_libraries(&self, mut builder: Option<&mut cc::Build>) { let prefix_path = self.qmake_query("QT_INSTALL_PREFIX"); let lib_path = self.qmake_query("QT_INSTALL_LIBS"); println!("cargo:rustc-link-search={lib_path}"); @@ -429,7 +429,7 @@ impl QtBuild { &lib_path, &link_lib, &prl_path, - builder, + &mut builder, ); } @@ -446,7 +446,7 @@ impl QtBuild { &lib_path, "qwasm", &format!("{platforms_path}/libqwasm.prl"), - builder, + &mut builder, ); } } diff --git a/crates/qt-build-utils/src/parse_cflags.rs b/crates/qt-build-utils/src/parse_cflags.rs index 0a5fbbcd6..00ff62596 100644 --- a/crates/qt-build-utils/src/parse_cflags.rs +++ b/crates/qt-build-utils/src/parse_cflags.rs @@ -105,7 +105,11 @@ fn split_flags(link_args: &[u8]) -> Vec { words } -pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8], builder: &mut cc::Build) { +pub(crate) fn parse_libs_cflags( + name: &str, + link_args: &[u8], + builder: &mut Option<&mut cc::Build>, +) { let mut is_msvc = false; let target = env::var("TARGET"); if let Ok(target) = &target { @@ -168,22 +172,24 @@ pub(crate) fn parse_libs_cflags(name: &str, link_args: &[u8], builder: &mut cc:: (path.parent(), path.file_name(), &target) { if file_name.to_string_lossy().ends_with(".o") { - let path_string = path.to_string_lossy().to_string(); - unsafe { - // Linking will fail with duplicate symbol errors if the same .o file is linked twice. - // Many of Qt's .prl files repeat listing .o files that other .prl files also list. - let already_linked_object_files = - LINKED_OBJECT_FILES.get_or_init(|| HashSet::new()); - if !already_linked_object_files.contains(&path_string) { - // Cargo doesn't have a means to directly specify an object to link, - // so use the cc crate to specify it instead. - // TODO: pass file path directly when link-arg library type is stabilized - // https://github.com/rust-lang/rust/issues/99427#issuecomment-1562092085 - // TODO: remove builder argument when it's not used anymore to link object files. - // also remove the dependency on cc when this is done - builder.object(path); + if let Some(builder) = builder { + let path_string = path.to_string_lossy().to_string(); + unsafe { + // Linking will fail with duplicate symbol errors if the same .o file is linked twice. + // Many of Qt's .prl files repeat listing .o files that other .prl files also list. + let already_linked_object_files = + LINKED_OBJECT_FILES.get_or_init(HashSet::new); + if !already_linked_object_files.contains(&path_string) { + // Cargo doesn't have a means to directly specify an object to link, + // so use the cc crate to specify it instead. + // TODO: pass file path directly when link-arg library type is stabilized + // https://github.com/rust-lang/rust/issues/99427#issuecomment-1562092085 + // TODO: remove builder argument when it's not used anymore to link object files. + // also remove the dependency on cc when this is done + builder.object(path); + } + LINKED_OBJECT_FILES.get_mut().unwrap().insert(path_string); } - LINKED_OBJECT_FILES.get_mut().unwrap().insert(path_string); } } else { match extract_lib_from_filename(target, &file_name.to_string_lossy()) { From 85db1e5779f925b1679e51c4ea8d7b3bd05d46f6 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Sat, 8 Jul 2023 21:10:55 +0200 Subject: [PATCH 09/11] some external build systems will include object files found by cargo_link_libraries and are hard to remove. instead instruct this function to not include these object files. --- crates/cxx-qt-build/Cargo.toml | 1 + crates/cxx-qt-build/src/lib.rs | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/cxx-qt-build/Cargo.toml b/crates/cxx-qt-build/Cargo.toml index 7849a40c3..0ea8365a8 100644 --- a/crates/cxx-qt-build/Cargo.toml +++ b/crates/cxx-qt-build/Cargo.toml @@ -27,3 +27,4 @@ codespan-reporting = "0.11" default = ["qt_gui", "qt_qml"] qt_gui = ["cxx-qt-lib-headers/qt_gui"] qt_qml = ["cxx-qt-lib-headers/qt_qml"] +link_qt_external = [] diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index 9b6f28e0b..6a9d68d21 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -400,7 +400,13 @@ impl CxxQtBuilder { let mut qtbuild = qt_build_utils::QtBuild::new(self.qt_modules.into_iter().collect()) .expect("Could not find Qt installation"); - qtbuild.cargo_link_libraries(Some(&mut self.cc_builder)); + // some external build systems will include object files found by cargo_link_libraries and + // are hard to remove. instead instruct this function to not include these object files. + if cfg!(feature = "link_qt_external") { + qtbuild.cargo_link_libraries(None); + } else { + qtbuild.cargo_link_libraries(Some(&mut self.cc_builder)); + } // Write cxx-qt-lib and cxx headers cxx_qt_lib_headers::write_headers(format!("{header_root}/cxx-qt-lib")); From 0d8047d66aa41120deaddcc01e5b994fc3be265c Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Sat, 8 Jul 2023 21:37:18 +0200 Subject: [PATCH 10/11] Fixed some assumptions with prl file searching by hardcoding android architectures. --- crates/qt-build-utils/src/lib.rs | 40 ++++++++++++++------------------ 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index 136e80784..52dc929a9 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -340,9 +340,8 @@ impl QtBuild { } } - /// Some prl files don't follow a consistent naming scheme. Try to find it by looking at all files in lib_path. - /// The android libraries adds its architecture at the end of it prl files eg liQt6Core_{arch}.prl. - /// The arch placeholder is somewhat different for each architecture preventing the use of a simple format. + /// Some prl files include their architecture in their naming scheme. + /// Just try all known architectures and fallback to non when they all failed. fn find_qt_module_prl( &self, lib_path: &str, @@ -350,28 +349,23 @@ impl QtBuild { version_major: u32, qt_module: &str, ) -> String { - match Path::new(lib_path).read_dir() { - Ok(lib_dir) => { - for entry in lib_dir { - match entry { - Ok(entry) => { - let file_name = entry.file_name(); - let file_name = file_name.to_string_lossy(); - let prl_file = file_name.ends_with(".prl"); - let matches_module = file_name - .contains(&format!("Qt{}{}", self.version.major, qt_module)); - if prl_file && matches_module { - return entry.path().display().to_string(); - } - } - Err(e) => { - println!("cargo:warning=Could not read {} entry: {}", lib_path, e); - } + for arch in ["", "_arm64-v8a", "_armeabi-v7a", "_x86", "_x86_64"] { + let prl_path = format!( + "{}/{}Qt{}{}{}.prl", + lib_path, prefix, version_major, qt_module, arch + ); + match Path::new(&prl_path).try_exists() { + Ok(exists) => { + if exists { + return prl_path; } } - } - Err(e) => { - println!("cargo:warning=Could not read dir {}: {}", lib_path, e); + Err(e) => { + println!( + "cargo:warning=failed checking for existence of {}: {}", + prl_path, e + ); + } } } From 80962a71d4cdc95324f60041d69a558350990dc3 Mon Sep 17 00:00:00 2001 From: Jimmy van Hest Date: Sat, 8 Jul 2023 21:57:36 +0200 Subject: [PATCH 11/11] Reverted unnecessary changes. --- crates/cxx-qt-lib/build.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index d7cedb11d..190f30810 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -15,6 +15,18 @@ fn main() { qt_modules.push("Qml".to_owned()); } + let qtbuild = qt_build_utils::QtBuild::new(qt_modules).expect("Could not find Qt installation"); + qtbuild.cargo_link_libraries(None); + // Required for tests + qt_build_utils::setup_linker(); + + // Find the Qt version and tell the Rust compiler + // this allows us to have conditional Rust code + println!( + "cargo:rustc-cfg=qt_version_major=\"{}\"", + qtbuild.version().major + ); + let mut rust_bridges = vec![ "core/qbytearray", "core/qcoreapplication", @@ -161,8 +173,6 @@ fn main() { println!("cargo:rerun-if-changed=src/{bridge}.rs"); } - let qtbuild = qt_build_utils::QtBuild::new(qt_modules).expect("Could not find Qt installation"); - for include_path in qtbuild.include_paths() { cxx_build::CFG .exported_header_dirs @@ -172,17 +182,6 @@ fn main() { let mut builder = cxx_build::bridges(rust_bridges.iter().map(|bridge| format!("src/{bridge}.rs"))); - qtbuild.cargo_link_libraries(None); - // Required for tests - qt_build_utils::setup_linker(); - - // Find the Qt version and tell the Rust compiler - // this allows us to have conditional Rust code - println!( - "cargo:rustc-cfg=qt_version_major=\"{}\"", - qtbuild.version().major - ); - let mut cpp_files = vec![ "core/qbytearray", "core/qcoreapplication",