From b03a78c05d89d67239909296f3d711d51d1063ed Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sat, 26 Sep 2026 16:36:33 +0800 Subject: [PATCH 1/7] 0.14.0: deps-archive places an archive's tree beside the program; deps-vcpkg and deps-cmake deploy named files of their prefix --- .github/scripts/check-deps-and-qt.sh | 58 ++++++- .github/workflows/ci.yml | 6 + README.md | 58 ++++++- deps/archive.cppm | 210 +++++++++++++++++++++++ deps/cmake.cppm | 7 + deps/deps.cppm | 61 +++++++ deps/vcpkg.cppm | 7 + mcpp.toml | 16 +- src/plugins.cppm | 2 +- tests/all-rules-compile/build.mcpp | 2 + tests/all-rules-compile/mcpp.toml | 2 +- tests/archive-consumer/assets/bundle.zip | Bin 0 -> 685 bytes tests/archive-consumer/build.mcpp | 11 ++ tests/archive-consumer/mcpp.toml | 27 +++ tests/archive-consumer/src/main.cpp | 19 ++ tests/vcpkg-consumer/build.mcpp | 2 + tools/deps_main.cpp | 39 ++++- 17 files changed, 512 insertions(+), 15 deletions(-) create mode 100644 deps/archive.cppm create mode 100644 tests/archive-consumer/assets/bundle.zip create mode 100644 tests/archive-consumer/build.mcpp create mode 100644 tests/archive-consumer/mcpp.toml create mode 100644 tests/archive-consumer/src/main.cpp diff --git a/.github/scripts/check-deps-and-qt.sh b/.github/scripts/check-deps-and-qt.sh index 30d64fe..546da9c 100644 --- a/.github/scripts/check-deps-and-qt.sh +++ b/.github/scripts/check-deps-and-qt.sh @@ -65,6 +65,10 @@ vcpkg_consumer() { "$MCPP" run | tee target/ci/run.log grep -qE '^vcpkg-consumer: fmt [0-9]+ says 42$' target/ci/run.log || fail "the program did not print through fmt" assert_not_rerun "$(stamp_of deps-vcpkg)" + # A file of the prefix the build program named in options::deploy. + find target -path '*/bin/licenses/fmt/copyright' | grep -q . || + fail "share/fmt/copyright was not deployed beside the program" + echo "ok: a file of the prefix is deployed beside the program" if is_windows; then # The pack collects it from the runtime search directory. @@ -74,6 +78,7 @@ vcpkg_consumer() { fail "started from the build directory, the program did not find fmt.dll" "$MCPP" pack --format dir | tee target/ci/pack.log find target/dist -iname 'fmt.dll' | grep -q . || fail "the packed tree carries no fmt.dll" + find target/dist -path '*/licenses/fmt/copyright' | grep -q . || fail "the packed tree carries no deployed copyright" echo "ok: the packed tree carries fmt.dll" fi } @@ -128,6 +133,56 @@ cmake_consumer() { echo "ok: a file added to the subproject re-ran its installation" } +archive_consumer() { + cd "$ROOT/tests/archive-consumer" + rm -rf target + mkdir -p target/ci + # PLANNING EXTRACTS NOTHING. The listing is read from the archive's central + # directory; the extraction is an action. + "$MCPP" emit build-database --format json > target/ci/db.json 2> target/ci/emit.log || + { cat target/ci/emit.log; fail "emit build-database failed before any extraction"; } + [ -z "$(find target -path '*deps-archive*' -type f)" ] || fail "emit build-database extracted something" + echo "ok: emit planned the archive and extracted nothing" + + "$MCPP" build 2>&1 | tee target/ci/build.log + "$MCPP" run | tee target/ci/run.log + grep -q "^archive-consumer: 'greetings from the archive', 'a file two levels down'$" target/ci/run.log || + fail "the program did not read the archive's files from beside itself" + # The C++ file among the data `#error`s if compiled; the build succeeded, + # and the file is beside the program as data. + find target -path '*/bin/runtime/bundle/not-compiled.cpp' | grep -q . || + fail "an archive member was not deployed beside the program" + echo "ok: the archive's tree is beside the program, a C++ member included, as data" + + local copy + copy=$(find target -path '*deps-archive/bundle/bundle/greeting.txt' | head -1) + [ -n "$copy" ] || fail "no extracted copy under target/" + touch -r "$copy" target/ci/before-second-build + sleep 1 + "$MCPP" build --profile dev > target/ci/second-build.log 2>&1 || + { cat target/ci/second-build.log; fail "the second build failed"; } + [ -z "$(find "$copy" -newer target/ci/before-second-build)" ] || + fail "a second build with nothing changed extracted the archive again" + echo "ok: a second build with nothing changed did not extract the archive again" + + touch assets/bundle.zip + "$MCPP" build --profile dev > target/ci/third-build.log 2>&1 || + { cat target/ci/third-build.log; fail "the build after touching the archive failed"; } + [ -n "$(find "$copy" -newer target/ci/before-second-build)" ] || + fail "a changed archive was not extracted again" + echo "ok: a changed archive was extracted again" + + "$MCPP" pack --format dir > target/ci/pack.log 2>&1 || { cat target/ci/pack.log; fail "mcpp pack failed"; } + find target/dist -path '*/runtime/bundle/nested/deep.txt' | grep -q . || + fail "the packed tree does not carry the archive's files" + local packed + packed=$(find target/dist -path '*/bin/archive-consumer*' -type f | grep -vE '\.(pdb|debug)$' | head -1) + [ -n "$packed" ] || fail "no packed program" + "$packed" | grep -q "^archive-consumer: 'greetings from the archive'" || + fail "the packed program did not read the archive's files" + echo "ok: the packed tree carries the archive's files, and the packed program reads them" +} + qt_consumer() { cd "$ROOT/tests/qt-consumer" rm -rf target @@ -179,9 +234,10 @@ qt_widgets_consumer() { case "${1:-}" in vcpkg-consumer) vcpkg_consumer ;; + archive-consumer) archive_consumer ;; vcpkg-workspace) vcpkg_workspace ;; cmake-consumer) cmake_consumer ;; qt-consumer) qt_consumer ;; qt-widgets-consumer) qt_widgets_consumer ;; - *) echo "usage: $0 vcpkg-consumer|vcpkg-workspace|cmake-consumer|qt-consumer|qt-widgets-consumer"; exit 2 ;; + *) echo "usage: $0 vcpkg-consumer|archive-consumer|vcpkg-workspace|cmake-consumer|qt-consumer|qt-widgets-consumer"; exit 2 ;; esac diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c51511c..2618265 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1650,6 +1650,9 @@ jobs: - name: deps-vcpkg installs a manifest as an action, and planning installs nothing run: bash .github/scripts/check-deps-and-qt.sh vcpkg-consumer + - name: deps-archive extracts a zip the project keeps and deploys its tree beside the program + run: bash .github/scripts/check-deps-and-qt.sh archive-consumer + - name: deps-vcpkg in two workspace members that share no dependency run: bash .github/scripts/check-deps-and-qt.sh vcpkg-workspace @@ -2325,6 +2328,9 @@ jobs: - name: deps-vcpkg installs a manifest as an action, and planning installs nothing run: bash .github/scripts/check-deps-and-qt.sh vcpkg-consumer + - name: deps-archive extracts a zip the project keeps and deploys its tree beside the program + run: bash .github/scripts/check-deps-and-qt.sh archive-consumer + - name: deps-vcpkg in two workspace members that share no dependency run: bash .github/scripts/check-deps-and-qt.sh vcpkg-workspace diff --git a/README.md b/README.md index 369e107..7775835 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ engine's own module family and is not used here. | `rules-cuda` | `mcpp.rules.cuda` | 2026.9.6.6 | `[build] accel = "cuda…"`, a constrained glob for `*.cu`; the clang route with an LLVM toolchain, the nvcc route with a GCC one | | `rules-hip` | `mcpp.rules.hip` | 2026.9.6.6 | `[build] accel = "hip, cuda12.9+{sm_89}"`, a constrained glob for `*.hip`. On the NVIDIA platform HIP is a header layer over the CUDA runtime, so the compiler is the project's own clang and there is no ROCm on the machine | | `rules-metal` | `mcpp.rules.metal` | 2026.9.8.1 | the Metal toolchain of the macOS host's Xcode, located rather than installed: Xcode is not redistributable, so no payload is declared. `.metal` sources the project names on a macOS or iOS row become one `xcrun --sdk metal` action per shader (`-MMD`, so an edited `#include` recompiles the shaders that include it) and one `xcrun --sdk metallib` action per library, placed beside the program with `mcpp::deploy` under `metallib/`, which `dist-apple` maps into the bundle's resources. `compile(shaders)` compiles one source several times with definitions of its own, one library per `shader`; `options::library` links every shader into one library (`default` is the one `newDefaultLibrary` finds). Before planning anything the rule asks `xcrun --sdk --show-sdk-path` and `--find metal` / `--find metallib`, and refuses naming the command that answered nothing, because a missing SDK and a missing compiler have different remedies (Xcode 26 installs the Metal toolchain as a separate component). A shader on any other row is refused naming the row. CI compiles the fixture on `macos-15` and checks each library's magic, and that a header edit recompiles only the shaders that include it | -| `rules-qt` | `mcpp.rules.qt` | 2026.9.26.2 (mcpp#702) | a Qt 6 SDK: `rules-qt-xim` declares `xim:qt` 6.11.1 on the target axis, `rules-qt-xim-addons` adds `xim:qt-addons` (the additional libraries) as a second prefix, and `options::root` names an SDK from elsewhere. From 0.13.0. `moc` for every header under the package root that declares `Q_OBJECT`, `Q_GADGET` or `Q_NAMESPACE` and for a source that includes its own `.moc`; `uic` for `.ui`, `rcc` for `.qrc`, `lrelease` for `.ts` (named in `[build] sources` or in the options), each a `role = "source"` action with declared inputs. The modules are linked by full path, and the SDK's `bin/` (Windows) or `lib/` is a runtime search directory: the program's run path, `mcpp run`'s load path, `mcpp pack`'s closure, and on Windows the Qt DLLs the program imports placed beside it by the engine. The plugin directories `deploy_plugins` names are deployed beside the program. See [`deps-vcpkg`, `deps-cmake` and `rules-qt`](#deps-vcpkg-deps-cmake-and-rules-qt) | +| `rules-qt` | `mcpp.rules.qt` | 2026.9.26.2 (mcpp#702) | a Qt 6 SDK: `rules-qt-xim` declares `xim:qt` 6.11.1 on the target axis, `rules-qt-xim-addons` adds `xim:qt-addons` (the additional libraries) as a second prefix, and `options::root` names an SDK from elsewhere. From 0.13.0. `moc` for every header under the package root that declares `Q_OBJECT`, `Q_GADGET` or `Q_NAMESPACE` and for a source that includes its own `.moc`; `uic` for `.ui`, `rcc` for `.qrc`, `lrelease` for `.ts` (named in `[build] sources` or in the options), each a `role = "source"` action with declared inputs. The modules are linked by full path, and the SDK's `bin/` (Windows) or `lib/` is a runtime search directory: the program's run path, `mcpp run`'s load path, `mcpp pack`'s closure, and on Windows the Qt DLLs the program imports placed beside it by the engine. The plugin directories `deploy_plugins` names are deployed beside the program. See [`deps-vcpkg`, `deps-cmake`, `deps-archive` and `rules-qt`](#deps-vcpkg-deps-cmake-deps-archive-and-rules-qt) | | `rules-slang` | `mcpp.rules.slang` | 2026.9.7.1 | `[build] accel = "vulkan1.2"`, a constrained glob for `*.slang`. Slang is a different language from GLSL rather than a second driver for it -- its own module system, generics, and targets beyond SPIR-V -- so it is a rule of its own. `.slang` is **not** in the engine's device-source table: this feature declares `device_extensions = [".slang"]` and `rule_module = "mcpp.rules.slang"`, and the engine routes it from there. That is the criterion for the whole arrangement -- a new device language costs no engine release. Since 0.7.0 it has the same `options::storage` axis as `rules-spirv` (header / object / sidecar), `options::extra_args` for the arguments the rule has no field for, and `options::per_file` for what one shader gets that the others do not -- a project with a `-fvk-use-gl-layout` and one shader needing `-emit-spirv-via-glsl` writes both without leaving one `compile()` call | | `rules-spirv` | `mcpp.rules.spirv` | 2026.9.6.6 | `[build] accel = "vulkan1.2"`, a constrained glob for the shader stages; compiles each shader through a `role = "source"` action and states which of the two compilers produced it | | `rules-swift` | `mcpp.rules.swift` | 2026.9.8.1 | the Swift compiler of the macOS host's Xcode or Command Line Tools, located rather than installed, as `rules-metal` locates its toolchain. From 0.12.0. The `.swift` sources a project names on a macOS or iOS row compile as one module named after the package: one whole-module `xcrun --sdk swiftc -wmo -emit-object -target ` action whose role is `object`, so the object joins every image of the package, and one `swiftc -typecheck -emit-objc-header-path` action whose role is `source`, whose directory `mcpp::include_dir` adds, so the package's C and C++ sources include `-Swift.h`. `options::bridging_header` names a C header Swift sees without an import. The link receives the toolchain's `usr/lib/swift/` and the SDK's `usr/lib/swift` as search directories and `/usr/lib/swift` as a run path through `mcpp::link_flag`. Before planning anything the rule asks `xcrun --sdk --show-sdk-path` and `--find swiftc`, and refuses naming the command that answered nothing; a Swift source on any other row is refused naming the row. Not supported: a Swift `import` of another package's module, and another package's C++ including this package's generated header, which both need an engine channel that publishes a package's interface directory to its dependents; and SwiftPM dependencies. CI builds `tests/swift-consumer` on `macos-15` -- a C++ program calling a `@_cdecl` Swift function that calls back into C -- and runs it | @@ -90,8 +90,9 @@ engine's own module family and is not used here. | `dist-apple` | `mcpp.dist.apple` | 2026.9.14.2 (0.10.0); 2026.9.11.2 (macOS) and 2026.9.12.3 (iOS) before it | the base macOS install (`ditto`, `codesign`, `hdiutil`), and `xim:macapp-run` for `mcpp run` on macOS, which this feature declares with `when = "run"`. macOS: `Contents/`-shaped, as always. iOS (`aarch64-ios-sim`, `aarch64-ios`): a flat bundle at the same call site -- no separate feature, no separate module -- with `MinimumOSVersion` from `mcpp::min_platform_version()` (#622 A11), `CFBundleSupportedPlatforms` read from `env == "sim"`, `UIDeviceFamily`, `LSRequiresIPhoneOS`, and a directory of flat PNGs listed under `CFBundleIcons` in place of macOS's single `.icns` file. Signing is skipped on the simulator row (`options::identity` is ignored, with a `mcpp::warning` naming why), and the device row signs only with an identity. The iOS row is measured end to end on `macos-15`: a real `mcpp build`, `mcpp pack --format app` and `mcpp run` against `aarch64-ios-sim`, through `xim:apple-simulator-tools`' `simctl-run`. **The macOS floor is one release higher than its siblings** and the reason is not this member: under 2026.9.11.1 `mcpp pack` staged before dispatching and let a staging failure fail the command, so on a Mach-O program -- which the built-in closure walk refuses, because it uses `LD_TRACE_LOADED_OBJECTS` and dyld answers that by running the program -- every dispatched format was unreachable, including one that reads no staged tree. 2026.9.11.2 makes staging a service to the provider. From 0.9.2 the staged tree's deployed files (`bin//...`, which the engine stages for a Mach-O program before the closure walk since the release for mcpp#630) land at the bundle's resource destination -- `Contents/Resources//...` on macOS, the bundle root on iOS -- and the launcher alone goes to the executable directory, so `CFBundleExecutable` names a file that is where it says. The iOS fixture declares `llvm.libcxx` and `llvm.compiler-rt-builtins` under `cfg(os = "ios")`, which is what an application that imports `std` on those rows declares. From 0.10.0, with mcpp 2026.9.14.2: the dylibs the engine stages beside a Mach-O program, which the stage manifest's `needs` lines name, go to `Contents/Frameworks/` (`Frameworks/` on iOS) and not to the resources; the program is linked with the rpath that finds them there (`@executable_path/../Frameworks`, `@executable_path/Frameworks` on iOS) through `mcpp::link_flag`, so no file is edited after the link; a macOS bundle without `options::identity` is signed ad hoc, frameworks first and the bundle second, which `codesign --verify --deep --strict` requires of a bundle that carries a framework; an incomplete closure is a `mcpp::warning` naming the unresolved libraries; every refusal is a `mcpp::warning` as well, because the engine discards a build program's output when it exits 0. On macOS the member supplies the runner named `app` (`macapp-run`), so `mcpp run --format app` runs the bundle's executable in the foreground and returns its status with no runner in the manifest; a manifest runner of that name wins. `--format dmg` stages the bundle beside an `Applications` link and writes a UDZO image with `hdiutil create` (`options::volume_name`, `options::dmg`); it is refused on iOS. An engine below 2026.9.14.2 stages no `needs` lines, so the bundle carries no framework, anchors the rpath to the package directory, and hands the bundle directory to the kernel under `mcpp run --format app` unless `--runner app` is typed. CI measures the bundle on `macos-15`: the load command, the signature, the program with and without its framework (exit 7, then "Library not loaded"), `mcpp run --format app` with and without `--runner app`, and `hdiutil verify` and an attached image. From 0.11.0: a project's own Info.plist entries (`options::info_plist`), an iOS device bundle's provisioning profile (`options::provisioning_profile`), and `devicectl-run` (`xim:apple-device-tools`) as the device row's runner named `app` -- see [`dist-apple`: a project's Info.plist, and an iOS device](#dist-apple-a-projects-infoplist-and-an-ios-device). From 0.12.0 `options::omit_keys` leaves out a key the member only defaults -- see [`dist-apple`: a project's Info.plist, and an iOS device](#dist-apple-a-projects-infoplist-and-an-ios-device). From 0.12.0 a package in the resolved graph contributes Info.plist entries through `[package.metadata.dist-apple]`, applied before the application's own | | `dist-web` | `mcpp.dist.web` | 2026.9.13.1, the release that carries `${mcpp.self}` and `mcpp stage`'s argument shape as an engine contract (`stage --verify content --output `) -- what lets this member's copy run on every host mcpp does, Windows included, in place of the `cp` this member used through 0.8.0 | nothing beyond mcpp: `wasm32-emscripten` only. Copies `${mcpp.stage_dir}/bin/` -- the `.js` launcher, the implicit `.wasm`, the `.data` when present, and every `mcpp::deploy`'d file, all of which #622 A5 and A4 already stage there -- to `/web/`, dropping the `bin/` prefix a browser has no use for, and writes an `index.html` rendered from a project template or a built-in default that loads the script with a plain `