From 2c16b45a25d489a07749d075dabba47b59010d52 Mon Sep 17 00:00:00 2001 From: Fabrizio Cucci Date: Fri, 15 Mar 2024 09:32:26 -0700 Subject: [PATCH] Fix auto-download of CMake Summary: Changelog: [Internal] This is an attempt to fix [#39783](https://github.com/facebook/react-native/issues/39783). It seems that when `configureBuildForHermes` runs (as part of `yarn android` in `rn-tester`) cmake may not be available and is not installed automatically: * for 3p, even after installing the latest Android Studio, cmake 3.22.1 is not available by default but needs to be installed manually * for Meta employees, same story when looking at the `ANDROID_HOME` set by the `setup_fb4a.sh` in the `.zshrc` file: ``` # added by setup_fb4a.sh export ANDROID_SDK=/opt/android_sdk export ANDROID_NDK_REPOSITORY=/opt/android_ndk export ANDROID_HOME=${ANDROID_SDK} export PATH=${PATH}:${ANDROID_SDK}/emulator:${ANDROID_SDK}/tools:${ANDROID_SDK}/tools/bin:${ANDROID_SDK}/platform-tools ``` This diff introduces an explicit task to install cmake. ### ALTERNATIVE 1 See D54897379. ### ALTERNATIVE 2 Suggested by cortinico: > Create a mini module called :packages:react-native:ReactAndroid:hermes-engine:cmake-downloader which just triggers an empty cmake build via Android SDK Differential Revision: D54859484 --- .../hermes-engine/build.gradle.kts | 51 +++++++++++++------ 1 file changed, 35 insertions(+), 16 deletions(-) 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",