fix: Detect newArchEnabled at subproject scope on Android#1314
Conversation
`isNewArchitectureEnabled()` in react-native-nitro-modules, the Nitrogen
module template and both internal test packages reads
`rootProject.hasProperty("newArchEnabled")`. On RN >= 0.82 with Expo SDK
55, the new architecture is mandatory and the property is no longer
required to live in `android/gradle.properties`; React Native's
`ReactRootProjectPlugin` instead sets `newArchEnabled=true` on each
subproject's extraProperties. The rootProject-scoped check therefore
returns false in that scenario, the libraries silently skip
`apply plugin: "com.facebook.react"`, no codegen tasks register, and the
consumer app fails at `:app:configureCMakeDebug` with:
CMake Error at .../Android-autolinking.cmake:
add_subdirectory given source
".../node_modules/react-native-nitro-modules/android/build/generated/source/codegen/jni/"
which is not an existing directory.
This was previously reported in mrousavy#1031 (closed without identifying the
root cause).
Switch the helper to `project.hasProperty("newArchEnabled")`. That
covers both the RN >= 0.82 path (subproject extraProperties) and the
legacy explicit-opt-in path (`gradle.properties` values propagate to
every project), and keeps the `src/oldarch` source path viable for
RN < 0.82.
Pin the regression by removing `newArchEnabled=true` from
`example/android/gradle.properties`. Without the helper change the
example app fails to build under `build-android.yml`; with it the
build is green.
|
@jmalmo is attempting to deploy a commit to the Margelo Team on Vercel. A member of the Team first needs to authorize it. |
|
Follow-up filed: mrousavy/react-native-mmkv#1037 — same one-line fix applied to mmkv's independent copy of the gate. As called out in the Scope section above, mmkv's regression-pin test is currently blocked on this PR landing + a tagged nitro release; the mmkv PR is therefore scoped to just the helper change for now. |
|
Not sure if I fully get this - we still use |
|
The asymmetry is in RN's gradle plugin.
class ReactRootProjectPlugin : Plugin<Project> {
override fun apply(project: Project) {
checkLegacyArchProperty(project)
project.subprojects { subproject ->
...
// We set the New Architecture properties to true for all subprojects. So that
// libraries don't need to be modified and can keep on using the isNewArchEnabled()
// function to check if property is set.
...
subproject.extraProperties.set(PropertyUtils.NEW_ARCH_ENABLED, "true")
subproject.extraProperties.set(PropertyUtils.SCOPED_NEW_ARCH_ENABLED, "true")
}
}
}( Nothing in there sets the property on the root. So from inside
I verified this directly by adding a debug Which is why the current gate return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"skips To your "you cannot set new arch to true on one project but not the other" point — agreed, and the fix isn't doing that. Lines 40–41 unconditionally set both keys to There's an alternative I considered and rejected: callstack/react-native-pager-view#1047 deletes the gate entirely on the basis that 0.82+ is new-arch-only. Defensible if you'd rather drop |
What I changed
I switched
isNewArchitectureEnabled()fromrootProject.hasProperty(...)toproject.hasProperty(...)in the four bundledandroid/build.gradlefiles (react-native-nitro-modules, the Nitrogenpackages/template, and both test packages), plus the example app's helper.I also dropped
newArchEnabled=truefromexample/android/gradle.propertiesto pin the regression —build-android.ymlnow exercises the same code path real consumers hit.Why
I hit this on a fresh React Native 0.83 / Expo SDK 55 dev-client app with
react-native-nitro-modulesandreact-native-mmkv@^4installed.bun androidfailed at:app:configureCMakeDebug[arm64-v8a]:After digging in: on RN >= 0.82 the new architecture is mandatory and
newArchEnabledis no longer required to live ingradle.properties. React Native'sReactRootProjectPlugininstead sets the property on each subproject'sextraProperties(seeReactRootProjectPlugin.kt— thesubprojects { ... }block setsextraProperties.newArchEnabled = "true"per subproject; the root project itself is not touched). The current rootProject-scoped check misses that path — the bundled libs skipapply plugin: "com.facebook.react", no codegen tasks register, and the consumer build dies during CMake configure.I think this is the same root cause as #1031 — the reporter there worked around it by regenerating
android/from a fresh template (which re-introducednewArchEnabled=trueingradle.propertiesand masked the gate), so the issue got closed before the underlying bug was pinned. It's also reproducible after a stale, non---cleanExpo prebuild, where the existinggradle.propertiesstays put and never picks up the new template's content.project.hasProperty(...)covers all three cases without dropping legacy-arch support:ReactRootProjectPlugin's subprojectextraProperties.gradle.propertiesvalues propagate to every project, soproject.hasProperty(...)still returnstrue.src/oldarchsource path is preserved.I considered going the more aggressive route and deleting the gate entirely (mirroring callstack/react-native-pager-view#1047), but Nitro's
peerDependenciesare still permissive (react-native: "*") andsrc/oldarchis non-empty, so the conservative fix felt right. Happy to switch if you'd prefer the harder line.Scope — what this PR does not fix
To be upfront about this: my reproduction scenario in the "Why" section above mentions both
react-native-nitro-modulesandreact-native-mmkv@^4. This PR only fixesnitroitself. It does not fix consumer apps that already depend on a published version ofreact-native-mmkv(or any other Nitrogen-bootstrapped library), because:packages/template/android/build.gradleonly affects libraries that bootstrap from the template after this PR lands. Existing libraries carry a copy of the gate baked into their own publishedbuild.gradleat the time they were created, not regenerated by Nitrogen on each build.react-native-mmkv@4.x'spackages/react-native-mmkv/android/build.gradle(line 18 onmain) still readsrootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true".withGradlePropertiesconfig-plugin workaround,react-native-mmkvand any other downstream Nitrogen-bootstrapped library need their own follow-up PRs that mirror this change. I'll send those separately and will link them here.The Nitrogen generator itself (
createGradleExtension.ts) does not emit the gate — verified by inspection — so once this PR lands, newly-bootstrapped Nitro modules will pick up the fix frompackages/template.Test plan
I removed
newArchEnabled=truefromexample/android/gradle.properties, so the existingbuild-android.ymlworkflow now fails onmain(the bundled libs skip codegen) and passes with this PR. I verified locally with the same Gradle invocation CI uses:Result:
BUILD SUCCESSFUL in 2m 34s.generateCodegenArtifactsFromSchemaandconfigureCMakeDebug[x86_64]ran forreact-native-nitro-modules,react-native-nitro-test,react-native-nitro-test-externaland the app, and:app:assembleDebugcompleted.The Gradle build cache (
actions/cacheinbuild-android.yml) cannot mask the regression: cache key hashes**/*.gradle*(which this PR modifies), and per-librarybuild/generated/source/codegen/jni/outputs aren't in the cache key, so a stale cache hit can't paper over a missing codegen task.Other checks I ran:
bun install(postinstall builds nitro, nitrogen and both test packages) — green.bun typecheck— green.bun lint— green.bun specsnot run; no nitrogen output changes.Notes
main. No nitrogen spec changes, no spec/test removals, no unrelated edits.