Skip to content

Commit e2aa934

Browse files
committed
0.11.0: dist-apk takes Kotlin, libraries, archives and a Maven graph; dist-apple takes Info.plist entries and an iOS device
dist-apk - options::kotlin_sources: kotlinc from xim:kotlin (feature dist-apk-kotlin) compiles the Kotlin with the Java as reference sources, before javac; the Kotlin stdlib is dexed. - R classes: aapt2 link --java, with --extra-packages for every library. - options::libraries (Android libraries from source), options::aars and options::jars, and options::maven through a lock file resolved by xim:coursier (feature dist-apk-maven). MCPP_DIST_APK_MAVEN=update and =fetch are the only packs that reach the network; an ordinary pack checks each cached artifact against the lock's sha256. - A stated subset of the manifest merger: tools:node remove/replace, tools:replace, ${applicationId}, a conflicting element refused by name. - options::sign = false writes the aligned package unsigned. dist-apple - options::info_plist: a project's entries join the Info.plist; a key the member derives is refused by name, and one it only defaults is replaced. - options::provisioning_profile, on the iOS device row: embedded as embedded.mobileprovision, its entitlements sign the bundle, and its application identifier must cover the bundle's. - The device row's runner named app is devicectl-run, from xim:apple-device-tools. mcpp::plugins::xml is the XML reader and writer the two members share. With no new option set, both members plan what 0.10.1 planned. The three xim recipes are openxlings/xim-pkgindex#844's, in the published index since ccc6e12. The Android fixtures build on Linux: mcpp 2026.9.14.2 does not link an Android row on a macOS host (mcpp-community/mcpp#647, E3).
1 parent 4844e33 commit e2aa934

31 files changed

Lines changed: 2731 additions & 128 deletions

File tree

.github/workflows/ci.yml

Lines changed: 121 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,7 @@ jobs:
12191219
- name: dist-web's second pack copies nothing
12201220
working-directory: tests/web-consumer
12211221
run: MCPP="$MCPP" ./check-web-idempotent.sh
1222+
12221223
# `kind = "app"` on `*-linux-android` links a shared object (#622 A3),
12231224
# so this fixture's own toolchain resolution provisions `xim:android-
12241225
# ndk` for `--target x86_64-linux-android` exactly as mcpp's own CI
@@ -1288,8 +1289,10 @@ jobs:
12881289
# reached by an absolute path, dex into one file carrying classes from
12891290
# both; (g) a project's own res/ links as the BASE -- two resources
12901291
# nothing else defines reach the apk (0.9.0 linked res/ as an aapt2
1291-
# overlay, which refuses any resource the base does not already have).
1292-
- name: dist-apk's manifest template, Java roots, res/ and version tokens, (a) to (h)
1292+
# overlay, which refuses any resource the base does not already have);
1293+
# (i) Kotlin sources without the `dist-apk-kotlin` feature are refused
1294+
# naming it, and (j) `sign = false` with a keystore is refused (0.11.0).
1295+
- name: dist-apk's manifest template, Java roots, res/, version tokens and refusals, (a) to (j)
12931296
working-directory: tests/apk-consumer
12941297
run: MCPP="$MCPP" ./check-apk-features.sh
12951298

@@ -1308,6 +1311,24 @@ jobs:
13081311
working-directory: tests/apk-consumer-shared
13091312
run: MCPP="$MCPP" ./check-apk-closure.sh
13101313

