or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

build-configuration.mdbuild-phases.mdfile-management.mdframeworks-libraries.mdgroup-management.mdindex.mdproject-parsing.mdtarget-management.md

build-phases.mddocs/

0

# Build Phases

1

2

Build phase management for controlling compilation, resource copying, framework linking, and custom script execution with support for all Xcode build phase types.

3

4

## Capabilities

5

6

### Custom Build Phase Creation

7

8

Create custom build phases for advanced build workflows.

9

10

```javascript { .api }

11

/**

12

* Add custom build phase to target

13

* Creates and configures build phase with specified files and options

14

* @param {string[]} filePathsArray - Files to include in build phase

15

* @param {string} buildPhaseType - Build phase type (PBXSourcesBuildPhase, etc.)

16

* @param {string} comment - Build phase name/comment displayed in Xcode

17

* @param {string} target - Target UUID (optional, defaults to first target)

18

* @param {object|string} options - Build phase options or folder type for copy phases

19

* @param {string} subfolderPath - Subfolder path for copy phases

20

* @returns {object} Build phase object with UUID and configuration

21

*/

22

addBuildPhase(filePathsArray, buildPhaseType, comment, target, options, subfolderPath);

23

```

24

25

**Usage Examples:**

26

27

```javascript

28

// Add custom sources build phase

29

const sourcePhase = proj.addBuildPhase(

30

['src/CustomCode.m', 'src/Helper.m'],

31

'PBXSourcesBuildPhase',

32

'Custom Sources'

33

);

34

35

// Add custom resources build phase

36

const resourcePhase = proj.addBuildPhase(

37

['assets/config.plist', 'assets/data.json'],

38

'PBXResourcesBuildPhase',

39

'Configuration Resources'

40

);

41

42

// Add copy files build phase for frameworks

43

const copyPhase = proj.addBuildPhase(

44

['Frameworks/Custom.framework'],

45

'PBXCopyFilesBuildPhase',

46

'Embed Custom Frameworks',

47

null, // Use default target

48

'frameworks', // Destination folder type

49

'$(BUILT_PRODUCTS_DIR)'

50

);

51

52

// Add shell script build phase

53

const scriptPhase = proj.addBuildPhase(

54

[], // No files for script phases

55

'PBXShellScriptBuildPhase',

56

'Run SwiftLint',

57

null,

58

{

59

shellPath: '/bin/sh',

60

shellScript: 'if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo "warning: SwiftLint not installed"\nfi',

61

inputPaths: ['$(SRCROOT)/.swiftlint.yml'],

62

outputPaths: []

63

}

64

);

65

```

66

67

### Build Phase Access

68

69

Access existing build phases and their objects.

70

71

```javascript { .api }

72

/**

73

* Get sources build phase object for target

74

* @param {string} target - Target UUID

75

* @returns {object} Sources build phase object

76

*/

77

pbxSourcesBuildPhaseObj(target);

78

79

/**

80

* Get resources build phase object for target

81

* @param {string} target - Target UUID

82

* @returns {object} Resources build phase object

83

*/

84

pbxResourcesBuildPhaseObj(target);

85

86

/**

87

* Get frameworks build phase object for target

88

* @param {string} target - Target UUID

89

* @returns {object} Frameworks build phase object

90

*/

91

pbxFrameworksBuildPhaseObj(target);

92

93

/**

94

* Get embed frameworks build phase object for target

95

* @param {string} target - Target UUID

96

* @returns {object} Embed frameworks build phase object

97

*/

98

pbxEmbedFrameworksBuildPhaseObj(target);

99

100

/**

101

* Get copy files build phase object for target

102

* @param {string} target - Target UUID

103

* @returns {object} Copy files build phase object

104

*/

105

pbxCopyfilesBuildPhaseObj(target);

106

107

/**

108

* Find build phase by group and target

109

* @param {string} group - Build phase group name

110

* @param {string} target - Target UUID

111

* @returns {string} Build phase comment key

112

*/

113

buildPhase(group, target);

114

115

/**

116

* Get build phase object by name, group, and target

117

* @param {string} name - Build phase type name

118

* @param {string} group - Build phase group

119

* @param {string} target - Target UUID

120

* @returns {object} Build phase object

121

*/

122

buildPhaseObject(name, group, target);

123

```

