Skip to content

Commit

Permalink
feat!: Remove some Xcode build dir overrides
Browse files Browse the repository at this point in the history
Once upon a time, Xcode's default directory values had issues when there
were spaces in project names, so we override the CONFIGURATION_BUILD_DIR
and SHARED_PRECOMPS_DIR variables with our own paths.

However, this can interfere with Cocoapods, and Xcode should be able to
handle things sensibly nowadays.

Unfortunately, that results in our build artifacts living somewhere in a
randomly-named Xcode DerivedData directory, and then we can't do things
like run the app in a simulator or on a device because we don't know the
path to it.

Setting SYMROOT allows us to control the output directory of the built
products, with the caveat that Xcode always creates a subdirectory named
with the current configuration.

So instead of `build/emulator`, we'll have `build/Debug-iphonesimulator`
and instead of `build/device`, we'll have `build/Release-iphoneos`.

Hypothetically this is better because now we are sure that debug and
release files never get mixed up in the same output directory.

The downside is that this is a breaking change because it alters the
path for the output .ipa files.

Closes apache#617.
Closes apache#659.
Closes apache#671.

Co-Authored-By: Susan Tan <onceuponatimeforever@gmail.com>
  • Loading branch information
dpogue and ArcTanSusan committed Apr 16, 2023
1 parent 3d6c71a commit 0da4b51
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 35 deletions.
37 changes: 25 additions & 12 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,9 @@ module.exports.run = function (buildOpts) {
events.emit('log', `\tPlatform: ${buildOpts.device ? 'device' : 'emulator'}`);
events.emit('log', `\tTarget: ${emulatorTarget}`);

const buildOutputDir = path.join(projectPath, 'build', (buildOpts.device ? 'device' : 'emulator'));
const buildOutputDir = path.join(projectPath, 'build', `${configuration}-${(buildOpts.device ? 'iphoneos' : 'iphonesimulator')}`);

// remove the build/device folder before building
// remove the build output folder before building
fs.removeSync(buildOutputDir);

const xcodebuildArgs = getXcodeBuildArgs(projectName, projectPath, configuration, emulatorTarget, buildOpts);
Expand Down Expand Up @@ -264,7 +264,8 @@ module.exports.run = function (buildOpts) {
const exportOptionsPlist = plist.build(exportOptions);
const exportOptionsPath = path.join(projectPath, 'exportOptions.plist');

const buildOutputDir = path.join(projectPath, 'build', 'device');
const configuration = buildOpts.release ? 'Release' : 'Debug';
const buildOutputDir = path.join(projectPath, 'build', `${configuration}-iphoneos`);

function checkSystemRuby () {
const ruby_cmd = which.sync('ruby', { nothrow: true });
Expand Down Expand Up @@ -346,10 +347,16 @@ function getXcodeBuildArgs (projectName, projectPath, configuration, emulatorTar
'-archivePath', customArgs.archivePath || `${projectName}.xcarchive`
];
buildActions = ['archive'];
settings = [
customArgs.configuration_build_dir || `CONFIGURATION_BUILD_DIR=${path.join(projectPath, 'build', 'device')}`,
customArgs.shared_precomps_dir || `SHARED_PRECOMPS_DIR=${path.join(projectPath, 'build', 'sharedpch')}`
];
settings = [];

if (customArgs.configuration_build_dir) {
settings.push(customArgs.configuration_build_dir);
}

if (customArgs.shared_precomps_dir) {
settings.push(customArgs.shared_precomps_dir);
}

// Add other matched flags to otherFlags to let xcodebuild present an appropriate error.
// This is preferable to just ignoring the flags that the user has passed in.
if (customArgs.sdk) {
Expand All @@ -370,17 +377,23 @@ function getXcodeBuildArgs (projectName, projectPath, configuration, emulatorTar
}
} else { // emulator
options = [
'-workspace', customArgs.project || `${projectName}.xcworkspace`,
'-workspace', customArgs.workspace || `${projectName}.xcworkspace`,
'-scheme', customArgs.scheme || projectName,
'-configuration', customArgs.configuration || configuration,
'-sdk', customArgs.sdk || 'iphonesimulator',
'-destination', customArgs.destination || `platform=iOS Simulator,name=${emulatorTarget}`
];
buildActions = ['build'];
settings = [
customArgs.configuration_build_dir || `CONFIGURATION_BUILD_DIR=${path.join(projectPath, 'build', 'emulator')}`,
customArgs.shared_precomps_dir || `SHARED_PRECOMPS_DIR=${path.join(projectPath, 'build', 'sharedpch')}`
];
settings = [`SYMROOT=${path.join(projectPath, 'build')}`];

if (customArgs.configuration_build_dir) {
settings.push(customArgs.configuration_build_dir);
}

if (customArgs.shared_precomps_dir) {
settings.push(customArgs.shared_precomps_dir);
}

// Add other matched flags to otherFlags to let xcodebuild present an appropriate error.
// This is preferable to just ignoring the flags that the user has passed in.
if (customArgs.archivePath) {
Expand Down
5 changes: 3 additions & 2 deletions lib/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ module.exports.run = function (runOptions) {
}

let useDevice = !!runOptions.device;
const configuration = runOptions.release ? 'Release' : 'Debug';

return require('./listDevices').run()
.then(devices => {
Expand All @@ -60,7 +61,7 @@ module.exports.run = function (runOptions) {
}
}).then(() => build.findXCodeProjectIn(projectPath))
.then(projectName => {
let appPath = path.join(projectPath, 'build', 'emulator', `${projectName}.app`);
let appPath = path.join(projectPath, 'build', `${configuration}-iphonesimulator`, `${projectName}.app`);
const buildOutputDir = path.join(projectPath, 'build', 'device');

// select command to run and arguments depending whether
Expand Down Expand Up @@ -91,7 +92,7 @@ module.exports.run = function (runOptions) {
})
.then(
() => {
appPath = path.join(projectPath, 'build', 'device', `${projectName}.app`);
appPath = path.join(projectPath, 'build', `${configuration}-iphoneos`, `${projectName}.app`);
let extraArgs = [];
if (runOptions.argv) {
// argv.slice(2) removes node and run.js, filterSupportedArgs removes the run.js args
Expand Down
65 changes: 44 additions & 21 deletions tests/spec/unit/build.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,9 @@ describe('build', () => {
'generic/platform=iOS',
'-archivePath',
'TestProjectName.xcarchive',
'archive',
`CONFIGURATION_BUILD_DIR=${path.join(testProjectPath, 'build', 'device')}`,
`SHARED_PRECOMPS_DIR=${path.join(testProjectPath, 'build', 'sharedpch')}`
'archive'
]);
expect(args.length).toEqual(13);
expect(args.length).toEqual(11);
});

it('should generate appropriate args if buildFlags are passed in', () => {
Expand Down Expand Up @@ -93,11 +91,9 @@ describe('build', () => {
'generic/platform=iOS',
'-archivePath',
'TestProjectName.xcarchive',
'archive',
`CONFIGURATION_BUILD_DIR=${path.join(testProjectPath, 'build', 'device')}`,
`SHARED_PRECOMPS_DIR=${path.join(testProjectPath, 'build', 'sharedpch')}`
'archive'
]);
expect(args.length).toEqual(13);
expect(args.length).toEqual(11);
});

it('should generate appropriate args for simulator', () => {
Expand All @@ -114,10 +110,42 @@ describe('build', () => {
'-destination',
'platform=iOS Simulator,name=iPhone 5s',
'build',
`CONFIGURATION_BUILD_DIR=${path.join(testProjectPath, 'build', 'emulator')}`,
`SHARED_PRECOMPS_DIR=${path.join(testProjectPath, 'build', 'sharedpch')}`
`SYMROOT=${path.join(testProjectPath, 'build')}`
]);
expect(args.length).toEqual(13);
expect(args.length).toEqual(12);
});

it('should generate appropriate args for simulator if buildFlags are passed in', () => {
const buildFlags = [
'-workspace TestWorkspaceFlag',
'-scheme TestSchemeFlag',
'-configuration TestConfigurationFlag',
'-destination TestDestinationFlag',
'-archivePath TestArchivePathFlag',
'CONFIGURATION_BUILD_DIR=TestConfigBuildDirFlag',
'SHARED_PRECOMPS_DIR=TestSharedPrecompsDirFlag'
];

const args = getXcodeBuildArgs('TestProjectName', testProjectPath, 'TestConfiguration', 'iPhone 5s', { device: false, buildFlag: buildFlags });
expect(args).toEqual([
'-workspace',
'TestWorkspaceFlag',
'-scheme',
'TestSchemeFlag',
'-configuration',
'TestConfigurationFlag',
'-sdk',
'iphonesimulator',
'-destination',
'TestDestinationFlag',
'build',
`SYMROOT=${path.join(testProjectPath, 'build')}`,
'CONFIGURATION_BUILD_DIR=TestConfigBuildDirFlag',
'SHARED_PRECOMPS_DIR=TestSharedPrecompsDirFlag',
'-archivePath',
'TestArchivePathFlag'
]);
expect(args.length).toEqual(16);
});

it('should add matched flags that are not overriding for device', () => {
Expand All @@ -136,12 +164,10 @@ describe('build', () => {
'-archivePath',
'TestProjectName.xcarchive',
'archive',
`CONFIGURATION_BUILD_DIR=${path.join(testProjectPath, 'build', 'device')}`,
`SHARED_PRECOMPS_DIR=${path.join(testProjectPath, 'build', 'sharedpch')}`,
'-sdk',
'TestSdkFlag'
]);
expect(args.length).toEqual(15);
expect(args.length).toEqual(13);
});

it('should add matched flags that are not overriding for simulator', () => {
Expand All @@ -160,12 +186,11 @@ describe('build', () => {
'-destination',
'platform=iOS Simulator,name=iPhone 5s',
'build',
`CONFIGURATION_BUILD_DIR=${path.join(testProjectPath, 'build', 'emulator')}`,
`SHARED_PRECOMPS_DIR=${path.join(testProjectPath, 'build', 'sharedpch')}`,
`SYMROOT=${path.join(testProjectPath, 'build')}`,
'-archivePath',
'TestArchivePathFlag'
]);
expect(args.length).toEqual(15);
expect(args.length).toEqual(14);
});

it('should generate appropriate args for automatic provisioning', () => {
Expand Down Expand Up @@ -196,11 +221,9 @@ describe('build', () => {
'12345',
'-authenticationKeyIssuerID',
'00000000-0000-0000-0000-000000000000',
'archive',
`CONFIGURATION_BUILD_DIR=${path.join(testProjectPath, 'build', 'device')}`,
`SHARED_PRECOMPS_DIR=${path.join(testProjectPath, 'build', 'sharedpch')}`
'archive'
]);
expect(args.length).toEqual(20);
expect(args.length).toEqual(18);
});
});

Expand Down

0 comments on commit 0da4b51

Please sign in to comment.