-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tools-apple): add primitives for build and run commands (#3305)
- Loading branch information
Showing
22 changed files
with
771 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@rnx-kit/tools-apple": patch | ||
--- | ||
|
||
Added primitives for building 'build' and 'run' commands |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
"@rnx-kit/cli": patch | ||
--- | ||
|
||
Added experimental commands for building and running iOS/macOS apps. These need | ||
more testing, preferably outside of rnx-kit, and as such, will not be publicly | ||
available. But if you are willing to keep patches, there are ways to access | ||
them. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import type { Config } from "@react-native-community/cli-types"; | ||
import { InvalidArgumentError } from "commander"; | ||
import type { | ||
BuildConfiguration, | ||
DeviceType, | ||
InputParams, | ||
} from "./build/apple"; | ||
import { buildIOS } from "./build/ios"; | ||
import { buildMacOS } from "./build/macos"; | ||
|
||
function asConfiguration(configuration: string): BuildConfiguration { | ||
switch (configuration) { | ||
case "Debug": | ||
case "Release": | ||
return configuration; | ||
|
||
default: | ||
throw new InvalidArgumentError("Expected 'Debug' or 'Release'."); | ||
} | ||
} | ||
|
||
function asDestination(destination: string): DeviceType { | ||
switch (destination) { | ||
case "device": | ||
case "emulator": | ||
case "simulator": | ||
return destination; | ||
|
||
default: | ||
throw new InvalidArgumentError( | ||
"Expected 'device', 'emulator', or 'simulator'." | ||
); | ||
} | ||
} | ||
|
||
function asSupportedPlatform(platform: string): InputParams["platform"] { | ||
switch (platform) { | ||
case "ios": | ||
case "macos": | ||
case "visionos": | ||
return platform; | ||
default: | ||
throw new InvalidArgumentError( | ||
"Supported platforms: 'ios', 'macos', 'visionos'." | ||
); | ||
} | ||
} | ||
|
||
export function rnxBuild( | ||
_argv: string[], | ||
config: Config, | ||
buildParams: InputParams | ||
) { | ||
switch (buildParams.platform) { | ||
case "ios": | ||
case "visionos": | ||
return buildIOS(config, buildParams); | ||
case "macos": | ||
return buildMacOS(config, buildParams); | ||
} | ||
} | ||
|
||
export const rnxBuildCommand = { | ||
name: "rnx-build", | ||
description: | ||
"Build your native app for testing in emulator/simulator or on device", | ||
func: rnxBuild, | ||
options: [ | ||
{ | ||
name: "--platform <string>", | ||
description: "Target platform", | ||
parse: asSupportedPlatform, | ||
}, | ||
{ | ||
name: "--workspace <string>", | ||
description: | ||
"Path, relative to project root, of the Xcode workspace to build (macOS only)", | ||
}, | ||
{ | ||
name: "--scheme <string>", | ||
description: "Name of scheme to build", | ||
}, | ||
{ | ||
name: "--configuration <string>", | ||
description: | ||
"Build configuration for building the app; 'Debug' or 'Release'", | ||
default: "Debug", | ||
parse: asConfiguration, | ||
}, | ||
{ | ||
name: "--destination <string>", | ||
description: | ||
"Destination of the built app; 'device', 'emulator', or 'simulator'", | ||
default: "simulator", | ||
parse: asDestination, | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import type { Ora } from "ora"; | ||
|
||
// Copy of types from `@rnx-kit/tools-apple`. If Jest wasn't such a pain to | ||
// configure, we would have added an `import type` at the top instead: | ||
// | ||
// import type { BuildParams } from "@rnx-kit/tools-apple" with { "resolution-mode": "import" }; | ||
// | ||
// But Jest doesn't like import attributes and it doesn't matter if we add | ||
// `@babel/plugin-syntax-import-attributes` in the config. | ||
// | ||
// TOOD: Remove the `DeviceType`, `BuildConfiguration` and `BuildParams` when we | ||
// can migrate away from Jest in this package. | ||
export type DeviceType = "device" | "emulator" | "simulator"; | ||
export type BuildConfiguration = "Debug" | "Release"; | ||
type BuildParams = | ||
| { | ||
platform: "ios" | "visionos"; | ||
scheme?: string; | ||
destination?: DeviceType; | ||
configuration?: BuildConfiguration; | ||
archs?: string; | ||
isBuiltRemotely?: boolean; | ||
} | ||
| { | ||
platform: "macos"; | ||
scheme?: string; | ||
configuration?: BuildConfiguration; | ||
isBuiltRemotely?: boolean; | ||
}; | ||
|
||
export type BuildArgs = { | ||
xcworkspace: string; | ||
args: string[]; | ||
}; | ||
|
||
export type BuildResult = BuildArgs | number | null; | ||
|
||
export type InputParams = BuildParams & { | ||
device?: string; | ||
workspace?: string; | ||
}; | ||
|
||
export function runBuild( | ||
xcworkspace: string, | ||
buildParams: BuildParams, | ||
logger: Ora | ||
): Promise<BuildResult> { | ||
return import("@rnx-kit/tools-apple").then(({ xcodebuild }) => { | ||
return new Promise<BuildResult>((resolve) => { | ||
logger.start("Building..."); | ||
|
||
const errors: Buffer[] = []; | ||
const proc = xcodebuild(xcworkspace, buildParams, (text) => { | ||
const current = logger.text; | ||
logger.info(text); | ||
logger.start(current); | ||
}); | ||
|
||
proc.stdout.on("data", () => (logger.text += ".")); | ||
proc.stderr.on("data", (data) => errors.push(data)); | ||
|
||
proc.on("close", (code) => { | ||
if (code === 0) { | ||
logger.succeed("Build succeeded"); | ||
resolve({ xcworkspace, args: proc.spawnargs }); | ||
} else { | ||
logger.fail(Buffer.concat(errors).toString()); | ||
process.exitCode = code ?? 1; | ||
resolve(code); | ||
} | ||
}); | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import type { Config } from "@react-native-community/cli-types"; | ||
import * as path from "node:path"; | ||
import ora from "ora"; | ||
import type { BuildResult, InputParams } from "./apple"; | ||
import { runBuild } from "./apple"; | ||
|
||
export function buildIOS( | ||
config: Config, | ||
buildParams: InputParams, | ||
logger = ora() | ||
): Promise<BuildResult> { | ||
const { platform } = buildParams; | ||
const { sourceDir, xcodeProject } = config.project[platform] ?? {}; | ||
if (!sourceDir || !xcodeProject) { | ||
const root = platform.substring(0, platform.length - 2); | ||
logger.fail(`No ${root}OS project was found`); | ||
process.exitCode = 1; | ||
return Promise.resolve(1); | ||
} | ||
|
||
const { name, path: projectDir } = xcodeProject; | ||
if (!name?.endsWith(".xcworkspace")) { | ||
logger.fail( | ||
"No Xcode workspaces were found; did you forget to run `pod install`?" | ||
); | ||
process.exitCode = 1; | ||
return Promise.resolve(1); | ||
} | ||
|
||
const xcworkspace = path.resolve(sourceDir, projectDir, name); | ||
return runBuild(xcworkspace, buildParams, logger); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import type { Config } from "@react-native-community/cli-types"; | ||
import * as fs from "node:fs"; | ||
import * as path from "node:path"; | ||
import ora from "ora"; | ||
import type { BuildResult, InputParams } from "./apple"; | ||
import { runBuild } from "./apple"; | ||
|
||
function findXcodeWorkspaces(searchDir: string) { | ||
return fs.existsSync(searchDir) | ||
? fs.readdirSync(searchDir).filter((file) => file.endsWith(".xcworkspace")) | ||
: []; | ||
} | ||
|
||
export function buildMacOS( | ||
_config: Config, | ||
{ workspace, ...buildParams }: InputParams, | ||
logger = ora() | ||
): Promise<BuildResult> { | ||
if (workspace) { | ||
return runBuild(workspace, buildParams, logger); | ||
} | ||
|
||
const sourceDir = "macos"; | ||
const workspaces = findXcodeWorkspaces(sourceDir); | ||
if (workspaces.length === 0) { | ||
logger.fail( | ||
"No Xcode workspaces were found; specify an Xcode workspace with `--workspace`" | ||
); | ||
process.exitCode = 1; | ||
return Promise.resolve(1); | ||
} | ||
|
||
if (workspaces.length > 1) { | ||
logger.fail( | ||
`Multiple Xcode workspaces were found; picking the first one: ${workspaces.join(", ")}` | ||
); | ||
logger.fail( | ||
"If this is wrong, specify another workspace with `--workspace`" | ||
); | ||
} | ||
|
||
return runBuild(path.join(sourceDir, workspaces[0]), buildParams, logger); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import type { Config } from "@react-native-community/cli-types"; | ||
import { rnxBuildCommand } from "./build"; | ||
import type { InputParams } from "./build/apple"; | ||
import { runIOS } from "./run/ios"; | ||
import { runMacOS } from "./run/macos"; | ||
|
||
export function rnxRun( | ||
_argv: string[], | ||
config: Config, | ||
buildParams: InputParams | ||
) { | ||
switch (buildParams.platform) { | ||
case "ios": | ||
case "visionos": | ||
return runIOS(config, buildParams); | ||
case "macos": | ||
return runMacOS(config, buildParams); | ||
default: | ||
// @ts-expect-error Safe guard against user input | ||
console.error(`Unsupported platform: ${buildParams.platform}`); | ||
process.exitCode = 1; | ||
return Promise.resolve(); | ||
} | ||
} | ||
|
||
export const rnxRunCommand = { | ||
name: "rnx-run", | ||
description: | ||
"Build and run your native app for testing in emulator/simulator or on device", | ||
func: rnxRun, | ||
options: [ | ||
...rnxBuildCommand.options, | ||
{ | ||
name: "--device <string>", | ||
description: "The name of the device to launch the app in", | ||
}, | ||
], | ||
}; |
Oops, something went wrong.