Skip to content

Commit

Permalink
feat: build-ios command (#1744)
Browse files Browse the repository at this point in the history
* feat: add buildIOS command

* refactor: extract shared functions from build-ios

* refactor: extract shared functions from run-ios

* feat: default build ios to generic destination

* feat: update build-ios examples

* docs: update build-ios docs

* refactor: use shared build flags

* feat: rename configuratio to mode

* feat: add deprecation note

* fix: ts issue

* docs: reword xcconfig argument for build ios

* refactor: improvements after code review

* fix: pass mode to iOS install command

Co-authored-by: Paweł Kata <gmabber@gmail.com>
Co-authored-by: Adam Trzcinski <trzcinski.adam@gmail.com>
  • Loading branch information
3 people authored Jan 25, 2023
1 parent ee15ba3 commit e932dd6
Show file tree
Hide file tree
Showing 15 changed files with 814 additions and 249 deletions.
74 changes: 73 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ React Native CLI comes with following commands:
- [`run-android`](#run-android)
- [`build-android`](#build-android)
- [`run-ios`](#run-ios)
- [`build-ios`](#build-ios)
- [`start`](#start)
- [`upgrade`](#upgrade)
- [`profile-hermes`](#profile-hermes)
Expand Down Expand Up @@ -429,6 +430,7 @@ react-native run-ios [options]

Builds your app and starts it on iOS simulator.


#### Options

#### `--simulator <simulator_name>`
Expand All @@ -454,7 +456,77 @@ react-native run-ios --simulator "iPhone XS Max"

#### `--configuration <string>`

Explicitly set the scheme configuration to use default: 'Debug'.
[Deprecated] Explicitly set the scheme configuration to use default: 'Debug'.

#### `--scheme <string>`

Explicitly set Xcode scheme to use.

#### `--device [string]`

Explicitly set device to use by name. The value is not required if you have a single device connected.

#### `--udid <string>`

Explicitly set device to use by udid.

#### `--no-packager`

Do not launch packager while building.

#### `--verbose`

Do not use `xcbeautify` or `xcpretty` even if installed.

#### `--port <number>`

Runs packager on specified port.

Default: `process.env.RCT_METRO_PORT || 8081`

#### `--xcconfig <string>`

Explicitly set `xcconfig` to use in build.

#### `--buildFolder <string>`

Location for iOS build artifacts. Corresponds to Xcode's `-derivedDataPath`.

### `build-ios`

Usage:

```sh
react-native build-ios [options]
```

Builds IOS app.

#### Options
#### `--simulator <simulator_name>`

> default: iPhone 14
Explicitly set the simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version, e.g. `"iPhone 6 (10.0)"`.

Notes: If selected simulator does not exist, cli will try to run fallback simulators in following order:

- `iPhone 14`
- `iPhone 13`
- `iPhone 12`
- `iPhone 11`

Notes: `simulator_name` must be a valid iOS simulator name. If in doubt, open your AwesomeApp/ios/AwesomeApp.xcodeproj folder on XCode and unroll the dropdown menu containing the simulator list. The dropdown menu is situated on the right hand side of the play button (top left corner).

Example: this will launch your project directly onto the iPhone XS Max simulator:

```sh
react-native run-ios --simulator "iPhone XS Max"
```

#### `--configuration <string>`

[Deprecated] Explicitly set the scheme configuration to use default: 'Debug'.

#### `--scheme <string>`

Expand Down
164 changes: 164 additions & 0 deletions packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import child_process, {
ChildProcess,
SpawnOptionsWithoutStdio,
} from 'child_process';
import chalk from 'chalk';
import {IOSProjectInfo} from '@react-native-community/cli-types';
import {logger, CLIError} from '@react-native-community/cli-tools';
import {getLoader} from '@react-native-community/cli-tools';

export type BuildFlags = {
mode: string;
packager: boolean;
verbose: boolean;
xcconfig?: string;
buildFolder?: string;
port: number;
terminal: string | undefined;
};

export function buildProject(
xcodeProject: IOSProjectInfo,
udid: string | undefined,
scheme: string,
args: BuildFlags,
): Promise<string> {
return new Promise((resolve, reject) => {
const xcodebuildArgs = [
xcodeProject.isWorkspace ? '-workspace' : '-project',
xcodeProject.name,
...(args.xcconfig ? ['-xcconfig', args.xcconfig] : []),
...(args.buildFolder ? ['-derivedDataPath', args.buildFolder] : []),
'-configuration',
args.mode,
'-scheme',
scheme,
'-destination',
udid
? `id=${udid}`
: args.mode === 'Debug'
? 'generic/platform=iOS Simulator'
: 'generic/platform=iOS',
];
const loader = getLoader();
logger.info(
`Building ${chalk.dim(
`(using "xcodebuild ${xcodebuildArgs.join(' ')}")`,
)}`,
);
let xcodebuildOutputFormatter: ChildProcess | any;
if (!args.verbose) {
if (xcbeautifyAvailable()) {
xcodebuildOutputFormatter = child_process.spawn('xcbeautify', [], {
stdio: ['pipe', process.stdout, process.stderr],
});
} else if (xcprettyAvailable()) {
xcodebuildOutputFormatter = child_process.spawn('xcpretty', [], {
stdio: ['pipe', process.stdout, process.stderr],
});
}
}
const buildProcess = child_process.spawn(
'xcodebuild',
xcodebuildArgs,
getProcessOptions(args),
);
let buildOutput = '';
let errorOutput = '';
buildProcess.stdout.on('data', (data: Buffer) => {
const stringData = data.toString();
buildOutput += stringData;
if (xcodebuildOutputFormatter) {
xcodebuildOutputFormatter.stdin.write(data);
} else {
if (logger.isVerbose()) {
logger.debug(stringData);
} else {
loader.start(
`Building the app${'.'.repeat(buildOutput.length % 10)}`,
);
}
}
});

buildProcess.stderr.on('data', (data: Buffer) => {
errorOutput += data;
});
buildProcess.on('close', (code: number) => {
if (xcodebuildOutputFormatter) {
xcodebuildOutputFormatter.stdin.end();
} else {
loader.stop();
}
if (code !== 0) {
reject(
new CLIError(
`
Failed to build iOS project.
We ran "xcodebuild" command but it exited with error code ${code}. To debug build
logs further, consider building your app with Xcode.app, by opening
${xcodeProject.name}.
`,
xcodebuildOutputFormatter
? undefined
: buildOutput + '\n' + errorOutput,
),
);
return;
}
logger.success('Successfully built the app');
resolve(buildOutput);
});
});
}

function xcbeautifyAvailable() {
try {
child_process.execSync('xcbeautify --version', {
stdio: [0, 'pipe', 'ignore'],
});
} catch (error) {
return false;
}
return true;
}

function xcprettyAvailable() {
try {
child_process.execSync('xcpretty --version', {
stdio: [0, 'pipe', 'ignore'],
});
} catch (error) {
return false;
}
return true;
}

function getProcessOptions({
packager,
terminal,
port,
}: {
packager: boolean;
terminal: string | undefined;
port: number;
}): SpawnOptionsWithoutStdio {
if (packager) {
return {
env: {
...process.env,
RCT_TERMINAL: terminal,
RCT_METRO_PORT: port.toString(),
},
};
}

return {
env: {
...process.env,
RCT_TERMINAL: terminal,
RCT_NO_LAUNCH_PACKAGER: 'true',
},
};
}
Loading

0 comments on commit e932dd6

Please sign in to comment.