Skip to content

Commit

Permalink
refactor!: Consistent app template/Xcode project name (#1485)
Browse files Browse the repository at this point in the history
* refactor!(project): Use consistent project name

* chore(deps): Remove unneeded xmlescape dependency

* fix(build): Consistent handling of archivePath flags

* feat!: Pull in CordovaLib with SwiftPM

* fix(orientation): Set orientation values in Xcodeproj

Otherwise when Xcode consolidates the plist file it will probably
overwrite the ones in the plist with the ones defined in the Xcodeproj
file (which is where it wants to put things by default nowadays).

* fix(pbxproj): Fix compat with some plugins

Some plugins look up the root group of the Xcode project based on the
name "CustomTemplate", which isn't really an ideal thing to do but I'm
unsure if there are better options and this name doesn't actually get
displayed anywhere so we might as well keep the label for compatibility
reasons.

* fix(project): Validate that App.xcodeproj exists

This is a guard against running a newer version of Cordova iOS tools
against an older project with a different name (which will fail due to
now-hardcoded assumptions about the project name).

* fix(run): Launch app based on the product name
  • Loading branch information
dpogue authored Oct 8, 2024
1 parent 98a3bcd commit a8f9c57
Show file tree
Hide file tree
Showing 72 changed files with 585 additions and 770 deletions.
33 changes: 7 additions & 26 deletions lib/Api.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,39 +81,20 @@ class Api {

setupEvents(events);

let xcodeProjDir;
let xcodeCordovaProj;

try {
const xcodeProjDir_array = fs.readdirSync(this.root).filter(e => e.match(/\.xcodeproj$/i));
if (xcodeProjDir_array.length > 1) {
for (let x = 0; x < xcodeProjDir_array.length; x++) {
if (xcodeProjDir_array[x].substring(0, 2) === '._') {
xcodeProjDir_array.splice(x, 1);
}
}
}
xcodeProjDir = xcodeProjDir_array[0];

if (!xcodeProjDir) {
throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`);
}

const cordovaProjName = xcodeProjDir.substring(xcodeProjDir.lastIndexOf(path.sep) + 1, xcodeProjDir.indexOf('.xcodeproj'));
xcodeCordovaProj = path.join(this.root, cordovaProjName);
} catch (e) {
throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`);
const xcodeProjDir = path.join(this.root, 'App.xcodeproj');
if (!fs.existsSync(xcodeProjDir)) {
throw new CordovaError(`The provided path "${this.root}" is not an up-to-date Cordova iOS project.`);
}

this.locations = {
root: this.root,
www: path.join(this.root, 'www'),
platformWww: path.join(this.root, 'platform_www'),
configXml: path.join(xcodeCordovaProj, 'config.xml'),
configXml: path.join(this.root, 'App', 'config.xml'),
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
pbxproj: path.join(this.root, xcodeProjDir, 'project.pbxproj'),
xcodeProjDir: path.join(this.root, xcodeProjDir),
xcodeCordovaProj
pbxproj: path.join(xcodeProjDir, 'project.pbxproj'),
xcodeProjDir,
xcodeCordovaProj: path.join(this.root, 'App')
};
}

Expand Down
18 changes: 6 additions & 12 deletions lib/Podfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ function Podfile (podFilePath, projectName, minDeploymentTarget) {
throw new CordovaError(util.format('Podfile: The file at %s is not `%s`.', this.path, Podfile.FILENAME));
}

if (!projectName) {
throw new CordovaError('Podfile: The projectName was not specified in the constructor.');
}

if (!fs.existsSync(this.path)) {
events.emit('verbose', util.format('Podfile: The file at %s does not exist.', this.path));
events.emit('verbose', 'Creating new Podfile in platforms/ios');
Expand Down Expand Up @@ -168,18 +164,16 @@ Podfile.prototype.escapeSingleQuotes = function (string) {
};

Podfile.prototype.getTemplate = function () {
// Escaping possible ' in the project name
const projectName = this.escapeSingleQuotes(this.projectName);
return util.format(
'# DO NOT MODIFY -- auto-generated by Apache Cordova\n' +
'%s\n' +
'platform :ios, \'%s\'\n' +
'%s\n' +
'target \'%s\' do\n' +
'\tproject \'%s.xcodeproj\'\n' +
'target \'App\' do\n' +
'\tproject \'App.xcodeproj\'\n' +
'%s\n' +
'end\n',
this.sourceToken, this.minDeploymentTarget, this.declarationToken, projectName, projectName, this.podToken);
this.sourceToken, this.minDeploymentTarget, this.declarationToken, this.podToken);
};

Podfile.prototype.addSpec = function (name, spec) {
Expand Down Expand Up @@ -361,10 +355,10 @@ Podfile.prototype.before_install = function (toolOptions) {
// Template tokens in order: project name, project name, debug | release
const template =
'// DO NOT MODIFY -- auto-generated by Apache Cordova\n' +
'#include "Pods/Target Support Files/Pods-%s/Pods-%s.%s.xcconfig"';
'#include "Pods/Target Support Files/Pods-App/Pods-App.%s.xcconfig"';

const debugContents = util.format(template, this.projectName, this.projectName, 'debug');
const releaseContents = util.format(template, this.projectName, this.projectName, 'release');
const debugContents = util.format(template, 'debug');
const releaseContents = util.format(template, 'release');

const debugConfigPath = path.join(this.path, '..', 'pods-debug.xcconfig');
const releaseConfigPath = path.join(this.path, '..', 'pods-release.xcconfig');
Expand Down
75 changes: 30 additions & 45 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,11 @@ const buildFlagMatchers = {
* a project path and name
*
* @param {*} projectPath
* @param {*} projectName
*/
function createProjectObject (projectPath, projectName) {
function createProjectObject (projectPath) {
const locations = {
root: projectPath,
pbxproj: path.join(projectPath, `${projectName}.xcodeproj`, 'project.pbxproj')
pbxproj: path.join(projectPath, 'App.xcodeproj', 'project.pbxproj')
};

return projectFile.parse(locations);
Expand Down Expand Up @@ -103,7 +102,6 @@ function getDefaultSimulatorTarget () {
module.exports.run = function (buildOpts) {
const projectPath = this.root;
let emulatorTarget = 'iOS Device';
let projectName = '';

buildOpts = buildOpts || {};

Expand Down Expand Up @@ -180,9 +178,7 @@ module.exports.run = function (buildOpts) {
}
})
.then(() => check_reqs.run())
.then(() => findXCodeProjectIn(projectPath))
.then(name => {
projectName = name;
.then(() => {
let extraConfig = '';
if (buildOpts.codeSignIdentity) {
extraConfig += `CODE_SIGN_IDENTITY = ${buildOpts.codeSignIdentity}\n`;
Expand All @@ -201,7 +197,7 @@ module.exports.run = function (buildOpts) {
}

function writeCodeSignStyle (value) {
const project = createProjectObject(projectPath, projectName);
const project = createProjectObject(projectPath);

events.emit('verbose', `Set CODE_SIGN_STYLE Build Property to ${value}.`);
project.xcode.updateBuildProperty('CODE_SIGN_STYLE', value);
Expand All @@ -223,7 +219,7 @@ module.exports.run = function (buildOpts) {
}).then(() => {
const configuration = buildOpts.release ? 'Release' : 'Debug';

events.emit('log', `Building project: ${path.join(projectPath, `${projectName}.xcworkspace`)}`);
events.emit('log', `Building project: ${path.join(projectPath, 'App.xcworkspace')}`);
events.emit('log', `\tConfiguration: ${configuration}`);
events.emit('log', `\tPlatform: ${buildOpts.device ? 'device' : 'emulator'}`);
events.emit('log', `\tTarget: ${emulatorTarget}`);
Expand All @@ -233,14 +229,14 @@ module.exports.run = function (buildOpts) {
// remove the build output folder before building
fs.rmSync(buildOutputDir, { recursive: true, force: true });

const xcodebuildArgs = getXcodeBuildArgs(projectName, projectPath, configuration, emulatorTarget, buildOpts);
const xcodebuildArgs = getXcodeBuildArgs(projectPath, configuration, emulatorTarget, buildOpts);
return execa('xcodebuild', xcodebuildArgs, { cwd: projectPath, stdio: 'inherit' });
}).then(() => {
if (!buildOpts.device || buildOpts.catalyst || buildOpts.noSign) {
return;
}

const project = createProjectObject(projectPath, projectName);
const project = createProjectObject(projectPath);
const bundleIdentifier = project.getPackageName();
const exportOptions = { ...buildOpts.exportOptions, compileBitcode: false, method: 'development' };

Expand Down Expand Up @@ -287,7 +283,7 @@ module.exports.run = function (buildOpts) {
}

function packageArchive () {
const xcodearchiveArgs = getXcodeArchiveArgs(projectName, projectPath, buildOutputDir, exportOptionsPath, buildOpts);
const xcodearchiveArgs = getXcodeArchiveArgs(projectPath, buildOutputDir, exportOptionsPath, buildOpts);
return execa('xcodebuild', xcodearchiveArgs, { cwd: projectPath, stdio: 'inherit' });
}

Expand All @@ -298,38 +294,15 @@ module.exports.run = function (buildOpts) {
.then(() => {}); // resolve to undefined
};

/**
* Searches for first XCode project in specified folder
* @param {String} projectPath Path where to search project
* @return {Promise} Promise either fulfilled with project name or rejected
*/
function findXCodeProjectIn (projectPath) {
// 'Searching for Xcode project in ' + projectPath);
const xcodeProjFiles = fs.readdirSync(projectPath).filter(name => path.extname(name) === '.xcodeproj');

if (xcodeProjFiles.length === 0) {
return Promise.reject(new CordovaError(`No Xcode project found in ${projectPath}`));
}
if (xcodeProjFiles.length > 1) {
events.emit('warn', `Found multiple .xcodeproj directories in \n${projectPath}\nUsing first one`);
}

const projectName = path.basename(xcodeProjFiles[0], '.xcodeproj');
return Promise.resolve(projectName);
}

module.exports.findXCodeProjectIn = findXCodeProjectIn;

/**
* Returns array of arguments for xcodebuild
* @param {String} projectName Name of xcode project
* @param {String} projectPath Path to project file. Will be used to set CWD for xcodebuild
* @param {String} configuration Configuration name: debug|release
* @param {String} emulatorTarget Target for emulator (rather than default)
* @param {Object} buildConfig The build configuration options
* @return {Array} Array of arguments that could be passed directly to spawn method
*/
function getXcodeBuildArgs (projectName, projectPath, configuration, emulatorTarget, buildConfig = {}) {
function getXcodeBuildArgs (projectPath, configuration, emulatorTarget, buildConfig = {}) {
let options;
let buildActions;
let settings;
Expand All @@ -349,11 +322,11 @@ function getXcodeBuildArgs (projectName, projectPath, configuration, emulatorTar

if (buildConfig.device && !buildConfig.catalyst) {
options = [
'-workspace', customArgs.workspace || `${projectName}.xcworkspace`,
'-scheme', customArgs.scheme || projectName,
'-workspace', customArgs.workspace || 'App.xcworkspace',
'-scheme', customArgs.scheme || 'App',
'-configuration', customArgs.configuration || configuration,
'-destination', customArgs.destination || 'generic/platform=iOS',
'-archivePath', customArgs.archivePath || `${projectName}.xcarchive`
'-archivePath', customArgs.archivePath || 'App.xcarchive'
];
buildActions = ['archive'];
settings = [];
Expand Down Expand Up @@ -386,8 +359,8 @@ function getXcodeBuildArgs (projectName, projectPath, configuration, emulatorTar
}
} else { // emulator
options = [
'-workspace', customArgs.workspace || `${projectName}.xcworkspace`,
'-scheme', customArgs.scheme || projectName,
'-workspace', customArgs.workspace || 'App.xcworkspace',
'-scheme', customArgs.scheme || 'App',
'-configuration', customArgs.configuration || configuration
];

Expand Down Expand Up @@ -425,15 +398,27 @@ function getXcodeBuildArgs (projectName, projectPath, configuration, emulatorTar

/**
* Returns array of arguments for xcodebuild
* @param {String} projectName Name of xcode project
* @param {String} projectPath Path to project file. Will be used to set CWD for xcodebuild
* @param {String} outputPath Output directory to contain the IPA
* @param {String} exportOptionsPath Path to the exportOptions.plist file
* @param {Object} buildConfig Build configuration options
* @return {Array} Array of arguments that could be passed directly to spawn method
*/
function getXcodeArchiveArgs (projectName, projectPath, outputPath, exportOptionsPath, buildConfig = {}) {
function getXcodeArchiveArgs (projectPath, outputPath, exportOptionsPath, buildConfig = {}) {
const options = [];
const buildFlags = buildConfig.buildFlag;
const customArgs = {};
customArgs.otherFlags = [];

if (buildFlags) {
if (typeof buildFlags === 'string' || buildFlags instanceof String) {
parseBuildFlag(buildFlags, customArgs);
} else { // buildFlags is an Array of strings
buildFlags.forEach(flag => {
parseBuildFlag(flag, customArgs);
});
}
}

if (buildConfig.automaticProvisioning) {
options.push('-allowProvisioningUpdates');
Expand All @@ -450,10 +435,10 @@ function getXcodeArchiveArgs (projectName, projectPath, outputPath, exportOption

return [
'-exportArchive',
'-archivePath', `${projectName}.xcarchive`,
'-archivePath', customArgs.archivePath || 'App.xcarchive',
'-exportOptionsPlist', exportOptionsPath,
'-exportPath', outputPath
].concat(options);
].concat(options).concat(customArgs.otherFlags);
}

function parseBuildFlag (buildFlag, args) {
Expand Down
10 changes: 2 additions & 8 deletions lib/clean.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,15 @@
const path = require('node:path');
const fs = require('node:fs');
const execa = require('execa');
const { CordovaError } = require('cordova-common');

module.exports.run = function () {
const projectPath = this.root;
const projectName = fs.readdirSync(projectPath).filter(name => path.extname(name) === '.xcodeproj');

if (!projectName) {
return Promise.reject(new CordovaError(`No Xcode project found in ${projectPath}`));
}

const xcodebuildClean = configName => {
return execa(
'xcodebuild',
['-project', projectName, '-configuration', configName, '-alltargets', 'clean'],
{ cwd: projectPath, stdio: 'inherit' }
['-project', 'App.xcodeproj', '-configuration', configName, '-alltargets', 'clean'],
{ cwd: this.root, stdio: 'inherit' }
);
};

Expand Down
Loading

0 comments on commit a8f9c57

Please sign in to comment.