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.
|
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.
|
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.
|
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",
|
"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) {
|
function withFdroidAutolinking(config) {
|
||||||
config = withDangerousMod(config, [
|
config = withDangerousMod(config, [
|
||||||
"android",
|
"android",
|
||||||
@@ -65,19 +110,7 @@ function withFdroidAutolinking(config) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return withAppBuildGradle(config, (modConfig) => {
|
return withAppBuildGradle(config, (modConfig) => {
|
||||||
if (modConfig.modResults.contents.includes("dependenciesInfo {")) {
|
modConfig.modResults.contents = configureFdroidAppBuildGradle(modConfig.modResults.contents);
|
||||||
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 }`,
|
|
||||||
);
|
|
||||||
return modConfig;
|
return modConfig;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user