Skip to content

0.11.0: dist-apk takes Kotlin, libraries, archives and a Maven graph; dist-apple takes Info.plist entries and an iOS device - #26

Merged
Sunrisepeak merged 1 commit into
mainfrom
feat/0.11.0-android-kotlin-maven-apple-device
Sep 15, 2026
Merged

Sunrisepeak merged 1 commit into
mainfrom
feat/0.11.0-android-kotlin-maven-apple-device

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Sep 15, 2026

Copy link
Copy Markdown
Member

An Android application whose developers write Kotlin, read resources through R, or use a library got nothing from dist-apk 0.10.1 but Java sources and one res/. An iOS application could not state a usage description, and could not be signed for a device or run on one. Gradle and Xcode give a project these, so a project that builds with mcpp as well had to leave them out or keep them in another build system.

Every addition is an explicit option. With none set, both members plan exactly what 0.10.1 planned: check-apk-features.sh (a) still compares the level-0 manifest byte-for-byte, and a macOS Info.plist with no info_plist is byte-identical to 0.10.1's.

dist-apk

option what it does
kotlin_sources kotlinc compiles the Kotlin first (-jvm-target 17), with the Java as reference sources; javac then compiles the Java against it, and d8 dexes both with kotlin-stdlib.jar. The compiler is xim:kotlin 2.4.20, declared by the new feature dist-apk-kotlin
(none) R classes: aapt2 link --java, with --extra-packages for every library package, compiled with the sources
libraries Android libraries from source (package, resources, manifest, assets, Java and Kotlin roots). An earlier library wins a resource both define; the application wins over all of them
aars, jars Local archives. An AAR contributes its classes, res/, manifest, jni/<abi> (only the ABIs the package carries) and assets/
maven, maven_repositories, maven_lock, maven_cache Coordinates resolved with their transitive dependencies into a lock file by xim:coursier 2.1.24, declared by the new feature dist-apk-maven
sign false writes the aligned package unsigned (APK and AAB); refused together with keystore

No ordinary build reaches the network. MCPP_DIST_APK_MAVEN=update resolves and writes the lock, and MCPP_DIST_APK_MAVEN=fetch downloads exactly the locked artifacts. Without either, a pack reads the cache and checks each artifact's sha256 against the lock. It refuses when there is no lock, when the lock was resolved for other coordinates, or when an artifact is missing, and names the mode that fixes it. The repositories are recorded in the lock but not required, so a mirror serving the same bytes is accepted.

The manifest merge is a stated subset of the Android Gradle plugin's merger, documented in the README and in merge_manifests:

  • <manifest> and <application> children are added by kind and android:name, and <queries> are unioned;
  • a conflicting element is refused, naming it and the library, unless the application says tools:node="replace" or "remove"; a differing <application> attribute is refused unless it is listed in tools:replace;
  • a higher library minSdkVersion is refused;
  • ${applicationId} is substituted, and any other placeholder is refused;
  • tools: attributes are stripped from the result.

The two features are separate from dist-apk because provisioning runs before the build program says what it compiles. Declared by dist-apk itself, the 90 MB compiler would be installed for every Android consumer.

dist-apple

  • info_plist: a plist whose top-level entries join the bundle's Info.plist. A key the member derives (CFBundleIdentifier, the version keys, MinimumOSVersion, ...) is refused by name, and a key it only defaults (UIDeviceFamily, LSRequiresIPhoneOS, NSHighResolutionCapable) takes the project's value.
  • provisioning_profile: only on the device row (aarch64-ios), and only with identity. The profile is embedded as embedded.mobileprovision, and its Entitlements sign the bundle unless entitlements names a file; a wildcard application identifier is stated as the bundle's own. The profile's identifier must cover the bundle identifier. It is read from the profile's bytes, so the plan checks it on any host. A profile on the simulator row, one without an identity, or one for another identifier is refused.
  • devicectl-run, from xim:apple-device-tools 0.1.0, is the device row's runner named app (when = "run", on that row only). mcpp run --target aarch64-ios --format app installs the bundle and launches it with the console attached. The simulator row keeps simctl-run.

mcpp::plugins::xml in the lib root is the reader and writer both members use. It handles elements, attributes and text; comments, the declaration and a DOCTYPE are dropped.

Tests

  • tests/apk-consumer-libraries (new), on Linux:
    1. A signed APK from a Kotlin/Java application, a source library, an AAR and a JAR: every class dexed, three R packages, the application's value for a shared resource name, the merged manifest (a removed permission absent, no tools:), and the AAR's library for x86_64 only.
    2. sign = false: aligned, and apksigner does not verify it.
    3. An App Bundle that bundletool validate accepts.
    4. A manifest conflict refused by name.
    5. The Maven lock: no lock refused; update; a cached pack; a cached pack with unreachable repositories; an empty cache refused; fetch; a stale lock refused.
  • check-apk-features.sh: (i) Kotlin sources without dist-apk-kotlin are refused naming it; (j) sign = false together with a keystore is refused.
  • check-ios-plan.sh:
    • the device row supplies devicectl-run, and the simulator row does not;
    • Info.plist entries, with UIDeviceFamily replaced, and a derived key refused;
    • the embed step, codesign with the profile's entitlements, and a wildcard profile;
    • the three profile refusals.
  • macOS job:
    • the entries in a real macOS bundle that plutil accepts and that launches;
    • the entries in a simulator bundle that mcpp run runs (exit 7).
  • all-rules-compile names the two new features. Its CI check counts members by family name (rules-*, tools-*, dist-*), because dist-apk is now implied by another feature and still a member.
  • Locally, mcpp 2026.9.14.3 on Linux: check-apk-features.sh (a)–(j), check-apk-closure.sh, check-apk-libraries.sh (Maven through a mirror), check-ios-plan.sh, check-apple-plan.sh and all-rules-compile pass. A consumer of dist-apk alone packs with the three new recipes absent from the index, offline.

Not on a macOS host: Android. The first run of this PR packed apk-consumer on macos-15 as well, where every payload publishes a table. mcpp build --target x86_64-linux-android failed at the application's own link with ld64.lld: error: unknown argument '-soname', before any dist-apk step ran. mcpp 2026.9.14.2 composes a macOS host's link line in a branch that states the target triple only for an Apple row, so -fuse-ld=lld picks the Mach-O flavour. That is an engine defect, filed as E3 of mcpp-community/mcpp#647. The macOS dist-apk steps are out of the workflow until a release links that row, and the README says the build host is Linux.

Not measured: a device. No runner here has an iOS device, so the signed device bundle, the install, and whether devicectl returns the program's exit status are unmeasured; devicectl-run says so when it runs.

Dependencies

xim:kotlin 2.4.20, xim:coursier 2.1.24 and xim:apple-device-tools 0.1.0 come from openxlings/xim-pkgindex#844 (with gitcode CN mirrors), which is merged and in the published index since xim-index-ccc6e12. A client sync through the CN mirror resolves all three. The temporary CI bridge that wrote the reviewed recipes into the synced index is gone.

Version 0.11.0; the engine floor is unchanged (2026.9.14.2).

… 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).
@Sunrisepeak
Sunrisepeak force-pushed the feat/0.11.0-android-kotlin-maven-apple-device branch from a11c63e to e2aa934 Compare September 15, 2026 15:58
@Sunrisepeak
Sunrisepeak merged commit 56c0bd6 into main Sep 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant