Skip to content

Commit 8e333f7

Browse files
cipolleschifacebook-github-bot
authored andcommitted
Implement version parsing and connect to RNTester (#39502)
Summary: Pull Request resolved: #39502 This diff adds the parsing of the package.json to retrieve the version of react native and it matches it against a regex to decide whether to enable or not the New Architecture. This change depends on [this CLI's PR](react-native-community/cli#2076). ## Changelog: [Android][Added] - Parse RN Version to decide whether to enable the New Arch or not. Reviewed By: cortinico Differential Revision: D49270012 fbshipit-source-id: 0a829d3aa75ecd7fb45c1f40b03f5a959744f92e
1 parent 3078738 commit 8e333f7

File tree

7 files changed

+202
-23
lines changed

7 files changed

+202
-23
lines changed

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ReactPlugin : Plugin<Project> {
6464
}
6565

6666
configureReactNativeNdk(project, extension)
67-
configureBuildConfigFields(project)
67+
configureBuildConfigFields(project, extension)
6868
configureDevPorts(project)
6969
configureBackwardCompatibilityReactMap(project)
7070

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
4848
val isDebuggableVariant =
4949
config.debuggableVariants.get().any { it.equals(variant.name, ignoreCase = true) }
5050

51-
configureNewArchPackagingOptions(project, variant)
51+
configureNewArchPackagingOptions(project, config, variant)
5252
configureJsEnginePackagingOptions(config, variant, isHermesEnabledInThisVariant)
5353

5454
if (!isDebuggableVariant) {

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package com.facebook.react.utils
99

1010
import com.android.build.api.variant.AndroidComponentsExtension
11+
import com.facebook.react.ReactExtension
1112
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
1213
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
1314
import org.gradle.api.Action
@@ -17,13 +18,15 @@ import org.gradle.api.plugins.AppliedPlugin
1718
@Suppress("UnstableApiUsage")
1819
internal object AgpConfiguratorUtils {
1920

20-
fun configureBuildConfigFields(project: Project) {
21+
fun configureBuildConfigFields(project: Project, extension: ReactExtension) {
2122
val action =
2223
Action<AppliedPlugin> {
2324
project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
2425
ext.buildFeatures.buildConfig = true
2526
ext.defaultConfig.buildConfigField(
26-
"boolean", "IS_NEW_ARCHITECTURE_ENABLED", project.isNewArchEnabled.toString())
27+
"boolean",
28+
"IS_NEW_ARCHITECTURE_ENABLED",
29+
project.isNewArchEnabled(extension).toString())
2730
ext.defaultConfig.buildConfigField(
2831
"boolean", "IS_HERMES_ENABLED", project.isHermesEnabled.toString())
2932
}

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal object NdkConfiguratorUtils {
2020
fun configureReactNativeNdk(project: Project, extension: ReactExtension) {
2121
project.pluginManager.withPlugin("com.android.application") {
2222
project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
23-
if (!project.isNewArchEnabled) {
23+
if (!project.isNewArchEnabled(extension)) {
2424
// For Old Arch, we don't need to setup the NDK
2525
return@finalizeDsl
2626
}
@@ -74,9 +74,10 @@ internal object NdkConfiguratorUtils {
7474
*/
7575
fun configureNewArchPackagingOptions(
7676
project: Project,
77-
variant: Variant,
77+
extension: ReactExtension,
78+
variant: Variant
7879
) {
79-
if (!project.isNewArchEnabled) {
80+
if (!project.isNewArchEnabled(extension)) {
8081
// For Old Arch, we set a pickFirst only on libraries that we know are
8182
// clashing with our direct dependencies (mainly FBJNI and Hermes).
8283
variant.packaging.jniLibs.pickFirsts.addAll(

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package com.facebook.react.utils
99

10+
import com.facebook.react.ReactExtension
1011
import com.facebook.react.model.ModelPackageJson
1112
import com.facebook.react.utils.KotlinStdlibCompatUtils.lowercaseCompat
1213
import com.facebook.react.utils.KotlinStdlibCompatUtils.toBooleanStrictOrNullCompat
@@ -16,19 +17,22 @@ import com.facebook.react.utils.PropertyUtils.REACT_NATIVE_ARCHITECTURES
1617
import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_ENABLED
1718
import com.facebook.react.utils.PropertyUtils.SCOPED_NEW_ARCH_ENABLED
1819
import com.facebook.react.utils.PropertyUtils.SCOPED_REACT_NATIVE_ARCHITECTURES
20+
import java.io.File
1921
import org.gradle.api.Project
2022
import org.gradle.api.file.DirectoryProperty
2123

2224
internal object ProjectUtils {
23-
internal val Project.isNewArchEnabled: Boolean
24-
get() =
25-
(project.hasProperty(NEW_ARCH_ENABLED) &&
26-
project.property(NEW_ARCH_ENABLED).toString().toBoolean()) ||
27-
(project.hasProperty(SCOPED_NEW_ARCH_ENABLED) &&
28-
project.property(SCOPED_NEW_ARCH_ENABLED).toString().toBoolean())
2925

3026
const val HERMES_FALLBACK = true
3127

28+
internal fun Project.isNewArchEnabled(extension: ReactExtension): Boolean {
29+
return (project.hasProperty(NEW_ARCH_ENABLED) &&
30+
project.property(NEW_ARCH_ENABLED).toString().toBoolean()) ||
31+
(project.hasProperty(SCOPED_NEW_ARCH_ENABLED) &&
32+
project.property(SCOPED_NEW_ARCH_ENABLED).toString().toBoolean()) ||
33+
shouldEnableNewArchForReactNativeVersion(project.reactNativeDir(extension))
34+
}
35+
3236
internal val Project.isHermesEnabled: Boolean
3337
get() =
3438
if (project.hasProperty(HERMES_ENABLED) || project.hasProperty(SCOPED_HERMES_ENABLED)) {
@@ -75,4 +79,39 @@ internal object ProjectUtils {
7579
}
7680
return architectures
7781
}
82+
83+
internal fun Project.reactNativeDir(extension: ReactExtension): String =
84+
extension.reactNativeDir.get().asFile.absolutePath
85+
86+
internal fun shouldEnableNewArchForReactNativeVersion(reactNativeDir: String): Boolean {
87+
val packageJsonFile = File(reactNativeDir, "package.json")
88+
if (!packageJsonFile.exists()) {
89+
return false
90+
}
91+
92+
val rnPackageJson = JsonUtils.fromPackageJson(packageJsonFile)
93+
if (rnPackageJson == null) {
94+
return false
95+
}
96+
97+
// This regex describe the version syntax for React Native in the shape of
98+
// major.minor.patch[-<prerelease>[[-.]k]]
99+
// Where
100+
// major is a number
101+
// minor is a number
102+
// patch is a number
103+
// <prerelease>[-.]k is optional, but if present is preceeded by a `-`
104+
// the <prerelease> tag is a string.
105+
// it can be followed by `-` or `.` and k is a number.
106+
val regex = """^(\d+)\.(\d+)\.(\d+)(?:-(\w+(?:[-.]\d+)?))?$""".toRegex()
107+
108+
val matchResult = regex.find(rnPackageJson.version)
109+
110+
if (matchResult == null) {
111+
return false
112+
}
113+
114+
val major = matchResult.groupValues[1].toInt()
115+
return major > 0 && major < 1000
116+
}
78117
}

packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,141 @@ class ProjectUtilsTest {
2727

2828
@Test
2929
fun isNewArchEnabled_returnsFalseByDefault() {
30-
assertFalse(createProject().isNewArchEnabled)
30+
val project = createProject()
31+
val extension = TestReactExtension(project)
32+
assertFalse(createProject().isNewArchEnabled(extension))
3133
}
3234

3335
@Test
3436
fun isNewArchEnabled_withDisabled_returnsFalse() {
3537
val project = createProject()
3638
project.extensions.extraProperties.set("newArchEnabled", "false")
37-
assertFalse(project.isNewArchEnabled)
39+
val extension = TestReactExtension(project)
40+
assertFalse(project.isNewArchEnabled(extension))
3841
}
3942

4043
@Test
4144
fun isNewArchEnabled_withEnabled_returnsTrue() {
4245
val project = createProject()
4346
project.extensions.extraProperties.set("newArchEnabled", "true")
44-
assertTrue(project.isNewArchEnabled)
47+
val extension = TestReactExtension(project)
48+
assertTrue(project.isNewArchEnabled(extension))
4549
}
4650

4751
@Test
4852
fun isNewArchEnabled_withInvalid_returnsFalse() {
4953
val project = createProject()
5054
project.extensions.extraProperties.set("newArchEnabled", "¯\\_(ツ)_/¯")
51-
assertFalse(project.isNewArchEnabled)
55+
val extension = TestReactExtension(project)
56+
assertFalse(project.isNewArchEnabled(extension))
57+
}
58+
59+
@Test
60+
fun isNewArchEnabled_withRNVersion0_returnFalse() {
61+
val project = createProject()
62+
val extension = TestReactExtension(project)
63+
File(tempFolder.root, "package.json").apply {
64+
writeText(
65+
// language=json
66+
"""
67+
{
68+
"version": "0.73.0"
69+
}
70+
"""
71+
.trimIndent())
72+
}
73+
extension.reactNativeDir.set(tempFolder.root)
74+
assertFalse(project.isNewArchEnabled(extension))
75+
}
76+
77+
@Test
78+
fun isNewArchEnabled_withRNVersion1_returnTrue() {
79+
val project = createProject()
80+
val extension = TestReactExtension(project)
81+
File(tempFolder.root, "package.json").apply {
82+
writeText(
83+
// language=json
84+
"""
85+
{
86+
"version": "1.2.3"
87+
}
88+
"""
89+
.trimIndent())
90+
}
91+
extension.reactNativeDir.set(tempFolder.root)
92+
assertTrue(project.isNewArchEnabled(extension))
93+
}
94+
95+
@Test
96+
fun isNewArchEnabled_withRNVersion1PrereleaseString_returnTrue() {
97+
val project = createProject()
98+
val extension = TestReactExtension(project)
99+
File(tempFolder.root, "package.json").apply {
100+
writeText(
101+
// language=json
102+
"""
103+
{
104+
"version": "1.2.3-prealpha0"
105+
}
106+
"""
107+
.trimIndent())
108+
}
109+
extension.reactNativeDir.set(tempFolder.root)
110+
assertTrue(project.isNewArchEnabled(extension))
111+
}
112+
113+
@Test
114+
fun isNewArchEnabled_withRNVersion1PrereleaseStringDotNumber_returnTrue() {
115+
val project = createProject()
116+
val extension = TestReactExtension(project)
117+
File(tempFolder.root, "package.json").apply {
118+
writeText(
119+
// language=json
120+
"""
121+
{
122+
"version": "1.2.3-prealpha.0"
123+
}
124+
"""
125+
.trimIndent())
126+
}
127+
extension.reactNativeDir.set(tempFolder.root)
128+
assertTrue(project.isNewArchEnabled(extension))
129+
}
130+
131+
@Test
132+
fun isNewArchEnabled_withRNVersion1PrereleaseStringDashNumber_returnTrue() {
133+
val project = createProject()
134+
val extension = TestReactExtension(project)
135+
File(tempFolder.root, "package.json").apply {
136+
writeText(
137+
// language=json
138+
"""
139+
{
140+
"version": "1.2.3-prealpha-0"
141+
}
142+
"""
143+
.trimIndent())
144+
}
145+
extension.reactNativeDir.set(tempFolder.root)
146+
assertTrue(project.isNewArchEnabled(extension))
147+
}
148+
149+
@Test
150+
fun isNewArchEnabled_withRNVersion1000_returnFalse() {
151+
val project = createProject()
152+
val extension = TestReactExtension(project)
153+
File(tempFolder.root, "package.json").apply {
154+
writeText(
155+
// language=json
156+
"""
157+
{
158+
"version": "1000.0.0"
159+
}
160+
"""
161+
.trimIndent())
162+
}
163+
extension.reactNativeDir.set(tempFolder.root)
164+
assertFalse(project.isNewArchEnabled(extension))
52165
}
53166

54167
@Test

packages/rn-tester/android/app/build.gradle

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,35 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
import groovy.json.JsonSlurper
9+
810
plugins {
911
id("com.facebook.react")
1012
alias(libs.plugins.android.application)
1113
alias(libs.plugins.kotlin.android)
1214
}
1315

16+
def reactNativeVersionRequireNewArchEnabled(reactNativeDirPath) {
17+
def reactNativePackageJson = "$reactNativeDirPath/package.json"
18+
def slurper = new JsonSlurper()
19+
def jsonData = slurper.parse(new File(reactNativePackageJson))
20+
def rnVersion = jsonData.version
21+
def regexPattern = /^(\d+)\.(\d+)\.(\d+)(?:-(\w+(?:[-.]\d+)?))?$/
22+
23+
24+
if (rnVersion =~ regexPattern) {
25+
def result = (rnVersion =~ regexPattern).findAll().first()
26+
27+
def major = result[1].toInteger()
28+
if (major > 0 && major < 1000) {
29+
return true
30+
}
31+
}
32+
return false
33+
}
34+
35+
def reactNativeDirPath = "$rootDir/packages/react-native"
36+
def isNewArchEnabled = project.property("newArchEnabled") == "true" || reactNativeVersionRequireNewArchEnabled(reactNativeDirPath)
1437
/**
1538
* This is the configuration block to customize your React Native Android app.
1639
* By default you don't need to apply any configuration, just uncomment the lines you need.
@@ -20,11 +43,11 @@ react {
2043
// The root of your project, i.e. where "package.json" lives. Default is '..'
2144
root = file("../../")
2245
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
23-
reactNativeDir = file("$rootDir/packages/react-native")
46+
reactNativeDir = file(reactNativeDirPath)
2447
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
2548
codegenDir = file("$rootDir/node_modules/@react-native/codegen")
2649
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
27-
cliFile = file("$rootDir/packages/react-native/cli.js")
50+
cliFile = file("$reactNativeDirPath/cli.js")
2851

2952
/* Variants */
3053
// The list of variants to that are debuggable. For those we're going to
@@ -54,7 +77,7 @@ react {
5477

5578
/* Hermes Commands */
5679
// The hermes compiler command to run. By default it is 'hermesc'
57-
hermesCommand = "$rootDir/packages/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc"
80+
hermesCommand = "$reactNativeDirPath/ReactAndroid/hermes-engine/build/hermes/bin/hermesc"
5881
enableHermesOnlyInVariants = ["hermesDebug", "hermesRelease"]
5982
}
6083

@@ -148,7 +171,7 @@ android {
148171
java {
149172
// SampleTurboModule.
150173
srcDirs += [
151-
"$rootDir/packages/react-native/ReactCommon/react/nativemodule/samples/platform/android",
174+
"$reactNativeDirPath/ReactCommon/react/nativemodule/samples/platform/android",
152175
]
153176
}
154177
}
@@ -172,7 +195,7 @@ android {
172195
cmake {
173196
// RN Tester is doing custom linking of C++ libraries therefore needs
174197
// a dedicated CMakeLists.txt file.
175-
if (project.property("newArchEnabled") == "true") {
198+
if (isNewArchEnabled) {
176199
path("src/main/jni/CMakeLists.txt")
177200
}
178201
}
@@ -189,7 +212,7 @@ afterEvaluate {
189212
// As we're building 4 native flavors in parallel, there is clash on the `.cxx/Debug` and
190213
// `.cxx/Release` folder where the CMake intermediates are stored.
191214
// We fixing this by instructing Gradle to always mergeLibs after they've been built.
192-
if (project.property("newArchEnabled") == "true") {
215+
if (isNewArchEnabled) {
193216
mergeHermesDebugNativeLibs.mustRunAfter(externalNativeBuildJscDebug)
194217
mergeHermesReleaseNativeLibs.mustRunAfter(externalNativeBuildJscRelease)
195218
mergeJscDebugNativeLibs.mustRunAfter(externalNativeBuildHermesDebug)

0 commit comments

Comments
 (0)