124

125

**Usage Examples:**

126

127

```javascript

128

// Get default build phases

129

const mainTarget = proj.getFirstTarget().uuid;

130

131

const sourcesPhase = proj.pbxSourcesBuildPhaseObj(mainTarget);

132

console.log('Sources files:', sourcesPhase.files.length);

133

134

const resourcesPhase = proj.pbxResourcesBuildPhaseObj(mainTarget);

135

console.log('Resource files:', resourcesPhase.files.length);

136

137

const frameworksPhase = proj.pbxFrameworksBuildPhaseObj(mainTarget);

138

console.log('Linked frameworks:', frameworksPhase.files.length);

139

140

// Find custom build phase

141

const customPhase = proj.buildPhaseObject('PBXShellScriptBuildPhase', 'Run Script', mainTarget);

142

if (customPhase) {

143

console.log('Script phase found:', customPhase.name);

144

}

145

```

146

147

### Build Phase File Management

148

149

Add and remove files from specific build phases.

150

151

```javascript { .api }

152

// Internal methods typically called by higher-level file management functions

153

154

/**

155

* Add file to sources build phase

156

* @param {object} file - File object to add

157

*/

158

addToPbxSourcesBuildPhase(file);

159

160

/**

161

* Remove file from sources build phase

162

* @param {object} file - File object to remove

163

*/

164

removeFromPbxSourcesBuildPhase(file);

165

166

/**

167

* Add file to resources build phase

168

* @param {object} file - File object to add

169

*/

170

addToPbxResourcesBuildPhase(file);

171

172

/**

173

* Remove file from resources build phase

174

* @param {object} file - File object to remove

175

*/

176

removeFromPbxResourcesBuildPhase(file);

177

178

/**

179

* Add file to frameworks build phase

180

* @param {object} file - File object to add

181

*/

182

addToPbxFrameworksBuildPhase(file);

183

184

/**

185

* Remove file from frameworks build phase

186

* @param {object} file - File object to remove

187

*/

188

removeFromPbxFrameworksBuildPhase(file);

189

190

/**

191

* Add file to embed frameworks build phase

192

* @param {object} file - File object to add

193

*/

194

addToPbxEmbedFrameworksBuildPhase(file);

195

196

/**

197

* Remove file from embed frameworks build phase

198

* @param {object} file - File object to remove

199

*/

200

removeFromPbxEmbedFrameworksBuildPhase(file);

201

202

/**

203

* Add file to copy files build phase

204

* @param {object} file - File object to add

205

*/

206

addToPbxCopyfilesBuildPhase(file);

207

208

/**

209

* Remove file from copy files build phase

210

* @param {object} file - File object to remove

211

*/

212

removeFromPbxCopyfilesBuildPhase(file);

213

```

214

215

**Usage Examples:**

216

217

```javascript

218

// These methods are typically called automatically by addSourceFile, addFramework, etc.

219

// but can be used directly for fine-grained control

220

221

// Add file to specific build phases manually

222

const sourceFile = { uuid: 'FILE_UUID', fileRef: 'FILE_REF_UUID', basename: 'MyFile.m' };

223

224

proj.addToPbxBuildFileSection(sourceFile); // Must add to build file section first

225

proj.addToPbxSourcesBuildPhase(sourceFile);

226

227

// Remove from build phases

228

proj.removeFromPbxSourcesBuildPhase(sourceFile);

229

proj.removeFromPbxBuildFileSection(sourceFile);

230

```

231

232

### Build Phase Types and Options

233

234

