@@ -1219,6 +1219,20 @@ 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+
1223+ # TEMPORARY, UNTIL openxlings/xim-pkgindex#844 IS IN THE PUBLISHED INDEX.
1224+ # 0.11.0's `dist-apk-kotlin` and `dist-apk-maven` declare `xim:kotlin` and
1225+ # `xim:coursier`, which only that pull request adds (with
1226+ # `xim:apple-device-tools`). The script writes the reviewed recipes into
1227+ # the synced index and does nothing for a recipe the index already
1228+ # carries; see its header for when it and this step are deleted. Here,
1229+ # right before the Android steps, because the index is synced again when
1230+ # a dependency misses and a sync rewrites the cache this edits.
1231+ - name : bridge the xim recipes the published index does not carry yet
1232+ run : |
1233+ "$MCPP" index update
1234+ python3 .github/scripts/bridge-xim-recipes.py
1235+
12221236 # `kind = "app"` on `*-linux-android` links a shared object (#622 A3),
12231237 # so this fixture's own toolchain resolution provisions `xim:android-
12241238 # ndk` for `--target x86_64-linux-android` exactly as mcpp's own CI
@@ -1288,8 +1302,10 @@ jobs:
12881302 # reached by an absolute path, dex into one file carrying classes from
12891303 # both; (g) a project's own res/ links as the BASE -- two resources
12901304 # 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)
1305+ # overlay, which refuses any resource the base does not already have);
1306+ # (i) Kotlin sources without the `dist-apk-kotlin` feature are refused
1307+ # naming it, and (j) `sign = false` with a keystore is refused (0.11.0).
1308+ - name : dist-apk's manifest template, Java roots, res/, version tokens and refusals, (a) to (j)
12931309 working-directory : tests/apk-consumer
12941310 run : MCPP="$MCPP" ./check-apk-features.sh
12951311
@@ -1308,6 +1324,24 @@ jobs:
13081324 working-directory : tests/apk-consumer-shared
13091325 run : MCPP="$MCPP" ./check-apk-closure.sh
13101326
1327+ # 0.11.0: KOTLIN, R CLASSES, LIBRARIES, ARCHIVES, A MAVEN GRAPH AND AN
1328+ # UNSIGNED PACKAGE. One fixture reaches every configuration through the
1329+ # environment variables its script sets: an application of Kotlin and
1330+ # Java, a library from source, a local AAR and JAR in one signed APK
1331+ # (every class in the dex, the three R packages, the application's value
1332+ # winning a resource both define, the merged manifest, the archive's
1333+ # native library and assets); `sign = false`; an App Bundle from the same
1334+ # inputs; a manifest conflict refused by name; and a Maven graph through
1335+ # its lock -- refused without one, resolved by
1336+ # `MCPP_DIST_APK_MAVEN=update`, read from the cache by an ordinary pack,
1337+ # refused with an empty cache, restored by `MCPP_DIST_APK_MAVEN=fetch`,
1338+ # and refused when the lock is stale. The two explicit modes are the only
1339+ # packs that reach the network.
1340+ - name : dist-apk compiles Kotlin beside Java and packs libraries, archives and a Maven graph
1341+ timeout-minutes : 30
1342+ working-directory : tests/apk-consumer-libraries
1343+ run : MCPP="$MCPP" ./check-apk-libraries.sh
1344+
13111345 # Compiles the device unit on a machine with no GPU: the clang route
13121346 # produces sm_89 code from the payload toolkit. Running it needs a
13131347 # device, so the run is of the CPU variant, which the same seam serves.
@@ -1369,25 +1403,26 @@ jobs:
13691403 # `<name> = { ... }`. Reading keys out of the table BODY matched
13701404 # `sources` once per feature and reported the fixture as
13711405 # 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
1406+ # MEMBERS, NOT EVERY FEATURE. A member is a feature named for its
1407+ # family -- `rules-*`, `tools-*`, `dist-*`, the README's naming table
1408+ # -- and a consumer names it. `surface` is not one: it carries the
1409+ # build-program half of the generated surface, every member implies
1410+ # it, and a consumer never writes it. Keeping it out keeps this check
13761411 # meaning "the fixture names every member a consumer can activate";
13771412 # padding the fixture instead would make it mean less.
13781413 #
1379- # The rule is read out of the manifest, not listed here, so a seventh
1380- # MEMBER is still caught: nothing implies it.
1414+ # BY NAME, NOT BY "NOTHING IMPLIES IT". That was the rule until 0.11.0,
1415+ # when `dist-apk-kotlin` and `dist-apk-maven` came to imply `dist-apk`,
1416+ # which a consumer still names. The rule is read out of the manifest,
1417+ # not listed here, so a new member is still caught.
13811418 # A TEMP FILE, NOT `<(...)`. This step also runs on windows-2022
13821419 # through Git Bash, where process substitution is emulated and not
13831420 # dependable -- and a check that behaves differently on one of the
13841421 # three hosts is the exact class of difference this job exists to
13851422 # 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
13881423 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)
1424+ | sed 's/^\[features\.//; s/\]$//' | grep -E '^(rules|tools|dist)-' | sort > /tmp/members .txt
1425+ feats=$(cat /tmp/members .txt)
13911426 used=$(sed -n '/features = \[/,/\], host-module/p' tests/all-rules-compile/mcpp.toml \
13921427 | grep -oE '"[a-z-]+"' | tr -d '"' | sort)
13931428 [ -n "$feats" ] || {
@@ -1710,23 +1745,20 @@ jobs:
17101745 # `<name> = { ... }`. Reading keys out of the table BODY matched
17111746 # `sources` once per feature and reported the fixture as
17121747 # 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.
1748+ # MEMBERS, NOT EVERY FEATURE: a feature named for its family
1749+ # (`rules-*`, `tools-*`, `dist-*`), which a consumer names; `surface`,
1750+ # which every member implies and no consumer writes, is not one. By
1751+ # name and not by "nothing implies it" since 0.11.0, when
1752+ # `dist-apk-kotlin` and `dist-apk-maven` came to imply `dist-apk`. The
1753+ # rule is read out of the manifest, so a new member is still caught.
17201754 #
17211755 # A temp file, not `<(...)`: this step also runs on windows-2022
17221756 # through Git Bash, where process substitution is emulated and not
17231757 # dependable -- and a check that behaves differently on one of the
17241758 # 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
17271759 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)
1760+ | sed 's/^\[features\.//; s/\]$//' | grep -E '^(rules|tools|dist)-' | sort > /tmp/members .txt
1761+ feats=$(cat /tmp/members .txt)
17301762 used=$(sed -n '/features = \[/,/\], host-module/p' tests/all-rules-compile/mcpp.toml \
17311763 | grep -oE '"[a-z-]+"' | tr -d '"' | sort)
17321764 [ -n "$feats" ] || {
@@ -1950,6 +1982,37 @@ jobs:
19501982 grep -q 'closure = not-walked' pack.log || echo "note: the stage manifest's closure line was not echoed by pack"
19511983 echo "ok: one bundle, a valid plist, it launches, and the resource is where NSBundle looks"
19521984
1985+ # 0.11.0 (`options::info_plist`): a project's Info.plist entries join the
1986+ # generated plist of the real bundle, a key the member only defaults
1987+ # (`NSHighResolutionCapable`) takes the project's value, Apple's parser
1988+ # accepts the result, and the bundle still launches. The refusal of a key
1989+ # the member derives is asserted at the plan level, in the Linux job.
1990+ - name : dist-apple carries a project's Info.plist entries, and the bundle launches
1991+ if : ${{ !cancelled() && runner.os == 'macOS' }}
1992+ working-directory : tests/app-consumer
1993+ run : |
1994+ set -e
1995+ export APP_CONSUMER_INFO_PLIST="$PWD/info-plist/usage.plist"
1996+ "$MCPP" pack --format app | tee pack-info.log
1997+ app=$(find target -name '*.app' -type d | head -1)
1998+ test -n "$app" || { cat pack-info.log; echo "FAIL: no .app bundle"; exit 1; }
1999+ plist="$app/Contents/Info.plist"
2000+ plutil -lint "$plist"
2001+ pb() { /usr/libexec/PlistBuddy -c "Print :$1" "$plist"; }
2002+ [ "$(pb NSCameraUsageDescription)" = "Scans the codes a user points the camera at." ] \
2003+ || { echo "FAIL: the usage description did not reach the bundle"; cat "$plist"; exit 1; }
2004+ [ "$(pb CFBundleURLTypes:0:CFBundleURLSchemes:0)" = "mcpp-fixture" ] \
2005+ || { echo "FAIL: the URL type did not reach the bundle"; cat "$plist"; exit 1; }
2006+ [ "$(pb NSHighResolutionCapable)" = "false" ] \
2007+ || { echo "FAIL: the project's NSHighResolutionCapable did not replace the default"; cat "$plist"; exit 1; }
2008+ [ "$(grep -c '<key>NSHighResolutionCapable</key>' "$plist")" -eq 1 ] \
2009+ || { echo "FAIL: NSHighResolutionCapable is stated twice"; cat "$plist"; exit 1; }
2010+ [ "$(pb CFBundleExecutable)" = "app-consumer" ] \
2011+ || { echo "FAIL: CFBundleExecutable is no longer the member's"; cat "$plist"; exit 1; }
2012+ "$app/Contents/MacOS/app-consumer" | grep -q '^app-consumer ok' \
2013+ || { echo "FAIL: the bundle does not launch with the project's entries"; exit 1; }
2014+ echo "ok: the project's entries are in the bundle's Info.plist, plutil accepts it, and the bundle launches"
2015+
19532016 # THE CLOSURE AS A FRAMEWORK, ON THE BUNDLE (mcpp#634, B1 to B3). The
19542017 # plan-level half runs on Linux. This step, the Metal step and the iOS
19552018 # step measure independent things, so each runs when an earlier step of
@@ -2070,6 +2133,54 @@ jobs:
20702133 || { echo "FAIL: the program's output line '1-2-3' is absent"; printf '%s\n' "$out" | tail -8 | od -c; exit 1; }
20712134 echo "ok: a flat iOS Simulator bundle, MinimumOSVersion 17.0, and the simulator ran it and returned its status 7"
20722135
2136+ # 0.11.0 (`options::info_plist`) on the iOS row: the project's entries
2137+ # join the simulator bundle's Info.plist, its `UIDeviceFamily` replaces
2138+ # the member's default, and the simulator installs and runs the bundle.
2139+ - name : dist-apple's iOS row carries a project's Info.plist entries, and the simulator runs the bundle
2140+ if : ${{ !cancelled() && runner.os == 'macOS' }}
2141+ working-directory : tests/ios-app-consumer
2142+ run : |
2143+ set -uo pipefail
2144+ if ! xcrun --sdk iphonesimulator --show-sdk-path >/dev/null 2>&1; then
2145+ echo "SKIP: no iphonesimulator SDK on this runner"; exit 0
2146+ fi
2147+ set -e
2148+ export IOS_APP_CONSUMER_INFO_PLIST="$PWD/info-plist/usage.plist"
2149+ "$MCPP" pack --target aarch64-ios-sim --format app | tee pack-info.log
2150+ app=$(find target -name '*.app' -type d | head -1)
2151+ test -n "$app" || { cat pack-info.log; echo "FAIL: no .app bundle"; exit 1; }
2152+ plist="$app/Info.plist"
2153+ plutil -lint "$plist"
2154+ pb() { /usr/libexec/PlistBuddy -c "Print :$1" "$plist"; }
2155+ [ "$(pb NSCameraUsageDescription)" = "Scans the codes a user points the camera at." ] \
2156+ || { echo "FAIL: the usage description did not reach the bundle"; cat "$plist"; exit 1; }
2157+ [ "$(pb CFBundleURLTypes:0:CFBundleURLSchemes:0)" = "mcpp-fixture" ] \
2158+ || { echo "FAIL: the URL type did not reach the bundle"; cat "$plist"; exit 1; }
2159+ [ "$(pb UIDeviceFamily:0)" = "1" ] && ! pb UIDeviceFamily:1 >/dev/null 2>&1 \
2160+ || { echo "FAIL: UIDeviceFamily is not the project's (1)"; cat "$plist"; exit 1; }
2161+ [ "$(pb CFBundleSupportedPlatforms:0)" = "iPhoneSimulator" ] \
2162+ || { echo "FAIL: CFBundleSupportedPlatforms is no longer the member's"; cat "$plist"; exit 1; }
2163+ out=$("$MCPP" run --target aarch64-ios-sim --format app 2>&1) && rc=0 || rc=$?
2164+ printf '%s\n' "$out" | tail -20
2165+ [ "$rc" -eq 7 ] \
2166+ || { echo "FAIL: mcpp run exited $rc with the project's entries, and the program exits 7"; exit 1; }
2167+ echo "ok: the simulator bundle carries the project's entries, and the simulator ran it and returned 7"
2168+
2169+ # NO dist-apk STEP ON THIS HOST, AND THE REASON IS THE ENGINE. The NDK,
2170+ # the build tools, the platform, the JDK, the Kotlin compiler and coursier
2171+ # all publish a macOS table, and `tests/apk-consumer` was packed here once
2172+ # (0.11.0's first run): `mcpp build --target x86_64-linux-android` failed
2173+ # at the application's own link with
2174+ #
2175+ # ld64.lld: error: unknown argument '-soname'
2176+ #
2177+ # before any dist-apk step ran. On a macOS host mcpp 2026.9.14.2 composes
2178+ # the link line in its macOS branch, which states the target triple only
2179+ # for an Apple row, so `-fuse-ld=lld` picks the Mach-O flavour
2180+ # (mcpp-community/mcpp#647, E3). The steps return when an engine links
2181+ # that row; the Windows NDK has no libc++ module surface, so that host
2182+ # has no Android row at all.
2183+
20732184 - name : the rule declared its own compiler
20742185 working-directory : tests/spirv-consumer
20752186 run : |
0 commit comments