diff --git a/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts b/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts index 17eb24f355b5..1e2552ef6a7c 100644 --- a/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts +++ b/packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts @@ -20,22 +20,27 @@ group = "com.facebook.react" version = parent?.properties?.get("publishing_version")?.toString()!! val cmakeVersion = parent?.properties?.get("cmake_version")?.toString()!! - -/** - * We use the bundled version of CMake in the Android SDK if available, to don't force Android users - * to install CMake externally. - */ -fun findCmakePath(cmakeVersion: String): String { - val cmakeRelativePath = "cmake/${cmakeVersion}/bin/cmake" - if (System.getenv("ANDROID_SDK_ROOT") != null && - File("${System.getenv("ANDROID_SDK_ROOT")}/${cmakeRelativePath}").exists()) { - return "${System.getenv("ANDROID_SDK_ROOT")}/${cmakeRelativePath}" +val cmakePath = "${getSDKPath()}/cmake/$cmakeVersion" +val cmakeBinaryPath = "${cmakePath}/bin/cmake" + +fun getSDKPath(): String { + val androidSdkRoot = System.getenv("ANDROID_SDK_ROOT") + val androidHome = System.getenv("ANDROID_HOME") + return when { + !androidSdkRoot.isNullOrBlank() -> androidSdkRoot + !androidHome.isNullOrBlank() -> androidHome + else -> throw IllegalStateException("Neither ANDROID_SDK_ROOT nor ANDROID_HOME is set.") } - if (System.getenv("ANDROID_HOME") != null && - File("${System.getenv("ANDROID_HOME")}/${cmakeRelativePath}").exists()) { - return "${System.getenv("ANDROID_HOME")}/${cmakeRelativePath}" +} + +fun getSDKManagerPath(): String { + val metaSdkManagerPath = File("${getSDKPath()}/cmdline-tools/latest/bin/sdkmanager") + val ossSdkManagerPath = File("${getSDKPath()}/tools/bin/sdkmanager") + return when { + metaSdkManagerPath.exists() -> metaSdkManagerPath.absolutePath + ossSdkManagerPath.exists() -> ossSdkManagerPath.absolutePath + else -> throw GradleException("Could not find sdkmanager executable.") } - return "cmake" } val reactNativeRootDir = project(":packages:react-native:ReactAndroid").projectDir.parent @@ -111,14 +116,28 @@ val unzipHermes by into(hermesDir) } +// NOTE: ideally, we would like CMake to be installed automatically by the `externalNativeBuild` +// below. To do that, we would need the various `ConfigureCMake*` tasks to run *before* +// `configureBuildForHermes` and `buildHermesC` so that CMake is available for their run. But the +// `ConfigureCMake*` tasks depend upon the `ImportHermesc.cmake` file which is actually generated by +// the two tasks mentioned before, so we install CMake manually to break the circular dependency. + +val installCMake by + tasks.registering(Exec::class) { + onlyIf { !File(cmakePath).exists() } + commandLine( + windowsAwareCommandLine(getSDKManagerPath(), "--install", "cmake;${cmakeVersion}")) + } + val configureBuildForHermes by tasks.registering(Exec::class) { + dependsOn(installCMake) workingDir(hermesDir) inputs.dir(hermesDir) outputs.files(hermesBuildOutputFileTree) commandLine( windowsAwareCommandLine( - findCmakePath(cmakeVersion), + cmakeBinaryPath, if (Os.isFamily(Os.FAMILY_WINDOWS)) "-GNMake Makefiles" else "", "-S", ".", @@ -135,7 +154,7 @@ val buildHermesC by outputs.file(hermesCOutputBinary) commandLine( windowsAwareCommandLine( - findCmakePath(cmakeVersion), + cmakeBinaryPath, "--build", hermesBuildDir.toString(), "--target",