```javascript { .api }

235

/**

236

* Supported build phase types

237

*/

238

const BUILD_PHASE_TYPES = {

239

'PBXSourcesBuildPhase': 'Compile Sources',

240

'PBXResourcesBuildPhase': 'Copy Bundle Resources',

241

'PBXFrameworksBuildPhase': 'Link Binary With Libraries',

242

'PBXCopyFilesBuildPhase': 'Copy Files',

243

'PBXShellScriptBuildPhase': 'Run Script'

244

};

245

246

/**

247

* Copy files destination types

248

*/

249

const COPY_DESTINATIONS = {

250

'absolute_path': 0,

251

'wrapper': 1,

252

'executables': 6,

253

'resources': 7,

254

'frameworks': 10,

255

'shared_frameworks': 11,

256

'shared_support': 12,

257

'plugins': 13,

258

'products_directory': 16,

259

'java_resources': 15,

260

'xpc_services': 0

261

};

262

263

/**

264

* Target type to destination mapping for copy phases

265

*/

266

const DESTINATION_BY_TARGET_TYPE = {

267

'application': 'wrapper',

268

'app_extension': 'plugins',

269

'bundle': 'wrapper',

270

'command_line_tool': 'wrapper',

271

'dynamic_library': 'products_directory',

272

'framework': 'shared_frameworks',

273

'frameworks': 'frameworks',

274

'static_library': 'products_directory',

275

'unit_test_bundle': 'wrapper',

276

'watch_app': 'wrapper',

277

'watch2_app': 'products_directory',

278

'watch_extension': 'plugins',

279

'watch2_extension': 'plugins'

280

};

281

```

282

283

### Shell Script Build Phase Options

284

285

```javascript { .api }

286

/**

287

* Options for shell script build phases

288

*/

289

interface ShellScriptOptions {

290

/** Shell path (e.g., '/bin/sh', '/usr/bin/python3') */

291

shellPath: string;

292

293

/** Shell script content */

294

shellScript: string;

295

296

/** Input file paths for dependency tracking */

297

inputPaths?: string[];

298

299

/** Output file paths for dependency tracking */

300

outputPaths?: string[];

301

302

/** Show environment variables in build log */

303

showEnvVarsInLog?: boolean;

304

305

/** Run script only when installing */

306

runOnlyForDeploymentPostprocessing?: boolean;

307

}

308

```

309

310

### Copy Files Build Phase Options

311

312

```javascript { .api }

313

/**

314

* Options for copy files build phases

315

*/

316

interface CopyFilesOptions {

317

/** Destination folder type */

318

folderType: string;

319

320

/** Subfolder path within destination */

321

subfolderPath?: string;

322

323

/** Only copy when installing */

324

runOnlyForDeploymentPostprocessing?: boolean;

325

}

326

```

327

328

### Build Phase Structure Types

329

330

```javascript { .api }

331

/**

332

* Base build phase structure

333

*/

334

interface BuildPhase {

335

/** Build phase type */

336

isa: string;

337

338

/** Build action mask */

339

buildActionMask: number;

340

341

/** Files in build phase */

342

files: BuildFile[];

343

344

/** Run only for deployment post-processing */

345

runOnlyForDeploymentPostprocessing: number;

346

}

347

348

interface PBXShellScriptBuildPhase extends BuildPhase {

349

/** Script name */

350

name: string;

351

352

/** Input file paths */

353

inputPaths: string[];

354

355

/** Output file paths */

356

outputPaths: string[];

357

358

/** Shell path */

359

shellPath: string;

360

361

/** Shell script content */

362

shellScript: string;

363

}

364

365

interface PBXCopyFilesBuildPhase extends BuildPhase {

366

/** Copy phase name */

367

name: string;

368

369

/** Destination path */

370

dstPath: string;

371

372

/** Destination subfolder specification */

373

dstSubfolderSpec: number;

374

}

375

376

interface BuildFile {

377

/** Build file UUID */

378

value: string;

379

380

/** Build file comment */

381

comment: string;

382

}

383

```

384

385

### Advanced Build Phase Examples

386

387

**SwiftLint Integration:**

388

```javascript

389

const swiftLintPhase = proj.addBuildPhase(

390

[],

391

'PBXShellScriptBuildPhase',

392

'SwiftLint',

393

null,

394

{

395

shellPath: '/bin/sh',

396

shellScript: `if which swiftlint >/dev/null; then

