/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import org.apache.tools.ant.taskdefs.condition.Os plugins { id("de.undercouch.download") id("com.android.library") id("maven-publish") id("signing") } group = "com.facebook.react" version = parent.publishing_version def cmakeVersion = parent.cmake_version /** * We use the bundled version of CMake in the Android SDK if available, to don't force Android * users to install CMake externally. */ def findCmakePath(cmakeVersion) { def cmakeRelativePath = "/cmake/${cmakeVersion}/bin/cmake" if (System.getenv("ANDROID_SDK_ROOT") && new File("${System.getenv("ANDROID_SDK_ROOT")}/${cmakeRelativePath}").exists()) { return "${System.getenv("ANDROID_SDK_ROOT")}/${cmakeRelativePath}" } if (System.getenv("ANDROID_HOME") && new File("${System.getenv("ANDROID_HOME")}/${cmakeRelativePath}").exists()) { return "${System.getenv("ANDROID_HOME")}/${cmakeRelativePath}" } return "cmake" } def reactNativeRootDir = project(':packages:react-native:ReactAndroid').projectDir.parent; def customDownloadDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR") def downloadsDir = customDownloadDir ? new File(customDownloadDir) : new File(reactNativeRootDir, "sdks/download") // By default we are going to download and unzip hermes inside the /sdks/hermes folder // but you can provide an override for where the hermes source code is located. def overrideHermesDir = System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR") != null def hermesDir = System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR") ?: new File(reactNativeRootDir, "sdks/hermes") def hermesBuildDir = new File("$buildDir/hermes") def hermesVersion = "main" def hermesVersionFile = new File(reactNativeRootDir, "sdks/.hermesversion") if (hermesVersionFile.exists()) { hermesVersion = hermesVersionFile.text } def ndkBuildJobs = Runtime.runtime.availableProcessors().toString() def prefabHeadersDir = new File("$buildDir/prefab-headers") // By setting REACT_NATIVE_HERMES_SKIP_PREFAB you can skip prefab publishing, to // reduce the size of the Hermes published .AAR. def skipPrefabPublishing = System.getenv("REACT_NATIVE_HERMES_SKIP_PREFAB") != null // We inject the JSI directory used inside the Hermes build with the -DJSI_DIR config. def jsiDir = new File(reactNativeRootDir, "ReactCommon/jsi") task downloadHermes(type: Download) { src("https://github.com/facebook/hermes/tarball/${hermesVersion}") onlyIfModified(true) overwrite(true) useETag("all") retries(5) dest(new File(downloadsDir, "hermes.tar.gz")) } task installArchives { dependsOn("publishAllPublicationsToNpmRepository") } task unzipHermes(dependsOn: downloadHermes, type: Copy) { from(tarTree(downloadHermes.dest)) { eachFile { file -> // We flatten the unzip as the tarball contains a `facebook-hermes-` // folder at the top level. if (file.relativePath.segments.size() > 1) { file.relativePath = new org.gradle.api.file.RelativePath(!file.isDirectory(), file.relativePath.segments.drop(1)) } } } into(hermesDir) } task configureBuildForHermes(type: Exec) { workingDir(hermesDir) commandLine(windowsAwareCommandLine(findCmakePath(cmakeVersion), Os.isFamily(Os.FAMILY_WINDOWS) ? "-GNMake Makefiles" : "", "-S", ".", "-B", hermesBuildDir.toString(), "-DJSI_DIR=" + jsiDir.absolutePath)) } task buildHermes(dependsOn: configureBuildForHermes, type: Exec) { workingDir(hermesDir) commandLine(windowsAwareCommandLine(findCmakePath(cmakeVersion), "--build", hermesBuildDir.toString(), "--target", "hermesc", "-j", ndkBuildJobs)) } task prepareHeadersForPrefab(type: Copy) { from("$hermesDir/API") from("$hermesDir/public") include("**/*.h") exclude("jsi/**") into(prefabHeadersDir) } static def windowsAwareCommandLine(String... commands) { def newCommands = [] if (Os.isFamily(Os.FAMILY_WINDOWS)) { newCommands = ['cmd', '/c'] } newCommands.addAll(commands) return newCommands } def reactNativeArchitectures() { def value = project.getProperties().get("reactNativeArchitectures") return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } repositories { // Normally RNGP will set repositories for all modules, // but when consumed from source, we need to re-declare // those repositories as there is no app module there. mavenCentral() google() } android { compileSdkVersion 31 buildToolsVersion = "31.0.0" namespace "com.facebook.hermes" // Used to override the NDK path/version on internal CI or by allowing // users to customize the NDK path/version from their root project (e.g. for M1 support) if (rootProject.hasProperty("ndkPath")) { ndkPath rootProject.ext.ndkPath } if (rootProject.hasProperty("ndkVersion")) { ndkVersion rootProject.ext.ndkVersion } defaultConfig { minSdkVersion 21 externalNativeBuild { cmake { arguments "-DHERMES_IS_ANDROID=True" arguments "-DANDROID_STL=c++_shared" arguments "-DANDROID_PIE=True" arguments "-DIMPORT_HERMESC=${new File(hermesBuildDir, "ImportHermesc.cmake").toString()}" arguments "-DJSI_DIR=${jsiDir}" arguments "-DHERMES_SLOW_DEBUG=False" arguments "-DHERMES_BUILD_SHARED_JSI=True" arguments "-DHERMES_RELEASE_VERSION=for RN ${version}" // We intentionally build Hermes with Intl support only. This is to simplify // the build setup and to avoid overcomplicating the build-type matrix. arguments "-DHERMES_ENABLE_INTL=True" // Due to https://github.com/android/ndk/issues/1693 we're losing Android // specific compilation flags. This can be removed once we moved to NDK 25/26 arguments "-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON" targets "libhermes" } } ndk { abiFilters (*reactNativeArchitectures()) } } externalNativeBuild { cmake { version cmakeVersion path "$hermesDir/CMakeLists.txt" } } buildTypes { debug { externalNativeBuild { cmake { // JS developers aren't VM developers. // Therefore we're passing as build type Release, to provide a faster build. // This has the (unlucky) side effect of letting AGP call the build // tasks `configureCMakeRelease` while is actually building the debug flavor. arguments "-DCMAKE_BUILD_TYPE=Release" // Adding -O3 to patch the issue here: https://github.com/android/ndk/issues/1740#issuecomment-1198438260 cppFlags "-O3" } } } release { externalNativeBuild { cmake { arguments "-DCMAKE_BUILD_TYPE=MinSizeRel" // For release builds, we don't want to enable the Hermes Debugger. arguments "-DHERMES_ENABLE_DEBUGGER=False" } } } } sourceSets { main { manifest.srcFile "$hermesDir/android/hermes/src/main/AndroidManifest.xml" java.srcDirs = [ "$hermesDir/lib/Platform/Intl/java" ] } } buildFeatures { prefab true prefabPublishing !skipPrefabPublishing } dependencies { implementation("com.facebook.fbjni:fbjni:0.3.0") implementation("com.facebook.soloader:soloader:0.10.4") implementation("com.facebook.yoga:proguard-annotations:1.19.0") implementation("androidx.annotation:annotation:1.3.0") } packagingOptions { exclude "**/libc++_shared.so" exclude "**/libjsi.so" exclude "**/libfbjni.so" } publishing { multipleVariants { withSourcesJar() allVariants() } } prefab { libhermes { headers prefabHeadersDir.absolutePath libraryName "libhermes" } } } afterEvaluate { if (!overrideHermesDir) { // If you're not specifying a Hermes Path override, we want to // download/unzip Hermes from Github then. configureBuildForHermes.dependsOn(unzipHermes) prepareHeadersForPrefab.dependsOn(unzipHermes) } preBuild.dependsOn(buildHermes) preBuild.dependsOn(prepareHeadersForPrefab) prepareHeadersForPrefab.dependsOn(buildHermes) } /* Publishing Configuration */ apply from: "../publish.gradle" // We need to override the artifact ID as this project is called `hermes-engine` but // the maven coordinates are on `hermes-android`. // Please note that the original coordinates, `hermes-engine`, have been voided // as they caused https://github.com/facebook/react-native/issues/35210 publishing { publications { getByName("release") { artifactId 'hermes-android' } } }