Publish smaller Android APKs for each architecture (#2349)
* feat(android): support F-Droid ABI-split builds * test(android): remove source-fragment ABI assertions
This commit is contained in:
@@ -120,6 +120,16 @@ PASEO_FDROID_BUILD=1 ./gradlew assembleRelease --no-daemon --max-workers=1 -Dorg
|
||||
|
||||
The flag must be present for both prebuild and Gradle because Gradle starts Metro for the release bundle. Keep the source build serial and daemon-free as shown above: compiling every Expo module can exhaust memory when Gradle workers run in parallel. The profile enables source-built Expo modules, excludes the proprietary camera, Firebase notification, and Expo development-client native modules, disables EAS updates and Gradle dependency metadata, and substitutes JavaScript stubs for camera and notifications. The resulting app supports direct and pasted-link pairing but not QR scanning or push notifications.
|
||||
|
||||
For a single-ABI APK, pass React Native's architecture property to Gradle:
|
||||
|
||||
```bash
|
||||
PASEO_FDROID_BUILD=1 ./gradlew assembleRelease \
|
||||
-PreactNativeArchitectures=arm64-v8a \
|
||||
--no-daemon --max-workers=1 -Dorg.gradle.parallel=false
|
||||
```
|
||||
|
||||
Supported values are `armeabi-v7a`, `arm64-v8a`, `x86`, and `x86_64`. The F-Droid profile filters native libraries to that ABI and changes the APK version code to `baseVersionCode * 10 + abiSuffix`, where the suffixes are ordered `1` through `4` in that same sequence. F-Droid metadata should use four build blocks with `VercodeOperation` entries `10 * %c + 1` through `10 * %c + 4` and pass the matching `reactNativeArchitectures` value in each build command. Builds without a single architecture keep the base version code.
|
||||
|
||||
Keep the excluded npm packages installed. Normal builds use them, while the F-Droid profile removes only their Android native modules and config plugins. Paseo always applies `expo-gradle-jvmargs` with `-Xmx4096m` and `-XX:MaxMetaspaceSize=1024m` so local Expo prebuilds have enough Gradle heap whether they use precompiled AARs or source-built Expo modules.
|
||||
|
||||
The EAS `production-apk` profile uses the large Android resource class. Release builds compile the native ABIs and run Hermes bundling in the same Gradle invocation; the default worker can exhaust its remaining memory and kill Hermes with exit code 137 even when Gradle's own heap is correctly sized.
|
||||
|
||||
@@ -11,6 +11,51 @@ const EXCLUDED_ANDROID_MODULES = [
|
||||
"expo-dev-menu-interface",
|
||||
];
|
||||
|
||||
const FDROID_ABI_VERSION_CODE_BLOCK = `// Paseo F-Droid single-ABI version codes
|
||||
def paseoAbiVersionCodes = [
|
||||
"armeabi-v7a": 1,
|
||||
"arm64-v8a": 2,
|
||||
"x86": 3,
|
||||
"x86_64": 4,
|
||||
]
|
||||
def paseoArchitectures = (findProperty("reactNativeArchitectures") ?: "")
|
||||
.toString()
|
||||
.split(",")
|
||||
.collect { it.trim() }
|
||||
.findAll { !it.isEmpty() }
|
||||
|
||||
if (paseoArchitectures.size() == 1) {
|
||||
def paseoAbi = paseoArchitectures[0]
|
||||
def paseoAbiVersionCode = paseoAbiVersionCodes[paseoAbi]
|
||||
if (paseoAbiVersionCode == null) {
|
||||
throw new GradleException("Unsupported Paseo Android ABI: " + paseoAbi)
|
||||
}
|
||||
android.defaultConfig.versionCode = android.defaultConfig.versionCode * 10 + paseoAbiVersionCode
|
||||
}
|
||||
`;
|
||||
|
||||
function configureFdroidAppBuildGradle(contents) {
|
||||
let configuredContents = contents;
|
||||
|
||||
if (!configuredContents.includes("dependenciesInfo {")) {
|
||||
const androidBlock = "android {";
|
||||
if (!configuredContents.includes(androidBlock)) {
|
||||
throw new Error("Could not disable F-Droid dependency metadata in app/build.gradle");
|
||||
}
|
||||
|
||||
configuredContents = configuredContents.replace(
|
||||
androidBlock,
|
||||
`${androidBlock}\n dependenciesInfo {\n includeInApk = false\n includeInBundle = false\n }`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!configuredContents.includes("// Paseo F-Droid single-ABI version codes")) {
|
||||
configuredContents = `${configuredContents.trimEnd()}\n\n${FDROID_ABI_VERSION_CODE_BLOCK}`;
|
||||
}
|
||||
|
||||
return configuredContents;
|
||||
}
|
||||
|
||||
function withFdroidAutolinking(config) {
|
||||
config = withDangerousMod(config, [
|
||||
"android",
|
||||
@@ -65,19 +110,7 @@ function withFdroidAutolinking(config) {
|
||||
});
|
||||
|
||||
return withAppBuildGradle(config, (modConfig) => {
|
||||
if (modConfig.modResults.contents.includes("dependenciesInfo {")) {
|
||||
return modConfig;
|
||||
}
|
||||
|
||||
const androidBlock = "android {";
|
||||
if (!modConfig.modResults.contents.includes(androidBlock)) {
|
||||
throw new Error("Could not disable F-Droid dependency metadata in app/build.gradle");
|
||||
}
|
||||
|
||||
modConfig.modResults.contents = modConfig.modResults.contents.replace(
|
||||
androidBlock,
|
||||
`${androidBlock}\n dependenciesInfo {\n includeInApk = false\n includeInBundle = false\n }`,
|
||||
);
|
||||
modConfig.modResults.contents = configureFdroidAppBuildGradle(modConfig.modResults.contents);
|
||||
return modConfig;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user