397

swiftlint

398

else

399

echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"

400

fi`,

401

inputPaths: [],

402

outputPaths: []

403

}

404

);

405

```

406

407

**Code Generation Script:**

408

```javascript

409

const codeGenPhase = proj.addBuildPhase(

410

[],

411

'PBXShellScriptBuildPhase',

412

'Generate Code',

413

null,

414

{

415

shellPath: '/usr/bin/python3',

416

shellScript: `import os

417

import sys

418

419

# Add your code generation logic here

420

print("Generating code...")`,

421

inputPaths: ['$(SRCROOT)/Templates/'],

422

outputPaths: ['$(DERIVED_FILE_DIR)/Generated.swift']

423

}

424

);

425

```

426

427

**Framework Embedding:**

428

```javascript

429

// Embed custom frameworks for app extensions

430

const embedPhase = proj.addBuildPhase(

431

['CustomFramework.framework', 'ThirdPartySDK.framework'],

432

'PBXCopyFilesBuildPhase',

433

'Embed Frameworks',

434

extensionTarget.uuid,

435

'frameworks',

436

''

437

);

438

```

439

440

**Resource Processing:**

441

```javascript

442

// Custom resource processing

443

const processResourcesPhase = proj.addBuildPhase(

444

[],

445

'PBXShellScriptBuildPhase',

446

'Process Resources',

447

null,

448

{

449

shellPath: '/bin/sh',

450

shellScript: `# Process and optimize images

451

if which imageoptim >/dev/null; then

452

find "$SRCROOT" -name "*.png" -exec imageoptim {} \\;

453

fi

454

455

# Validate plist files

456

find "$SRCROOT" -name "*.plist" -exec plutil -lint {} \\;`,

457

inputPaths: ['$(SRCROOT)/Resources/'],

458

outputPaths: []

459

}

460

);

461

```

462

463

**Multi-Target Build Phase:**

464

```javascript

465

// Add the same script phase to multiple targets

466

const targets = [

467

proj.getFirstTarget(),

468

proj.pbxTargetByName('MyExtension'),

469

proj.pbxTargetByName('MyFramework')

470

];

471

472

targets.forEach(target => {

473

if (target) {

474

proj.addBuildPhase(

475

[],

476

'PBXShellScriptBuildPhase',

477

'Common Build Script',

478

target.uuid || proj.findTargetKey(target.name),

479

{

480

shellPath: '/bin/sh',

481

shellScript: 'echo "Building target: ${TARGET_NAME}"',

482

inputPaths: [],

483

outputPaths: []

484

}

485

);

486

}

487

});

488

```

489

490

**Conditional Build Phases:**

491

```javascript

492

// Build phase that runs only in specific configurations

493

const debugOnlyPhase = proj.addBuildPhase(

494

[],

495

'PBXShellScriptBuildPhase',

496

'Debug Only Script',

497

null,

498

{

499

shellPath: '/bin/sh',

500

shellScript: `if [ "$CONFIGURATION" = "Debug" ]; then

501

echo "Running debug-only operations..."

502

# Debug-specific operations here

503

fi`,

504

inputPaths: [],

505

outputPaths: []

506

}

507

);

508

```

509

510

### Build Phase Best Practices

511

512

**Script Phase Ordering:**

513

- Place code generation scripts before compile sources phases

514

- Place linting/validation scripts after compile sources phases

515

- Place resource processing scripts before copy bundle resources phases

516

517

**Dependency Tracking:**

518

- Always specify `inputPaths` and `outputPaths` for scripts that generate files

519

- Use `$(SRCROOT)`, `$(BUILT_PRODUCTS_DIR)`, and other Xcode variables for portability

520

- Avoid hardcoded absolute paths

521

522

**Performance Optimization:**

523

- Use conditional checks in scripts to avoid unnecessary work

524

- Cache intermediate results when possible

525

- Run expensive operations only when inputs have changed