From ee617e0e031bb58e1d1a9954c50f5db45ebfcabd Mon Sep 17 00:00:00 2001 From: Blake Friedman Date: Wed, 12 Jun 2024 08:12:35 -0700 Subject: [PATCH] Fix cli assemble, build & install (#44902) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44902 Add support for building (assembling) a React Native Android project. Changelog: [General][Added] core-cli-utils Android support Differential Revision: D58287783 --- .../core-cli-utils/src/private/android.js | 155 +++++++++--------- 1 file changed, 74 insertions(+), 81 deletions(-) diff --git a/packages/core-cli-utils/src/private/android.js b/packages/core-cli-utils/src/private/android.js index 9117f9130f4143..71316a8c055b8e 100644 --- a/packages/core-cli-utils/src/private/android.js +++ b/packages/core-cli-utils/src/private/android.js @@ -12,102 +12,92 @@ import type {Task} from './types'; import type {ExecaPromise} from 'execa'; -import {isWindows, task, toPascalCase} from './utils'; +import {isWindows, task} from './utils'; import execa from 'execa'; -type AndroidBuildMode = 'debug' | 'release'; +type AndroidBuildMode = 'Debug' | 'Release'; -type AndroidBuild = { - sourceDir: string, - appName: string, +type Path = string; +type Args = $ReadOnlyArray; + +type Config = { + cwd: Path, + hermes?: boolean, mode: AndroidBuildMode, - gradleArgs?: Array, + name: string, + newArchitecture?: boolean, + sdk?: Path, }; -function gradle(cwd: string, ...args: string[]): ExecaPromise { +function gradle( + taskName: string, + args: Args, + options: {cwd: string, env?: {[k: string]: string | void}}, +): ExecaPromise { const gradlew = isWindows ? 'gradlew.bat' : './gradlew'; - return execa(gradlew, args, { - cwd, - stdio: 'inherit', + return execa(gradlew, [taskName, ...args], { + cwd: options.cwd, + env: options.env, }); } -// -// Gradle Task wrappers -// - -/** - * Assembles an Android app using Gradle - */ -export const assemble = ( - cwd: string, - appName: string, - mode: AndroidBuildMode, - ...args: $ReadOnlyArray -): ExecaPromise => - gradle(cwd, `${appName}:assemble${toPascalCase(mode)}`, ...args); - -/** - * Assembles and tests an Android app using Gradle - */ -export const build = ( - cwd: string, - appName: string, - mode: AndroidBuildMode, - ...args: $ReadOnlyArray -): ExecaPromise => - gradle(cwd, `${appName}:build${toPascalCase(mode)}`, ...args); - -/** - * Installs an Android app using Gradle - */ -export const install = ( - cwd: string, - appName: string, - mode: AndroidBuildMode, - ...args: $ReadOnlyArray -): ExecaPromise => - gradle(cwd, `${appName}:install${toPascalCase(mode)}`, ...args); +function androidSdkPath(sdk?: string): string { + return sdk ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK ?? ''; +} -/** - * Runs a custom Gradle task if your frameworks needs aren't handled by assemble, build or install. - */ -export const customTask = ( - cwd: string, - customTaskName: string, - ...args: $ReadOnlyArray -): ExecaPromise => gradle(cwd, customTaskName, ...args); +function boolToStr(value: boolean): string { + return value ? 'true' : 'false'; +} const FIRST = 1; // // Android Tasks // -type AndroidTasks = { - assemble: ( - options: AndroidBuild, - ...args: $ReadOnlyArray - ) => {run: Task}, - build: ( - options: AndroidBuild, - ...args: $ReadOnlyArray - ) => {run: Task}, - install: ( - options: AndroidBuild, - ...args: $ReadOnlyArray - ) => {run: Task}, -}; - -export const tasks: AndroidTasks = { - assemble: (options: AndroidBuild, ...gradleArgs: $ReadOnlyArray) => ({ - run: task(FIRST, 'Assemble Android App', () => - assemble(options.sourceDir, options.appName, options.mode, ...gradleArgs), - ), +export const tasks = ( + config: Config, +): ({ + assemble: (...gradleArgs: Args) => { + run: Task, + }, + build: (...gradleArgs: Args) => { + run: Task, + }, + install: (...gradleArgs: Args) => { + run: Task, + }, +}) => ({ + assemble: (...gradleArgs: Args) => ({ + run: task(FIRST, 'Assemble Android App', () => { + const args = []; + if (config.hermes != null) { + args.push(`-PhermesEnabled=${boolToStr(config.hermes)}`); + } + if (config.newArchitecture != null) { + args.push(`-PnewArchEnable=${boolToStr(config.newArchitecture)}`); + } + args.push(...gradleArgs); + return gradle(`${config.name}:assemble${config.mode}`, gradleArgs, { + cwd: config.cwd, + env: {ANDROID_HOME: androidSdkPath(config.sdk)}, + }); + }), }), - build: (options: AndroidBuild, ...gradleArgs: $ReadOnlyArray) => ({ - run: task(FIRST, 'Assembles and tests Android App', () => - build(options.sourceDir, options.appName, options.mode, ...gradleArgs), - ), + build: (...gradleArgs: Args) => ({ + run: task(FIRST, 'Assembles and tests Android App', () => { + const args = []; + if (config.hermes != null) { + args.push(`-PhermesEnabled=${boolToStr(config.hermes)}`); + } + if (config.newArchitecture != null) { + args.push(`-PnewArchEnable=${boolToStr(config.newArchitecture)}`); + } + args.push(...gradleArgs); + return gradle(`${config.name}:bundle${config.mode}`, args, { + cwd: config.cwd, + env: {ANDROID_HOME: androidSdkPath(config.sdk)}, + }); + }), }), /** * Useful extra gradle arguments: @@ -115,9 +105,12 @@ export const tasks: AndroidTasks = { * -PreactNativeDevServerPort=8081 sets the port for the installed app to point towards a Metro * server on (for example) 8081. */ - install: (options: AndroidBuild, ...gradleArgs: $ReadOnlyArray) => ({ + install: (...gradleArgs: Args) => ({ run: task(FIRST, 'Installs the assembled Android App', () => - install(options.sourceDir, options.appName, options.mode, ...gradleArgs), + gradle(`${config.name}:install${config.mode}`, gradleArgs, { + cwd: config.cwd, + env: {ANDROID_HOME: androidSdkPath(config.sdk)}, + }), ), }), @@ -125,4 +118,4 @@ export const tasks: AndroidTasks = { // a framework concern. For an example of how one could do this, please look at the community // CLI's code: // https://github.com/react-native-community/cli/blob/54d48a4e08a1aef334ae6168788e0157a666b4f5/packages/cli-platform-android/src/commands/runAndroid/index.ts#L272C1-L290C2 -}; +});