diff --git a/.github/workflows/github-cxx-qt-tests.yml b/.github/workflows/github-cxx-qt-tests.yml index 832e520b0..4fe580c99 100644 --- a/.github/workflows/github-cxx-qt-tests.yml +++ b/.github/workflows/github-cxx-qt-tests.yml @@ -130,9 +130,22 @@ jobs: if-no-files-found: ignore build-qt-minimal: - name: Ubuntu 24.04 (gcc) Qt 6 Minimal needs: [clang_format, license_check, rust_format_check, markdown_lint, shellcheck] - runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + include: + - name: macOS 15 (clang) Qt 6 Minimal + runs-on: macos-15 + clang_format_path: /Users/runner/Library/Python/3.14/bin/clang-format + - name: Ubuntu 24.04 (gcc) Qt 6 Minimal + runs-on: ubuntu-24.04 + clang_format_path: /home/runner/.local/bin/clang-format + - name: Windows 2022 (MSVC) Qt 6 Minimal + runs-on: windows-2022 + clang_format_path: C:\Users\runneradmin\AppData\Roaming\Python\Python312\Scripts\clang-format.exe + runs-on: ${{ matrix.runs-on }} + name: ${{ matrix.name }} steps: - name: "Checkout repository" uses: actions/checkout@v6 @@ -153,14 +166,12 @@ jobs: run: >- sudo apt-get update && sudo apt-get install -y - ninja-build libdouble-conversion3 libgl1-mesa-dev libssl-dev libvulkan-dev libxkbcommon-dev pkg-config - valgrind # Just test that we can build with no Qt install available # @@ -168,6 +179,7 @@ jobs: - name: "Build" run: cargo build --package qml-minimal-no-cmake - name: Test output files exist + if: runner.os == 'Linux' run: | ! command -v qmake test -x ./target/debug/qml-minimal-no-cmake diff --git a/Cargo.lock b/Cargo.lock index d02f1e5ab..3a7be87d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -250,9 +250,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytemuck" @@ -828,6 +828,7 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -1515,9 +1516,9 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "log" -version = "0.4.25" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" dependencies = [ "value-bag", ] @@ -1746,9 +1747,9 @@ dependencies = [ [[package]] name = "qt-artifacts" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176a99b61f396903126c16ebc03c653561cd6ee6951e1f3339d9170adfabff85" +checksum = "1ea43e816e97b5528fa5e355873f420a516770ca9bd561f6249dfc9b445c08d0" [[package]] name = "qt-build-utils" @@ -1769,6 +1770,7 @@ dependencies = [ "tempfile", "thiserror 1.0.69", "which", + "zip", ] [[package]] @@ -2575,6 +2577,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + [[package]] name = "typenum" version = "1.19.0" @@ -2645,9 +2653,9 @@ dependencies = [ [[package]] name = "value-bag" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" [[package]] name = "version_check" @@ -3342,8 +3350,40 @@ dependencies = [ "syn", ] +[[package]] +name = "zip" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42e33efc22a0650c311c2ef19115ce232583abbe80850bc8b66509ebef02de0" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/crates/qt-build-utils/Cargo.toml b/crates/qt-build-utils/Cargo.toml index 8299af754..13cc1fcf1 100644 --- a/crates/qt-build-utils/Cargo.toml +++ b/crates/qt-build-utils/Cargo.toml @@ -25,9 +25,11 @@ sha2 = { version = "0.10.9", optional = true } tempfile = { version = "3.25.0", optional = true } tar = { version = "0.4.44", optional = true } flate2 = { version = "1.1.9", optional = true } -qt-artifacts = { version = "0.1.2", optional = true } +qt-artifacts = { version = "0.1.3", optional = true } qt-version = { version = "0.1.4", optional = true } which = { version = "8.0.0", optional = true } +# NOTE: version 8.x has a MSRV of 1.88 and CXX-Qt is currently 1.85 +zip = { version = "7.2.0", optional = true, default-features = false, features = ["deflate"] } [features] # TODO: should we default to qmake or let downstream crates specify, such as cxx-qt-build @@ -44,7 +46,7 @@ default = ["qmake"] # When linking Qt dynamically, this makes no difference. link_qt_object_files = [] qmake = ["dep:which"] -qt_minimal = ["qt_version", "dep:serde", "serde/derive", "semver/serde", "dep:reqwest", "dep:sha2", "dep:tempfile", "dep:serde_json", "dep:flate2", "dep:tar", "dep:qt-artifacts", "dep:dirs"] +qt_minimal = ["qt_version", "dep:serde", "serde/derive", "semver/serde", "dep:reqwest", "dep:sha2", "dep:tempfile", "dep:serde_json", "dep:flate2", "dep:tar", "dep:qt-artifacts", "dep:dirs", "dep:zip"] qt_version = ["dep:qt-version"] serde = ["dep:serde"] diff --git a/crates/qt-build-utils/src/installation/qt_minimal/artifact.rs b/crates/qt-build-utils/src/installation/qt_minimal/artifact.rs index 9cf20f0a4..8d9e25b78 100644 --- a/crates/qt-build-utils/src/installation/qt_minimal/artifact.rs +++ b/crates/qt-build-utils/src/installation/qt_minimal/artifact.rs @@ -43,7 +43,14 @@ impl ParsedQtArtifact { .expect("Could not verify sha256 hash"); // Extract into the target folder - super::extract::extract_archive(&archive_path, target_path) + let archive_format = if self.url.ends_with(".tar.gz") { + super::extract::ArchiveFormat::TarGz + } else if self.url.ends_with(".zip") { + super::extract::ArchiveFormat::Zip + } else { + panic!("Unknown archive format to decompress: {}", self.url); + }; + super::extract::extract_archive(&archive_path, target_path, archive_format) .expect("Could not extract archive into target"); target_path.to_path_buf() @@ -77,7 +84,12 @@ impl ParsedQtArtifact { } if self.sha256 != hash_string { - return Err(anyhow::anyhow!("sha256 does not match for: {}", &self.url)); + return Err(anyhow::anyhow!( + "sha256 does not match for: {}, expected: {}, actual: {}", + &self.url, + self.sha256, + hash_string + )); } Ok(()) diff --git a/crates/qt-build-utils/src/installation/qt_minimal/extract.rs b/crates/qt-build-utils/src/installation/qt_minimal/extract.rs index 0b5e1be55..0ea93f73e 100644 --- a/crates/qt-build-utils/src/installation/qt_minimal/extract.rs +++ b/crates/qt-build-utils/src/installation/qt_minimal/extract.rs @@ -6,15 +6,36 @@ use flate2::read::GzDecoder; use std::{fs::File, path::Path}; use tar::Archive; +use zip::ZipArchive; + +pub(crate) enum ArchiveFormat { + TarGz, + Zip, +} /// Extract archive to same directory as this workspace, not same as path -pub(crate) fn extract_archive(archive_path: &Path, target_path: &Path) -> anyhow::Result<()> { - let tar_gz = File::open(archive_path)?; - let tar = GzDecoder::new(tar_gz); - let mut archive = Archive::new(tar); +pub(crate) fn extract_archive( + archive_path: &Path, + target_path: &Path, + archive_format: ArchiveFormat, +) -> anyhow::Result<()> { + let file = File::open(archive_path)?; + + match archive_format { + ArchiveFormat::TarGz => { + let gz_decoder = GzDecoder::new(file); + let mut archive = Archive::new(gz_decoder); + + // Modify destination in unpack here + archive.unpack(target_path)?; + } + ArchiveFormat::Zip => { + let mut archive = ZipArchive::new(file)?; - // Modify destination in unpack here - archive.unpack(target_path)?; + // Modify destination in unpack here + archive.extract(target_path)?; + } + } Ok(()) } diff --git a/crates/qt-build-utils/src/installation/qt_minimal/mod.rs b/crates/qt-build-utils/src/installation/qt_minimal/mod.rs index 6a6bb3b0e..f765de35f 100644 --- a/crates/qt-build-utils/src/installation/qt_minimal/mod.rs +++ b/crates/qt-build-utils/src/installation/qt_minimal/mod.rs @@ -28,7 +28,9 @@ impl TryFrom for QtInstallationQtMinimal { println!("cargo::rerun-if-changed={}", path_qt.display()); // Verify that the expected folders exist - for folder in ["bin", "include", "lib", "libexec"] { + // + // NOTE: libexec does not exist on Windows, so is not mandatory + for folder in ["bin", "include", "lib"] { if !path_qt.join(folder).exists() { return Err(anyhow::anyhow!( "Failed to find {folder} in Qt path: {}", @@ -45,7 +47,19 @@ impl TryFrom for QtInstallationQtMinimal { .join(folder) .join(crate::QtTool::QtPaths.binary_name()) }) - .find(|path| path.exists()) + .find(|path| { + if path.exists() { + return true; + } + + // NOTE: try with .exe for Windows + let path_exe = path.with_extension("exe"); + if path_exe.exists() { + return true; + } + + false + }) else { return Err(anyhow::anyhow!( "Failed to find qtpaths in Qt path: {}", @@ -159,6 +173,12 @@ impl QtInstallation for QtInstallationQtMinimal { if path.exists() { return Ok(path); } + + // NOTE: try with .exe for Windows + let path_exe = path.with_extension("exe"); + if path_exe.exists() { + return Ok(path_exe); + } } Err(anyhow::anyhow!(