1314+
# 0.11.0: KOTLIN, R CLASSES, LIBRARIES, ARCHIVES, A MAVEN GRAPH AND AN
1315+
# UNSIGNED PACKAGE. One fixture reaches every configuration through the
1316+
# environment variables its script sets: an application of Kotlin and
1317+
# Java, a library from source, a local AAR and JAR in one signed APK
1318+
# (every class in the dex, the three R packages, the application's value
1319+
# winning a resource both define, the merged manifest, the archive's
1320+
# native library and assets); `sign = false`; an App Bundle from the same
1321+
# inputs; a manifest conflict refused by name; and a Maven graph through
1322+
# its lock -- refused without one, resolved by
1323+
# `MCPP_DIST_APK_MAVEN=update`, read from the cache by an ordinary pack,
1324+
# refused with an empty cache, restored by `MCPP_DIST_APK_MAVEN=fetch`,
1325+
# and refused when the lock is stale. The two explicit modes are the only
1326+
# packs that reach the network.
1327+
- name: dist-apk compiles Kotlin beside Java and packs libraries, archives and a Maven graph
1328+
timeout-minutes: 30
1329+
working-directory: tests/apk-consumer-libraries
1330+
run: MCPP="$MCPP" ./check-apk-libraries.sh
1331+
13111332
# Compiles the device unit on a machine with no GPU: the clang route
13121333
# produces sm_89 code from the payload toolkit. Running it needs a
13131334
# device, so the run is of the CPU variant, which the same seam serves.
@@ -1369,25 +1390,26 @@ jobs:
13691390
# `<name> = { ... }`. Reading keys out of the table BODY matched
13701391
# `sources` once per feature and reported the fixture as
13711392
# incomplete while the extractor was what broke.
1372-
# MEMBERS, NOT EVERY FEATURE. A feature that another feature
1373-
# IMPLIES is internal -- `surface` carries the build-program half of
1374-
# the generated surface and every member implies it, and a consumer
1375-
# never writes it. Subtracting the implied ones keeps this check
1393+
# MEMBERS, NOT EVERY FEATURE. A member is a feature named for its
1394+
# family -- `rules-*`, `tools-*`, `dist-*`, the README's naming table
1395+
# -- and a consumer names it. `surface` is not one: it carries the
1396+
# build-program half of the generated surface, every member implies
1397+
# it, and a consumer never writes it. Keeping it out keeps this check
13761398
# meaning "the fixture names every member a consumer can activate";
13771399
# padding the fixture instead would make it mean less.
13781400
#
1379-
# The rule is read out of the manifest, not listed here, so a seventh
1380-
# MEMBER is still caught: nothing implies it.
1401+
# BY NAME, NOT BY "NOTHING IMPLIES IT". That was the rule until 0.11.0,
1402+
# when `dist-apk-kotlin` and `dist-apk-maven` came to imply `dist-apk`,
1403+
# which a consumer still names. The rule is read out of the manifest,
1404+
# not listed here, so a new member is still caught.
13811405
# A TEMP FILE, NOT `<(...)`. This step also runs on windows-2022
13821406
# through Git Bash, where process substitution is emulated and not
13831407
# dependable -- and a check that behaves differently on one of the
13841408
# three hosts is the exact class of difference this job exists to
13851409
# catch, so it must not introduce one.
1386-
grep -oE '^implies[[:space:]]*=.*' mcpp.toml \
1387-
| grep -oE '"[a-z0-9-]+"' | tr -d '"' | sort -u > /tmp/implied.txt
13881410
grep -oE '^\[features\.[a-z0-9-]+\]' mcpp.toml \
1389-
| sed 's/^\[features\.//; s/\]$//' | sort > /tmp/allfeats.txt
1390-
feats=$(comm -23 /tmp/allfeats.txt /tmp/implied.txt)
1411+
| sed 's/^\[features\.//; s/\]$//' | grep -E '^(rules|tools|dist)-' | sort > /tmp/members.txt
1412+
feats=$(cat /tmp/members.txt)
13911413
used=$(sed -n '/features = \[/,/\], host-module/p' tests/all-rules-compile/mcpp.toml \
13921414
| grep -oE '"[a-z-]+"' | tr -d '"' | sort)
13931415
[ -n "$feats" ] || {
@@ -1710,23 +1732,20 @@ jobs:
17101732
# `<name> = { ... }`. Reading keys out of the table BODY matched
17111733
# `sources` once per feature and reported the fixture as
17121734
# incomplete while the extractor was what broke.
1713-
# MEMBERS, NOT EVERY FEATURE. A feature that another feature
1714-
# IMPLIES is internal -- `surface` carries the build-program half of
1715-
# the generated surface and every member implies it, and a consumer
1716-
# never writes it. Subtracting the implied ones keeps this check
1717-
# meaning "the fixture names every member a consumer can activate";
1718-
# padding the fixture instead would make it mean less. The rule is
1719-
# read out of the manifest, so a seventh MEMBER is still caught.
1735+
# MEMBERS, NOT EVERY FEATURE: a feature named for its family
1736+
# (`rules-*`, `tools-*`, `dist-*`), which a consumer names; `surface`,
1737+
# which every member implies and no consumer writes, is not one. By
1738+
# name and not by "nothing implies it" since 0.11.0, when
1739+
# `dist-apk-kotlin` and `dist-apk-maven` came to imply `dist-apk`. The
1740+
# rule is read out of the manifest, so a new member is still caught.
17201741
#
17211742
# A temp file, not `<(...)`: this step also runs on windows-2022
17221743
# through Git Bash, where process substitution is emulated and not
17231744
# dependable -- and a check that behaves differently on one of the
17241745
# three hosts is the class of difference this job exists to catch.
1725-
grep -oE '^implies[[:space:]]*=.*' mcpp.toml \
1726-
| grep -oE '"[a-z0-9-]+"' | tr -d '"' | sort -u > /tmp/implied.txt
17271746
grep -oE '^\[features\.[a-z0-9-]+\]' mcpp.toml \
1728-
| sed 's/^\[features\.//; s/\]$//' | sort > /tmp/allfeats.txt
1729-
feats=$(comm -23 /tmp/allfeats.txt /tmp/implied.txt)
1747+
| sed 's/^\[features\.//; s/\]$//' | grep -E '^(rules|tools|dist)-' | sort > /tmp/members.txt
1748+
feats=$(cat /tmp/members.txt)
17301749
used=$(sed -n '/features = \[/,/\], host-module/p' tests/all-rules-compile/mcpp.toml \
17311750
| grep -oE '"[a-z-]+"' | tr -d '"' | sort)
17321751
[ -n "$feats" ] || {
@@ -1950,6 +1969,37 @@ jobs:
19501969
grep -q 'closure = not-walked' pack.log || echo "note: the stage manifest's closure line was not echoed by pack"
19511970
echo "ok: one bundle, a valid plist, it launches, and the resource is where NSBundle looks"
19521971
1972+
# 0.11.0 (`options::info_plist`): a project's Info.plist entries join the
1973+
# generated plist of the real bundle, a key the member only defaults
1974+
# (`NSHighResolutionCapable`) takes the project's value, Apple's parser
1975+
# accepts the result, and the bundle still launches. The refusal of a key
1976+
# the member derives is asserted at the plan level, in the Linux job.
1977+
- name: dist-apple carries a project's Info.plist entries, and the bundle launches
1978+
if: ${{ !cancelled() && runner.os == 'macOS' }}
1979+
working-directory: tests/app-consumer
1980+
run: |
1981+
set -e
1982+
export APP_CONSUMER_INFO_PLIST="$PWD/info-plist/usage.plist"
1983+
"$MCPP" pack --format app | tee pack-info.log
1984+
app=$(find target -name '*.app' -type d | head -1)
1985+
test -n "$app" || { cat pack-info.log; echo "FAIL: no .app bundle"; exit 1; }
1986+
plist="$app/Contents/Info.plist"
1987+
plutil -lint "$plist"
1988+
pb() { /usr/libexec/PlistBuddy -c "Print :$1" "$plist"; }
1989+
[ "$(pb NSCameraUsageDescription)" = "Scans the codes a user points the camera at." ] \
1990+
|| { echo "FAIL: the usage description did not reach the bundle"; cat "$plist"; exit 1; }
1991+
[ "$(pb CFBundleURLTypes:0:CFBundleURLSchemes:0)" = "mcpp-fixture" ] \
1992+
|| { echo "FAIL: the URL type did not reach the bundle"; cat "$plist"; exit 1; }
1993+
[ "$(pb NSHighResolutionCapable)" = "false" ] \
1994+
|| { echo "FAIL: the project's NSHighResolutionCapable did not replace the default"; cat "$plist"; exit 1; }
1995+
[ "$(grep -c '<key>NSHighResolutionCapable</key>' "$plist")" -eq 1 ] \
1996+
|| { echo "FAIL: NSHighResolutionCapable is stated twice"; cat "$plist"; exit 1; }
1997+
[ "$(pb CFBundleExecutable)" = "app-consumer" ] \
1998+
|| { echo "FAIL: CFBundleExecutable is no longer the member's"; cat "$plist"; exit 1; }
1999+
"$app/Contents/MacOS/app-consumer" | grep -q '^app-consumer ok' \
2000+
|| { echo "FAIL: the bundle does not launch with the project's entries"; exit 1; }
2001+
echo "ok: the project's entries are in the bundle's Info.plist, plutil accepts it, and the bundle launches"
2002+
19532003
# THE CLOSURE AS A FRAMEWORK, ON THE BUNDLE (mcpp#634, B1 to B3). The
19542004
# plan-level half runs on Linux. This step, the Metal step and the iOS
19552005
# step measure independent things, so each runs when an earlier step of
@@ -2070,6 +2120,54 @@ jobs:
20702120
|| { echo "FAIL: the program's output line '1-2-3' is absent"; printf '%s\n' "$out" | tail -8 | od -c; exit 1; }
20712121
echo "ok: a flat iOS Simulator bundle, MinimumOSVersion 17.0, and the simulator ran it and returned its status 7"
20722122
2123+
# 0.11.0 (`options::info_plist`) on the iOS row: the project's entries
2124+
# join the simulator bundle's Info.plist, its `UIDeviceFamily` replaces
2125+
# the member's default, and the simulator installs and runs the bundle.
2126+
- name: dist-apple's iOS row carries a project's Info.plist entries, and the simulator runs the bundle
2127+
if: ${{ !cancelled() && runner.os == 'macOS' }}
2128+
working-directory: tests/ios-app-consumer
2129+
run: |
2130+
set -uo pipefail
2131+
if ! xcrun --sdk iphonesimulator --show-sdk-path >/dev/null 2>&1; then
2132+
echo "SKIP: no iphonesimulator SDK on this runner"; exit 0
2133+
fi
2134+
set -e
2135+
export IOS_APP_CONSUMER_INFO_PLIST="$PWD/info-plist/usage.plist"
2136+
"$MCPP" pack --target aarch64-ios-sim --format app | tee pack-info.log
2137+
app=$(find target -name '*.app' -type d | head -1)
2138+
test -n "$app" || { cat pack-info.log; echo "FAIL: no .app bundle"; exit 1; }
2139+
plist="$app/Info.plist"
2140+
plutil -lint "$plist"
2141+
pb() { /usr/libexec/PlistBuddy -c "Print :$1" "$plist"; }
2142+
[ "$(pb NSCameraUsageDescription)" = "Scans the codes a user points the camera at." ] \
2143+
|| { echo "FAIL: the usage description did not reach the bundle"; cat "$plist"; exit 1; }
2144+
[ "$(pb CFBundleURLTypes:0:CFBundleURLSchemes:0)" = "mcpp-fixture" ] \
2145+
|| { echo "FAIL: the URL type did not reach the bundle"; cat "$plist"; exit 1; }
2146+
[ "$(pb UIDeviceFamily:0)" = "1" ] && ! pb UIDeviceFamily:1 >/dev/null 2>&1 \
2147+
|| { echo "FAIL: UIDeviceFamily is not the project's (1)"; cat "$plist"; exit 1; }
2148+
[ "$(pb CFBundleSupportedPlatforms:0)" = "iPhoneSimulator" ] \
2149+
|| { echo "FAIL: CFBundleSupportedPlatforms is no longer the member's"; cat "$plist"; exit 1; }
2150+
out=$("$MCPP" run --target aarch64-ios-sim --format app 2>&1) && rc=0 || rc=$?
2151+
printf '%s\n' "$out" | tail -20
2152+
[ "$rc" -eq 7 ] \
2153+
|| { echo "FAIL: mcpp run exited $rc with the project's entries, and the program exits 7"; exit 1; }
2154+
echo "ok: the simulator bundle carries the project's entries, and the simulator ran it and returned 7"
2155+
2156+
# NO dist-apk STEP ON THIS HOST, AND THE REASON IS THE ENGINE. The NDK,
2157+
# the build tools, the platform, the JDK, the Kotlin compiler and coursier
2158+
# all publish a macOS table, and `tests/apk-consumer` was packed here once
2159+
# (0.11.0's first run): `mcpp build --target x86_64-linux-android` failed
2160+
# at the application's own link with
2161+
#
2162+
# ld64.lld: error: unknown argument '-soname'
2163+
#
2164+
# before any dist-apk step ran. On a macOS host mcpp 2026.9.14.2 composes
2165+
# the link line in its macOS branch, which states the target triple only
2166+
# for an Apple row, so `-fuse-ld=lld` picks the Mach-O flavour
2167+
# (mcpp-community/mcpp#647, E3). The steps return when an engine links
2168+
# that row; the Windows NDK has no libc++ module surface, so that host
2169+
# has no Android row at all.
2170+
20732171
- name: the rule declared its own compiler
20742172
working-directory: tests/spirv-consumer
20752173
run: |

0 commit comments

Comments
 (0)