@@ -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