Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[TIMOB-12353] Add LiveTi support to iOS simulator builds #3857

Closed
wants to merge 2 commits into from

3 participants

@euforic

Added process.env.NODE_ENV check for 'liveti' to swap users app.js for liveti.js during symlinking

@jhaynie
Owner

this seems like we support something more like a build hook than having to hack the CLI for this.

@cb1kenobi can't we use something like an event hook for this or add support for this and then liveti would include this code in its own repo?

@cb1kenobi
Owner

@jhaynie Yeah, we could totally do this in a hook, though we'd need to first add the hook to the _build.js code. I like that solution though. How is liveti "enabled"?

@euforic

@jhaynie @cb1kenobi I agree the build hook would work a lot better. LiveTi could still be enabled via process.env.NODE_ENV or even create process.env.TI_ENV

@cb1kenobi
Owner

@euforic if we integrate liveti in a hook, the hook itself is in a CLI plugin. There is no need to use environment variables. CLI plugins are enabled via the tiapp.xml.

@euforic

@cb1kenobi yeah but if use the tiapp.xml you have to edit it ever time you want to use it. With the env var you can just enable and disable via cli flag

@jhaynie
Owner

@cb1kenobi will are going to have more use cases like liveti where we need to be able to have them automatically work against a compiling project (like code processor, etc) whereby we don't want to have to edit tiapp.xml to do this... is there another simple way to do this? (even environment vars suck since you have to add them to make them work). is there some sort of way that we can automatically load any of these hooks? it might just been that we install them all in a specific directory and then the CLI knows how to scan this directory for automatic hooks?

@jhaynie
Owner

@cb1kenobi what if we used NPM install hooks (https://npmjs.org/doc/scripts.html) to update a hooks registery file in Titanium directory (~/Library/Application Support/Titanium/cli.hooks.json or equivalent) that would basically register its directory to the file which would be an array of hook folder locations. Then, CLI would read this, and just load up all the registered hooks. So, Alloy, LiveTi, Codeprocessor, etc would now just register their hook and we could forgo have to copy plugins into a project or anything like that for all of these types of collaborative tools.

@cb1kenobi
Owner

@jhaynie The CLI supports a config option "paths.hooks" which is an array of paths that are scanned for .js files which are then require()'d. In theory, those .js files would contain the hooks.

@cb1kenobi
Owner

Closing because we now have more build hooks, so this isn't needed.

@cb1kenobi cb1kenobi closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 9, 2013
  1. @euforic
Commits on Feb 11, 2013
  1. @euforic

    syntax clean up

    euforic authored
This page is out of date. Refresh to see the latest.
Showing with 292 additions and 286 deletions.
  1. +292 −286 iphone/cli/commands/_build.js
View
578 iphone/cli/commands/_build.js
@@ -76,7 +76,7 @@ exports.config = function (logger, config, cli) {
return function (callback) {
ios.detect(function (env) {
iosEnv = env || {};
-
+
var sdks = {},
sims = {},
defaultSdk,
@@ -86,7 +86,7 @@ exports.config = function (logger, config, cli) {
lowerCasedDistNames = iosEnv.certs.distNames.map(function (s) { return s.toLowerCase(); }),
humanize = require('humanize'),
libTiCoreSize = humanize.filesize(fs.lstatSync(afs.resolvePath(__dirname, '..', '..', 'libTiCore.a')).size, 1024, 1).toUpperCase();
-
+
// attempt to resolve a default ios developer cert name (used for device builds)
if (process.env.CODE_SIGN_IDENTITY) {
devName = process.env.CODE_SIGN_IDENTITY.replace(/(iPhone Developer\: (.+) \(.+)/, '$2');
@@ -107,7 +107,7 @@ exports.config = function (logger, config, cli) {
}
}
}
-
+
Object.keys(iosEnv.xcode).forEach(function (key) {
iosEnv.xcode[key].sdks.forEach(function (sdk) {
if (iosEnv.xcode[key].selected && !defaultSdk || version.gt(sdk, defaultSdk)) {
@@ -117,14 +117,14 @@ exports.config = function (logger, config, cli) {
});
});
sdks = Object.keys(sdks).sort().reverse();
-
+
Object.keys(iosEnv.xcode).forEach(function (key) {
iosEnv.xcode[key].sims.forEach(function (sdk) {
sims[sdk] = 1;
});
});
sims = Object.keys(sims).sort().reverse();
-
+
callback(conf = {
flags: {
retina: {
@@ -171,33 +171,33 @@ exports.config = function (logger, config, cli) {
error: __('Invalid developer name'),
validator: function (name) {
name && (name = name.trim());
-
+
var dn = devNameIdRegExp.test(name) ? lowerCasedDevNames.map(function (name) {
return name.trim();
}) : lowerCasedDevNames.map(function (name) {
var m = name.match(/^([^(]+?)*/);
return (m ? m[0] : name).trim();
});
-
+
if (!name || dn.indexOf(name.toLowerCase()) == -1) {
var width = 0,
i = 0,
l = iosEnv.certs.devNames.length,
h = Math.ceil(l / 2),
output = [__('Available names:')];
-
+
for (; i < h; i++) {
if (iosEnv.certs.devNames[i].length > width) {
width = iosEnv.certs.devNames[i].length;
}
output.push(' ' + iosEnv.certs.devNames[i]);
}
-
+
width += 12; // 4 spaces left padding, 8 spaces between columns
for (h = 1; i < l; h++, i++) {
output[h] = appc.string.rpad(output[h], width) + iosEnv.certs.devNames[i];
}
-
+
throw new appc.exception(
name ? __('Unable to find an iOS Developer Certificate for "%s"', name) : __('Select an iOS Developer Certificate:'),
output.map(function (s, i) { return i ? s.cyan : s; })
@@ -272,7 +272,7 @@ exports.config = function (logger, config, cli) {
error: __('Invalid Provisioning Profile UUID'),
validator: function (uuid) {
uuid = (uuid || '').trim();
-
+
var availableUUIDs = [],
addUUIDs = function (section, uuids) {
if (uuids.length) {
@@ -285,7 +285,7 @@ exports.config = function (logger, config, cli) {
}
}
};
-
+
if (cli.argv.target == 'device') {
if (addUUIDs(__('Available Development UUIDs:'), iosEnv.provisioningProfiles.development)) return true;
} else {
@@ -293,7 +293,7 @@ exports.config = function (logger, config, cli) {
if (addUUIDs(__('Available Adhoc UUIDs:'), iosEnv.provisioningProfiles.adhoc)) return true;
// TODO: display enterprise adhoc uuids
}
-
+
if (uuid) {
throw new appc.exception(__('Unable to find a Provisioning Profile UUID "%s"', uuid), availableUUIDs);
} else {
@@ -318,39 +318,39 @@ exports.config = function (logger, config, cli) {
abbr: 'T',
callback: function (value) {
var pp = iosEnv.provisioningProfiles;
-
+
// as soon as we know the target, toggle required options for validation
switch (value) {
case 'device':
conf.options['developer-name'].required = true;
conf.options['pp-uuid'].required = true;
-
+
if (!iosEnv.certs.devNames.length) {
logger.error(__('Unable to find any iOS Developer Certificates') + '\n');
logger.log(__('Download and install a certificate from %s', 'https://developer.apple.com/ios/manage/certificates/team/index.action'.cyan) + '\n');
process.exit(1);
}
-
+
if (!pp.development.length) {
logger.error(__('No development provisioning profiles found') + '\n');
logger.log(__('Download and install a provisioning profile from %s', 'https://developer.apple.com/ios/manage/provisioningprofiles/index.action'.cyan) + '\n');
process.exit(1);
}
break;
-
+
case 'dist-adhoc':
conf.options['output-dir'].required = true;
// purposely fall through!
case 'dist-appstore':
conf.options['distribution-name'].required = true;
conf.options['pp-uuid'].required = true;
-
+
if (!iosEnv.certs.distNames.length) {
logger.error(__('Unable to find any iOS Distribution Certificates') + '\n');
logger.log(__('Download and install a certificate from %s', 'https://developer.apple.com/ios/manage/distribution/index.action'.cyan) + '\n');
process.exit(1);
}
-
+
if (!pp.distribution.length && !pp.adhoc.length && !pp.enterprise.length) {
logger.error(__('No distribution or adhoc provisioning profiles found') + '\n');
logger.log(__('Download and install a provisioning profile from %s', 'https://developer.apple.com/ios/manage/provisioningprofiles/index.action'.cyan) + '\n');
@@ -374,29 +374,29 @@ exports.config = function (logger, config, cli) {
exports.validate = function (logger, config, cli) {
var sdks = {},
sims = {};
-
+
// if we're running from Xcode, we want to use the PROJECT_DIR environment variable
if (process.env.PROJECT_DIR) {
cli.argv['project-dir'] = path.dirname(process.env.PROJECT_DIR);
}
-
+
ti.validateProjectDir(logger, cli, cli.argv, 'project-dir');
-
+
var resourcesDir = path.join(cli.argv['project-dir'], 'Resources');
-
+
// make sure we have an app.js
if (!afs.exists(resourcesDir, 'app.js')) {
logger.error(__('No app.js found') + '\n');
logger.log(__("Ensure the app.js file exists in your project's \"Resources\" directory.") + '\n');
process.exit(1);
}
-
+
if (!cli.argv.xcode || !process.env.TITANIUM_CLI_XCODEBUILD) {
// make sure the app doesn't have any blacklisted directories in the Resources directory and warn about graylisted names
fs.readdirSync(resourcesDir).forEach(function (filename) {
var lcaseFilename = filename.toLowerCase(),
isDir = fs.lstatSync(path.join(resourcesDir, filename)).isDirectory();
-
+
if (blacklistDirectories.indexOf(lcaseFilename) != -1) {
if (isDir) {
logger.error(__('Found blacklisted directory in the Resources directory') + '\n');
@@ -423,20 +423,20 @@ exports.validate = function (logger, config, cli) {
}
});
}
-
+
ti.validateTiappXml(logger, cli.tiapp);
-
+
if (!ti.validateCorrectSDK(logger, config, cli, 'build')) {
// we're running the build command for the wrong SDK version, gracefully return
return false;
}
-
+
if (!Object.keys(iosEnv.xcode).length) {
logger.error(__('Unable to find Xcode') + '\n');
logger.log(__('Please download and install Xcode, then try again') + '\n');
process.exit(1);
}
-
+
if (!iosEnv.xcode.__selected__) {
logger.error(__('No Xcode version is currently selected') + '\n');
logger.log(__("Use 'xcode-select' to select one of the Xcode versions:"));
@@ -448,19 +448,19 @@ exports.validate = function (logger, config, cli) {
logger.log();
process.exit(1);
}
-
+
Object.keys(iosEnv.xcode).forEach(function (sdk) {
sdk != '__selected__' && iosEnv.xcode[sdk].sdks.forEach(function (ver) {
sdks[ver] = iosEnv.xcode[sdk].sims;
});
});
-
+
if (!Object.keys(sdks).length) {
logger.error(__('Unable to find any iOS SDKs') + '\n');
logger.log(__('Please download and install an iOS SDK (version %s or newer)', version.format(minIosSdkVersion, 2)) + '\n');
process.exit(1);
}
-
+
if (!cli.argv['ios-version'] || !Object.keys(sdks).some(function (ver) {
if (version.eq(ver, cli.argv['ios-version'])) {
cli.argv['ios-version'] = ver;
@@ -475,7 +475,7 @@ exports.validate = function (logger, config, cli) {
logger.log();
process.exit(1);
}
-
+
// check if this is the Xcode pre-compile phase
if (cli.argv.xcode) {
if (process.env.TITANIUM_CLI_XCODEBUILD) {
@@ -511,13 +511,13 @@ exports.validate = function (logger, config, cli) {
cli.argv['force-copy-all'] = false; // we don't want to copy the big libTiCore.a file around by default
}
}
-
+
if (targets.indexOf(cli.argv.target) == -1) {
logger.error(__('Invalid target "%s"', cli.argv.target) + '\n');
appc.string.suggest(cli.argv.target, targets, logger.log, 3);
process.exit(1);
}
-
+
if (!cli.argv.xcode && cli.argv.target != 'simulator') {
// make sure they have Apple's WWDR cert installed
if (!iosEnv.certs.wwdr) {
@@ -525,7 +525,7 @@ exports.validate = function (logger, config, cli) {
logger.log(__('Download and install the certificate from %s', 'https://developer.apple.com/ios/manage/certificates/team/index.action'.cyan) + '\n');
process.exit(1);
}
-
+
if (cli.argv.target == 'device') {
// validate developer cert
if (!iosEnv.certs.devNames.length) {
@@ -533,9 +533,9 @@ exports.validate = function (logger, config, cli) {
logger.log(__('Download and install a certificate from %s', 'https://developer.apple.com/ios/manage/certificates/team/index.action'.cyan) + '\n');
process.exit(1);
}
-
+
cli.argv['developer-name'] = (cli.argv['developer-name'] || '').trim();
-
+
var devNames = devNameIdRegExp.test(cli.argv['developer-name']) ? iosEnv.certs.devNames.map(function (name) {
return name.trim().toLowerCase();
}) : iosEnv.certs.devNames.map(function (name) {
@@ -543,7 +543,7 @@ exports.validate = function (logger, config, cli) {
return (m ? m[0] : name).trim().toLowerCase();
}),
p = devNames.indexOf(cli.argv['developer-name'].toLowerCase());
-
+
if (p == -1) {
logger.error(__('Unable to find an iOS Developer Certificate for "%s"', cli.argv['developer-name']) + '\n');
logger.log(__('Available developer names:'));
@@ -563,13 +563,13 @@ exports.validate = function (logger, config, cli) {
logger.log(__('Download and install a certificate from %s', 'https://developer.apple.com/ios/manage/distribution/index.action'.cyan) + '\n');
process.exit(1);
}
-
+
cli.argv['distribution-name'] = (cli.argv['distribution-name'] || '').trim();
-
+
var p = iosEnv.certs.distNames.map(function (name) {
return name.toLowerCase();
}).indexOf(cli.argv['distribution-name'].toLowerCase());
-
+
if (p != -1) {
cli.argv['distribution-name'] = iosEnv.certs.distNames[p];
} else {
@@ -583,18 +583,18 @@ exports.validate = function (logger, config, cli) {
process.exit(1);
}
}
-
+
// validate provisioning profile
if (!cli.argv['pp-uuid']) {
logger.error(__('Missing required option "--pp-uuid"') + '\n');
process.exit(1);
}
-
+
cli.argv['pp-uuid'] = cli.argv['pp-uuid'].trim();
-
+
(function (pp) {
iosEnv.provisioningProfilesByUUID = {};
-
+
var availableUUIDs = [],
allUUIDs = [],
addUUIDs = function (section, uuids) {
@@ -612,14 +612,14 @@ exports.validate = function (logger, config, cli) {
}
return match;
};
-
+
if (cli.argv.target == 'device') {
if (addUUIDs(__('Available Development UUIDs:'), pp.development)) return;
} else {
if (addUUIDs(__('Available Distribution UUIDs:'), pp.distribution) + addUUIDs(__('Available Adhoc UUIDs:'), pp.adhoc)) return;
// TODO: display enterprise adhoc uuids
}
-
+
logger.error(__('Invalid Provisioning Profile UUID "%s"', cli.argv['pp-uuid']) + '\n');
availableUUIDs.forEach(function (line) {
logger.log(line);
@@ -628,7 +628,7 @@ exports.validate = function (logger, config, cli) {
appc.string.suggest(cli.argv['pp-uuid'], allUUIDs, logger.log);
process.exit(1);
})(iosEnv.provisioningProfiles);
-
+
// validate keychain
var keychain = cli.argv.keychain ? afs.resolvePath(cli.argv.keychain) : null;
if (keychain && !afs.exists(keychain)) {
@@ -642,13 +642,13 @@ exports.validate = function (logger, config, cli) {
process.exit(1);
}
}
-
+
if (cli.argv.target == 'dist-adhoc') {
if (!cli.argv['output-dir']) {
logger.error(__('Invalid required option "--output-dir"') + '\n');
process.exit(1);
}
-
+
cli.argv['output-dir'] = afs.resolvePath(cli.argv['output-dir']);
if (!afs.exists(cli.argv['output-dir'])) {
wrench.mkdirSyncRecursive(cli.argv['output-dir']);
@@ -657,7 +657,7 @@ exports.validate = function (logger, config, cli) {
process.exit(1);
}
}
-
+
var deviceFamily = cli.argv['device-family'],
deploymentTargets = cli.tiapp['deployment-targets'];
if (!deviceFamily && process.env.TARGETED_DEVICE_FAMILY) {
@@ -678,16 +678,16 @@ exports.validate = function (logger, config, cli) {
logger.info(__('No device family specified, defaulting to %s', 'universal'));
deviceFamily = 'universal';
}
-
+
if (!deviceFamilies[deviceFamily]) {
logger.error(__('Invalid device family "%s"', deviceFamily) + '\n');
appc.string.suggest(deviceFamily, Object.keys(deviceFamilies), logger.log, 3);
process.exit(1);
}
-
+
// device family may have been modified, so set it back in the args
cli.argv['device-family'] = deviceFamily;
-
+
if (cli.argv.target == 'simulator') {
if (!cli.argv['sim-version']) {
cli.argv['sim-version'] = cli.argv['ios-version'];
@@ -706,21 +706,21 @@ exports.validate = function (logger, config, cli) {
logger.log();
process.exit(1);
}
-
+
if (!cli.argv['sim-type']) {
cli.argv['sim-type'] = cli.argv['device-family'] == 'ipad' ? 'ipad' : 'iphone';
}
}
-
+
if (cli.argv['debug-host'] && cli.argv.target != 'dist-appstore') {
if (typeof cli.argv['debug-host'] == 'number') {
logger.error(__('Invalid debug host "%s"', cli.argv['debug-host']) + '\n');
logger.log(__('The debug host must be in the format "host:port".') + '\n');
process.exit(1);
}
-
+
var parts = cli.argv['debug-host'].split(':');
-
+
if ((cli.argv.target == 'simulator' && parts.length < 2) || (cli.argv.target != 'simulator' && parts.length < 4)) {
logger.error(__('Invalid debug host "%s"', cli.argv['debug-host']) + '\n');
if (cli.argv.target == 'simulator') {
@@ -730,7 +730,7 @@ exports.validate = function (logger, config, cli) {
}
process.exit(1);
}
-
+
if (parts.length > 1 && parts[1]) {
var port = parseInt(parts[1]);
if (isNaN(port) || port < 1 || port > 65535) {
@@ -744,7 +744,7 @@ exports.validate = function (logger, config, cli) {
exports.run = function (logger, config, cli, finished) {
cli.argv.platform = 'ios';
-
+
if (cli.argv.xcode) {
// basically, we bypass the pre, post, and finalize hooks for xcode builds
new build(logger, config, cli, finished);
@@ -799,27 +799,27 @@ function sendAnalytics(cli) {
function build(logger, config, cli, finished) {
this.logger = logger;
this.cli = cli;
-
+
this.titaniumIosSdkPath = afs.resolvePath(__dirname, '..', '..');
this.titaniumSdkVersion = path.basename(path.join(this.titaniumIosSdkPath, '..'));
-
+
this.platformName = path.basename(this.titaniumIosSdkPath); // the name of the actual platform directory which will some day be "ios"
-
+
this.projectDir = cli.argv['project-dir'];
this.buildDir = path.join(this.projectDir, 'build', this.platformName);
this.assetsDir = path.join(this.buildDir, 'assets');
this.tiapp = cli.tiapp;
this.target = cli.argv.target;
this.provisioningProfileUUID = cli.argv['pp-uuid'];
-
+
this.moduleSearchPaths = [ this.projectDir, afs.resolvePath(this.titaniumIosSdkPath, '..', '..', '..', '..') ];
if (config.paths && Array.isArray(config.paths.modules)) {
this.moduleSearchPaths = this.moduleSearchPaths.concat(config.paths.modules);
}
-
+
this.debugHost = cli.argv['debug-host'];
this.keychain = cli.argv.keychain;
-
+
if (cli.argv.xcode) {
this.deployType = cli.argv['deploy-type'];
} else {
@@ -834,22 +834,22 @@ function build(logger, config, cli, finished) {
this.iosBuildDir = path.join(this.buildDir, 'build', this.xcodeTarget + '-' + (this.target == 'simulator' ? 'iphonesimulator' : 'iphoneos'));
this.xcodeAppDir = cli.argv.xcode ? path.join(process.env.TARGET_BUILD_DIR, process.env.CONTENTS_FOLDER_PATH) : path.join(this.iosBuildDir, this.tiapp.name + '.app');
this.xcodeProjectConfigFile = path.join(this.buildDir, 'project.xcconfig');
-
+
this.certDeveloperName = cli.argv['developer-name'];
this.certDistributionName = cli.argv['distribution-name'];
-
+
this.forceCopy = !!cli.argv['force-copy'];
this.forceCopyAll = !!cli.argv['force-copy-all'];
-
+
this.forceRebuild = false;
-
+
// the ios sdk version is not in the selected xcode version, need to find the version that does have it
Object.keys(iosEnv.xcode).forEach(function (sdk) {
if (sdk != '__selected__' && (!this.xcodeEnv || iosEnv.xcode[sdk].selected) && iosEnv.xcode[sdk].sdks.some(function (ver) { return version.eq(ver, this.iosSdkVersion); }, this)) {
this.xcodeEnv = iosEnv.xcode[sdk];
}
}, this);
-
+
this.logger.info(__('Build type: %s', this.deployType.cyan));
this.logger.debug(__('Titanium iOS SDK directory: %s', this.titaniumIosSdkPath.cyan));
this.logger.info(__('Building for target: %s', this.target.cyan));
@@ -867,7 +867,7 @@ function build(logger, config, cli, finished) {
} else if (/dist-appstore|dist\-adhoc/.test(this.target)) {
this.logger.info(__('iOS Distribution Certificate: %s', this.certDistributionName.cyan));
}
-
+
// validate the min-ios-ver from the tiapp.xml
this.minIosVer = this.tiapp.ios && this.tiapp.ios['min-ios-ver'] || minIosSdkVersion;
if (version.gte(this.iosSdkVersion, '6.0') && version.lt(this.minIosVer, '4.3')) {
@@ -881,7 +881,7 @@ function build(logger, config, cli, finished) {
this.minIosVer = this.iosSdkVersion;
}
this.logger.info(__('Minimum iOS version: %s', version.format(this.minIosVer, 2, 3).cyan));
-
+
if (/device|dist\-appstore|dist\-adhoc/.test(this.target)) {
if (this.keychain) {
this.logger.info(__('Using keychain: %s', this.keychain));
@@ -889,22 +889,22 @@ function build(logger, config, cli, finished) {
this.logger.info(__('Using default keychain'));
}
}
-
+
if (this.debugHost && this.target != 'dist-appstore') {
this.logger.info(__('Debugging enabled via debug host: %s', this.debugHost.cyan));
} else {
this.logger.info(__('Debugging disabled'));
}
-
+
// make sure we have an icon
if (!this.tiapp.icon || !['Resources', 'Resources/iphone', 'Resources/ios'].some(function (p) {
return afs.exists(this.projectDir, p, this.tiapp.icon);
}, this)) {
this.tiapp.icon = 'appicon.png';
}
-
+
Array.isArray(this.tiapp.modules) || (this.tiapp.modules = []);
-
+
if (cli.argv.xcode) {
this.logger.info(__('Initiating Xcode pre-compile phase'));
this.xcodePrecompilePhase(finished);
@@ -915,7 +915,7 @@ function build(logger, config, cli, finished) {
}
build.prototype = {
-
+
copyDirSync: function (src, dest, opts) {
afs.copyDirSyncRecursive(src, dest, opts || {
preserve: true,
@@ -924,7 +924,7 @@ build.prototype = {
ignoreFiles: ['.gitignore', '.cvsignore']
});
},
-
+
copyDirAsync: function (src, dest, callback, opts) {
afs.copyDirRecursive(src, dest, callback, opts || {
preserve: true,
@@ -933,7 +933,7 @@ build.prototype = {
ignoreFiles: ['.gitignore', '.cvsignore']
});
},
-
+
hashFile: function (file, limit) {
var result = '';
if (afs.exists(file)) {
@@ -951,7 +951,7 @@ build.prototype = {
}
return result;
},
-
+
preparePhase: function (finished) {
this.architectures = 'armv6 armv7 i386';
// no armv6 support above 4.3 or with 6.0+ SDK
@@ -961,10 +961,10 @@ build.prototype = {
this.architectures = 'armv7 i386';
}
this.logger.debug(__('Building for the following architectures: %s', this.architectures.cyan));
-
+
// create the build directory (<project dir>/build/[iphone|ios]) if it doesn't already exist
wrench.mkdirSyncRecursive(this.assetsDir);
-
+
// read the build manifest from the last time we built, if exists
this.buildManifest = {};
this.buildManifestFile = path.join(this.buildDir, 'build-manifest.json');
@@ -973,20 +973,20 @@ build.prototype = {
this.buildManifest = JSON.parse(fs.readFileSync(this.buildManifestFile)) || {};
} catch (e) {}
}
-
+
// determine the libTiCore hash
this.libTiCoreHash = this.hashFile(path.join(this.titaniumIosSdkPath, 'libTiCore.a'));
-
+
// figure out all of the modules currently in use
this.modulesHash = crypto.createHash('md5').update(this.tiapp.modules.filter(function (m) {
return !m.platform || /^iphone|ipad|commonjs$/.test(m.platform);
}).map(function (m) {
return m.id + ',' + m.platform + ',' + m.version;
}).join('|')).digest('hex');
-
+
// check if we need to do a rebuild
this.forceRebuild = this.checkIfShouldForceRebuild();
-
+
this.cli.fireHook('build.pre.compile', this, function () {
// let's start building some apps!
parallel(this, [
@@ -1007,10 +1007,10 @@ build.prototype = {
this.createXcodeProject();
this.populateIosFiles();
}
-
+
// create the actual .app dir if it doesn't exist
wrench.mkdirSyncRecursive(this.xcodeAppDir);
-
+
parallel(this, [
function (next) {
if (this.target == 'simulator') {
@@ -1045,7 +1045,7 @@ build.prototype = {
});
}.bind(this));
},
-
+
createInfoPlist: function (callback) {
var src = this.projectDir + '/Info.plist',
dest = this.buildDir + '/Info.plist',
@@ -1062,7 +1062,7 @@ build.prototype = {
'__URLSCHEME__': this.tiapp.name.replace(/\./g, '_').replace(/ /g, '').toLowerCase(),
'__ADDITIONAL_URL_SCHEMES__': fbAppId ? '<string>fb' + fbAppId + '</string>' : ''
};
-
+
function merge(src, dest) {
Object.keys(src).forEach(function (prop) {
if (!/^\+/.test(prop)) {
@@ -1075,37 +1075,37 @@ build.prototype = {
}
});
}
-
+
if (afs.exists(this.titaniumIosSdkPath, 'Info.plist')) {
plist.parse(fs.readFileSync(path.join(this.titaniumIosSdkPath, 'Info.plist')).toString().replace(/(__.+__)/g, function (match, key, format) {
return consts.hasOwnProperty(key) ? consts[key] : '<!-- ' + key + ' -->'; // if they key is not a match, just comment out the key
}));
}
-
+
// if the user has a Info.plist in their project directory, consider that a custom override
if (afs.exists(src)) {
this.logger.info(__('Copying custom Info.plist from project directory'));
-
+
var custom = new appc.plist().parse(fs.readFileSync(src).toString());
if (custom.CFBundleIdentifier != this.tiapp.id) {
this.logger.info(__('Forcing rebuild: custom Info.plist CFBundleIdentifier not equal to tiapp.xml <id>'));
this.forceRebuild = true;
}
-
+
merge(custom, plist);
}
-
+
plist.UIRequiresPersistentWiFi = this.tiapp.hasOwnProperty('persistent-wifi') ? !!this.tiapp['persistent-wifi'] : false;
plist.UIPrerenderedIcon = this.tiapp.hasOwnProperty('prerendered-icon') ? !!this.tiapp['prerendered-icon'] : false;
plist.UIStatusBarHidden = this.tiapp.hasOwnProperty('statusbar-hidden') ? !!this.tiapp['statusbar-hidden'] : false;
-
+
plist.UIStatusBarStyle = 'UIStatusBarStyleDefault';
if (/opaque_black|opaque|black/.test(this.tiapp['statusbar-style'])) {
plist.UIStatusBarStyle = 'UIStatusBarStyleBlackOpaque';
} else if (/translucent_black|transparent|translucent/.test(this.tiapp['statusbar-style'])) {
plist.UIStatusBarStyle = 'UIStatusBarStyleBlackTranslucent';
}
-
+
if (iphone) {
if (iphone.orientations) {
var orientationsMap = {
@@ -1114,10 +1114,10 @@ build.prototype = {
'LANDSCAPE_LEFT': 'UIInterfaceOrientationLandscapeLeft',
'LANDSCAPE_RIGHT': 'UIInterfaceOrientationLandscapeRight'
};
-
+
Object.keys(iphone.orientations).forEach(function (key) {
var entry = 'UISupportedInterfaceOrientations' + (key == 'ipad' ? '~ipad' : '');
-
+
Array.isArray(plist[entry]) || (plist[entry] = []);
iphone.orientations[key].forEach(function (name) {
var value = orientationsMap[name.split('.').pop().toUpperCase()] || name;
@@ -1128,22 +1128,22 @@ build.prototype = {
});
});
}
-
+
if (iphone.backgroundModes) {
plist.UIBackgroundModes = (plist.UIBackgroundModes || []).concat(iphone.backgroundModes);
}
-
+
if (iphone.requires) {
plist.UIRequiredDeviceCapabilities = (plist.UIRequiredDeviceCapabilities || []).concat(iphone.requiredFeatures);
}
-
+
if (iphone.types) {
Array.isArray(plist.CFBundleDocumentTypes) || (plist.CFBundleDocumentTypes = []);
iphone.types.forEach(function (type) {
var types = plist.CFBundleDocumentTypes,
match = false,
i = 0;
-
+
for (; i < types.length; i++) {
if (types[i].CFBundleTypeName == type.name) {
types[i].CFBundleTypeIconFiles = type.icon;
@@ -1153,7 +1153,7 @@ build.prototype = {
break;
}
}
-
+
if (!match) {
types.push({
CFBundleTypeName: type.name,
@@ -1165,11 +1165,11 @@ build.prototype = {
});
}
}
-
+
ios && ios.plist && merge(ios.plist, plist);
-
+
plist.CFBundleIdentifier = this.tiapp.id;
-
+
// device builds require an additional token to ensure uniquiness so that iTunes will detect an updated app to sync
if (this.target == 'device') {
plist.CFBundleVersion = appc.version.format(this.tiapp.version, 3, 3) + '.' + (new Date).getTime();
@@ -1177,7 +1177,7 @@ build.prototype = {
plist.CFBundleVersion = appc.version.format(this.tiapp.version, 3, 3);
}
plist.CFBundleShortVersionString = plist.CFBundleVersion;
-
+
Array.isArray(plist.CFBundleIconFiles) || (plist.CFBundleIconFiles = []);
['.png', '@2x.png', '-72.png', '-Small-50.png', '-72@2x.png', '-Small-50@2x.png', '-Small.png', '-Small@2x.png'].forEach(function (name) {
name = iconName + name;
@@ -1189,12 +1189,12 @@ build.prototype = {
}
}
}, this);
-
+
fs.writeFileSync(dest, plist.toString('xml'));
-
+
callback();
},
-
+
createDebuggerPlist: function (callback) {
var parts = (this.debugHost || '').split(':'),
plist = fs.readFileSync(path.join(this.titaniumIosSdkPath, 'debugger.plist'))
@@ -1205,7 +1205,7 @@ build.prototype = {
.replace(/__DEBUGGER_HOSTS__/g, parts.length > 3 ? parts[3] : ''),
dest = path.join(this.buildDir, 'debugger.plist'),
plistExists = afs.exists(dest);
-
+
if (!plistExists || fs.readFileSync(dest).toString() != plist) {
if (!plistExists) {
this.logger.info(__('Forcing rebuild: debugger.plist does not exist'));
@@ -1218,7 +1218,7 @@ build.prototype = {
callback();
}
},
-
+
createEntitlementsPlist: function (callback) {
if (/device|dist\-appstore|dist\-adhoc/.test(this.target)) {
// allow the project to have its own custom entitlements
@@ -1242,12 +1242,12 @@ build.prototype = {
callback();
}
},
-
+
scrubName: function (name) {
name = name.replace(/-/g, '_').replace(/\W/g, '')
return /^[0-9]/.test(name) ? 'k' + name : name;
},
-
+
createXcodeProject: function () {
var xcodeDir = path.join(this.buildDir, this.tiapp.name + '.xcodeproj'),
namespace = this.scrubName(this.tiapp.name),
@@ -1278,7 +1278,7 @@ build.prototype = {
return contents;
}
};
-
+
this.logger.info(__('Copying Xcode iOS files'));
['Classes', 'headers'].forEach(function (dir) {
afs.copyDirSyncRecursive(
@@ -1287,7 +1287,7 @@ build.prototype = {
copyOpts
);
}, this);
-
+
afs.copyFileSync(
path.join(this.titaniumIosSdkPath, this.platformName, 'Titanium_Prefix.pch'),
path.join(this.buildDir, this.tiapp.name + '_Prefix.pch'),
@@ -1295,10 +1295,10 @@ build.prototype = {
logger: this.logger.debug
}
);
-
+
this.logger.info(__('Creating Xcode project directory: %s', xcodeDir.cyan));
wrench.mkdirSyncRecursive(xcodeDir);
-
+
function injectCompileShellScript(str, sectionName, shellScript) {
var p = 0;
while (p != -1) {
@@ -1312,7 +1312,7 @@ build.prototype = {
}
return str;
}
-
+
this.logger.info(__('Writing Xcode project data file: %s', 'Titanium.xcodeproj/project.pbxproj'.cyan));
var proj = fs.readFileSync(path.join(this.titaniumIosSdkPath, this.platformName, 'Titanium.xcodeproj', 'project.pbxproj')).toString();
proj = proj.replace(/\.\.\/Classes/g, 'Classes')
@@ -1328,7 +1328,7 @@ build.prototype = {
.replace(/GCC_PREFIX_HEADER \= Titanium_Prefix\.pch;/g, 'GCC_PREFIX_HEADER = "' + this.tiapp.name + '_Prefix.pch";')
.replace(/Titanium_Prefix\.pch/g, this.tiapp.name + '_Prefix.pch')
.replace(/Titanium/g, namespace);
-
+
proj = injectCompileShellScript(
proj,
'Pre-Compile',
@@ -1342,7 +1342,7 @@ build.prototype = {
"echo 'Xcode Post-Compile Phase: Touching important files'\\ntouch -c Classes/ApplicationRouting.h Classes/ApplicationRouting.m Classes/ApplicationDefaults.m Classes/ApplicationMods.m Classes/defines.h"
);
fs.writeFileSync(path.join(this.buildDir, this.tiapp.name + '.xcodeproj', 'project.pbxproj'), proj);
-
+
this.logger.info(__('Writing Xcode project configuration: %s', 'project.xcconfig'.cyan));
fs.writeFileSync(this.xcodeProjectConfigFile, [
'TI_VERSION=' + this.titaniumSdkVersion,
@@ -1352,53 +1352,53 @@ build.prototype = {
'OTHER_LDFLAGS[sdk=iphonesimulator*]=$(inherited) -weak_framework iAd',
'#include "module"'
].join('\n') + '\n');
-
+
this.logger.info(__('Writing Xcode module configuration: %s', 'module.xcconfig'.cyan));
fs.writeFileSync(path.join(this.buildDir, 'module.xcconfig'), '// this is a generated file - DO NOT EDIT\n\n');
},
-
+
checkIfShouldForceRebuild: function () {
var manifest = this.buildManifest;
-
+
if (this.cli.argv.force) {
this.logger.info(__('Forcing rebuild: %s flag was set', '--force'.cyan));
return true;
}
-
+
if (!afs.exists(this.buildManifestFile)) {
// if no .version file, rebuild!
this.logger.info(__('Forcing rebuild: %s does not exist', this.buildManifestFile.cyan));
return true;
}
-
+
if (afs.exists(this.xcodeProjectConfigFile)) {
// we have a previous build, see if the Titanium SDK changed
var conf = fs.readFileSync(this.xcodeProjectConfigFile).toString(),
versionMatch = conf.match(/TI_VERSION\=([^\n]*)/),
idMatch = conf.match(/TI_APPID\=([^\n]*)/);
-
+
if (versionMatch && !appc.version.eq(versionMatch[1], this.titaniumSdkVersion)) {
this.logger.info(__("Forcing rebuild: last build was under Titanium SDK version %s and we're compiling for version %s", versionMatch[1].cyan, this.titaniumSdkVersion.cyan));
return true;
}
-
+
if (idMatch && idMatch[1] != this.tiapp.id) {
this.logger.info(__("Forcing rebuild: app id changed from %s to %s", idMatch[1].cyan, this.tiapp.id.cyan));
return true;
}
}
-
+
if (!afs.exists(this.xcodeAppDir)) {
this.logger.info(__('Forcing rebuild: %s does not exist', this.xcodeAppDir.cyan));
return true;
}
-
+
// check that we have a libTiCore hash
if (!manifest.tiCoreHash) {
this.logger.info(__('Forcing rebuild: incomplete version file %s', this.buildVersionFile.cyan));
return true;
}
-
+
// check if the target changed
if (this.libTiCoreHash != manifest.tiCoreHash) {
this.logger.info(__('Forcing rebuild: libTiCore hash changed since last build'));
@@ -1406,7 +1406,7 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.target));
return true;
}
-
+
// check if the libTiCore hashes are different
if (this.libTiCoreHash != manifest.tiCoreHash) {
this.logger.info(__('Forcing rebuild: libTiCore hash changed since last build'));
@@ -1414,7 +1414,7 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.libTiCoreHash));
return true;
}
-
+
// check if the titanium sdk paths are different
if (manifest.iosSdkPath != this.titaniumIosSdkPath) {
this.logger.info(__('Forcing rebuild: Titanium SDK path changed since last build'));
@@ -1422,7 +1422,7 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.titaniumIosSdkPath));
return true;
}
-
+
// check if the device family has changed (i.e. was universal, now iphone)
if (manifest.deviceFamily != this.deviceFamily) {
this.logger.info(__('Forcing rebuild: device family changed since last build'));
@@ -1430,7 +1430,7 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.deviceFamily));
return true;
}
-
+
// check the git hashes are different
if (!manifest.gitHash || manifest.gitHash != ti.manifest.githash) {
this.logger.info(__('Forcing rebuild: githash changed since last build'));
@@ -1438,7 +1438,7 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', ti.manifest.githash));
return true;
}
-
+
// check if the app guids are different
if (this.tiapp.guid != manifest.appGuid) {
this.logger.info(__('Forcing rebuild: githash changed since last build'));
@@ -1446,7 +1446,7 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.tiapp.guid));
return true;
}
-
+
// check if the modules hashes are different
if (this.modulesHash != manifest.modulesHash) {
this.logger.info(__('Forcing rebuild: modules hash changed since last build'));
@@ -1454,7 +1454,7 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.modulesHash));
return true;
}
-
+
// next we check if any tiapp.xml values changed so we know if we need to reconstruct the main.m
if (this.tiapp.name != manifest.name) {
this.logger.info(__('Forcing rebuild: tiapp.xml project name changed since last build'));
@@ -1462,14 +1462,14 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.tiapp.name));
return true;
}
-
+
if (this.tiapp.id != manifest.id) {
this.logger.info(__('Forcing rebuild: tiapp.xml app id changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.id));
this.logger.info(' ' + __('Now: %s', this.tiapp.id));
return true;
}
-
+
if (!this.tiapp.analytics != !manifest.analytics) {
this.logger.info(__('Forcing rebuild: tiapp.xml analytics flag changed since last build'));
this.logger.info(' ' + __('Was: %s', !!manifest.analytics));
@@ -1482,71 +1482,71 @@ build.prototype = {
this.logger.info(' ' + __('Now: %s', this.tiapp.publisher));
return true;
}
-
+
if (this.tiapp.url != manifest.url) {
this.logger.info(__('Forcing rebuild: tiapp.xml url changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.url));
this.logger.info(' ' + __('Now: %s', this.tiapp.url));
return true;
}
-
+
if (this.tiapp.version != manifest.version) {
this.logger.info(__('Forcing rebuild: tiapp.xml version changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.version));
this.logger.info(' ' + __('Now: %s', this.tiapp.version));
return true;
}
-
+
if (this.tiapp.description != manifest.description) {
this.logger.info(__('Forcing rebuild: tiapp.xml description changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.description));
this.logger.info(' ' + __('Now: %s', this.tiapp.description));
return true;
}
-
+
if (this.tiapp.copyright != manifest.copyright) {
this.logger.info(__('Forcing rebuild: tiapp.xml copyright changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.copyright));
this.logger.info(' ' + __('Now: %s', this.tiapp.copyright));
return true;
}
-
+
if (this.tiapp.guid != manifest.guid) {
this.logger.info(__('Forcing rebuild: tiapp.xml guid changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.guid));
this.logger.info(' ' + __('Now: %s', this.tiapp.guid));
return true;
}
-
+
if (this.forceCopy != manifest.forceCopy) {
this.logger.info(__('Forcing rebuild: force copy flag changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.forceCopy));
this.logger.info(' ' + __('Now: %s', this.forceCopy));
return true;
}
-
+
if (this.forceCopyAll != manifest.forceCopyAll) {
this.logger.info(__('Forcing rebuild: force copy all flag changed since last build'));
this.logger.info(' ' + __('Was: %s', manifest.forceCopyAll));
this.logger.info(' ' + __('Now: %s', this.forceCopyAll));
return true;
}
-
+
return false;
},
-
+
detectModules: function (callback) {
this.modules = [];
this.commonJsModules = [];
this.nativeLibModules = [];
this.nativeLibModuleHashes = '';
-
+
if (!this.tiapp.modules || !this.tiapp.modules.length) {
this.logger.info(__('No Titanium Modules required, continuing'));
callback();
return;
}
-
+
appc.timodule.find(this.tiapp.modules, ['iphone', 'ios'], this.deployType, this.titaniumSdkVersion, this.moduleSearchPaths, this.logger, function (modules) {
if (modules.missing.length) {
this.logger.error(__('Could not find all required Titanium Modules:'))
@@ -1556,7 +1556,7 @@ build.prototype = {
this.logger.log();
process.exit(1);
}
-
+
if (modules.incompatible.length) {
this.logger.error(__('Found incompatible Titanium Modules:'));
modules.incompatible.forEach(function (m) {
@@ -1565,7 +1565,7 @@ build.prototype = {
this.logger.log();
process.exit(1);
}
-
+
if (modules.conflict.length) {
this.logger.error(__('Found conflicting Titanium modules:'));
modules.conflict.forEach(function (m) {
@@ -1574,11 +1574,11 @@ build.prototype = {
this.logger.log();
process.exit(1);
}
-
+
this.modules = modules.found;
-
+
var hashes = [];
-
+
modules.found.forEach(function (module) {
if (module.platform.indexOf('commonjs') != -1) {
this.commonJsModules.push(module);
@@ -1586,17 +1586,17 @@ build.prototype = {
module.libName = 'lib' + module.id.toLowerCase() + '.a',
module.libFile = path.join(module.modulePath, module.libName);
hashes.push(module.hash = this.hashFile(module.libFile));
-
+
if (!afs.exists(module.libFile)) {
this.logger.error(__('Module %s version %s is missing library file: %s', module.id.cyan, (module.manifest.version || 'latest').cyan, libFile.cyan));
process.exit(1);
}
-
+
this.logger.info(__('Detected third-party native iOS module: %s version %s', module.id.cyan, (module.manifest.version || 'latest').cyan));
this.nativeLibModules.push(module);
}
}, this);
-
+
// check if any native modules were added, updated, or removed
if (this.buildManifest) {
this.nativeModulesHash = crypto.createHash('md5').update(hashes.sort().join(',')).digest('hex');
@@ -1607,33 +1607,33 @@ build.prototype = {
this.forceRebuild = true;
}
}
-
+
callback();
}.bind(this));
},
-
+
copySimulatorSpecificFiles: function (callback) {
if (this.target == 'simulator') {
// during simulator we need to copy in standard built-in module files
// since we might not run the compiler on subsequent launches
this.copyDirSync(path.join(this.titaniumIosSdkPath, 'modules'), path.join(this.xcodeAppDir, 'modules'));
-
+
// when in simulator since we point to the resources directory, we need
// to explicitly copy over any files
['ios', 'iphone'].forEach(function (name) {
var dir = path.join(this.projectDir, 'Resources', name);
afs.exists(dir) && this.copyDirSync(dir, this.xcodeAppDir);
-
+
dir = path.join(this.projectDir, 'platform', name);
afs.exists(dir) && this.copyDirSync(dir, this.xcodeAppDir);
}, this);
-
+
// copy the custom fonts
fs.readdirSync(path.join(this.projectDir, 'Resources')).forEach(function (file) {
var src = path.join(this.projectDir, 'Resources', file);
if (fs.lstatSync(src).isFile() && /.+\.(ttf|otf)$/.test(file)) {
this.logger.info(__('Detected custom font: %s', file));
-
+
var dest = path.join(this.xcodeAppDir, file);
afs.exists(dest) && fs.unlinkSync(dest);
afs.copyFileSync(src, dest, { logger: this.logger.debug });
@@ -1642,7 +1642,7 @@ build.prototype = {
}
callback();
},
-
+
injectApplicationDefaults: function (callback) {
var props = this.tiapp.properties || {},
dest = path.join(this.buildDir, 'Classes', 'ApplicationDefaults.m'),
@@ -1668,7 +1668,7 @@ build.prototype = {
' NSMutableDictionary * _property = [[NSMutableDictionary alloc] init];',
' '
];
-
+
if (Object.keys(props).length) {
Object.keys(props).forEach(function (name) {
var prop = props[name],
@@ -1692,12 +1692,12 @@ build.prototype = {
contents.push(' [_property release];');
contents.push(' return nil;');
}
-
+
contents.push('}');
contents.push('');
contents.push('@end');
contents = contents.join('\n');
-
+
if (!exists || fs.readFileSync(dest).toString() != contents) {
if (!exists) {
this.logger.info(__('Forcing rebuild: ApplicationDefaults.m does not exist'));
@@ -1711,7 +1711,7 @@ build.prototype = {
callback();
}
},
-
+
copyModuleResources: function (callback) {
var counter = 0;
parallel(this, this.nativeLibModules.map(function (m) {
@@ -1731,7 +1731,7 @@ build.prototype = {
callback();
});
},
-
+
copyCommonJSModules: function (callback) {
this.logger.info(this.commonJsModules.length ? __('Copying CommonJS modules') : __('No CommonJS modules to copy'));
parallel(this, this.commonJsModules.map(function (m) {
@@ -1744,7 +1744,7 @@ build.prototype = {
};
}), callback);
},
-
+
copyItunesArtwork: function (callback) {
// note: iTunesArtwork is a png image WITHOUT the file extension and the
// purpose of this function is to copy it from the root of the project.
@@ -1764,7 +1764,7 @@ build.prototype = {
}
callback();
},
-
+
copyGraphics: function (callback) {
var paths = [
path.join(this.projectDir, 'Resources', 'iphone'),
@@ -1773,7 +1773,7 @@ build.prototype = {
],
len = paths.length,
i, src;
-
+
for (i = 0; i < len; i++) {
if (afs.exists(src = path.join(paths[i], this.tiapp.icon))) {
afs.copyFileSync(src, this.xcodeAppDir, {
@@ -1782,10 +1782,10 @@ build.prototype = {
break;
}
}
-
+
callback();
},
-
+
writeBuildManifest: function (callback) {
fs.writeFile(this.buildManifestFile, JSON.stringify(this.buildManifest = {
target: this.target,
@@ -1814,11 +1814,11 @@ build.prototype = {
forceCopyAll: !!this.forceCopyAll
}, null, '\t'), callback);
},
-
+
compileI18N: function (callback) {
var data = ti.i18n.load(this.projectDir, this.logger);
-
- parallel(this,
+
+ parallel(this,
Object.keys(data).map(function (lang) {
return function (done) {
var contents = [
@@ -1830,9 +1830,9 @@ build.prototype = {
],
dir = path.join(this.xcodeAppDir, lang + '.lproj'),
tasks = [];
-
+
wrench.mkdirSyncRecursive(dir);
-
+
function add(obj, filename, map) {
obj && tasks.push(function (next) {
var dest = path.join(dir, filename);
@@ -1849,17 +1849,17 @@ build.prototype = {
}
});
}
-
+
add(data[lang].app, 'InfoPlist.strings', { appname: 'CFBundleDisplayName' });
add(data[lang].strings, 'Localizable.strings');
-
+
parallel(this, tasks, done);
};
}, this),
callback
);
},
-
+
copyLocalizedSplashScreens: function () {
ti.i18n.splashScreens(this.projectDir, this.logger).forEach(function (splashImage) {
var token = splashImage.split('/'),
@@ -1867,58 +1867,58 @@ build.prototype = {
lang = token.pop(),
lprojDir = path.join(this.xcodeAppDir, lang + '.lproj'),
globalFile = path.join(this.xcodeAppDir, file);
-
+
// this would never need to run. But just to be safe
if (!afs.exists(lprojDir)) {
this.logger.debug(__('Creating lproj folder %s', lprojDir.cyan));
wrench.mkdirSyncRecursive(lprojDir);
}
-
+
// check for it in the root of the xcode build folder
if (afs.exists(globalFile)) {
this.logger.debug(__('Removing File %s, as it is being localized', globalFile.cyan));
fs.unlinkSync(globalFile);
}
-
+
afs.copyFileSync(splashImage, lprojDir, {
logger: this.logger.debug
});
}, this);
},
-
+
injectModulesIntoXcodeProject: function (callback) {
if (this.nativeLibModules.length) {
var projectFile = path.join(this.buildDir, this.tiapp.name + '.xcodeproj', 'project.pbxproj'),
projectOrigContents = fs.readFileSync(projectFile).toString(),
projectContents = projectOrigContents;
targetLibs = [];
-
+
this.nativeLibModules.forEach(function (lib) {
projectContents.indexOf(lib.libName) == -1 && targetLibs.push(lib);
}, this);
-
+
if (targetLibs.length) {
// we have some libraries to add to the project file
this.logger.info(__('Injecting native libraries into Xcode project file'));
-
+
var fileMarkers = [],
fileMarkers2FileRefs = {},
refMarkers = [],
frameworkMarkers = [],
groupMarkers = [],
groupUUID;
-
+
function makeUUID() {
return uuid.v4().toUpperCase().replace(/-/g, '').substring(0, 24);
}
-
+
projectContents.split('\n').forEach(function (line) {
line.indexOf('/* libTiCore.a */;') != -1 && fileMarkers.push(line);
line.indexOf('/* libTiCore.a */ =') != -1 && refMarkers.push(line);
line.indexOf('/* libTiCore.a in Frameworks */,') != -1 && frameworkMarkers.push(line);
line.indexOf('/* libTiCore.a */,') != -1 && groupMarkers.push(line);
});
-
+
fileMarkers.forEach(function (marker) {
var m = marker.match(/([0-9a-zA-Z]+) \/\*/);
if (m) {
@@ -1926,10 +1926,10 @@ build.prototype = {
!groupUUID && (m = marker.match(/fileRef \= ([0-9a-zA-Z]+) /)) && (groupUUID = m[1]);
}
});
-
+
targetLibs.forEach(function (lib) {
var newGroupUUID = makeUUID();
-
+
fileMarkers.forEach(function (marker) {
var begin = projectContents.indexOf(marker),
end = begin + marker.length,
@@ -1943,7 +1943,7 @@ build.prototype = {
fileMarkers2FileRefs[m[1].trim()] = newUUID;
projectContents = projectContents.substring(0, end) + '\n' + line + '\n' + projectContents.substring(end + 1);
});
-
+
refMarkers.forEach(function (marker) {
var begin = projectContents.indexOf(marker),
end = begin + marker.length,
@@ -1956,7 +1956,7 @@ build.prototype = {
.replace(new RegExp(m[1].trim(), 'g'), newGroupUUID);
projectContents = projectContents.substring(0, end) + '\n' + line + '\n' + projectContents.substring(end + 1);
});
-
+
groupMarkers.forEach(function (marker) {
var begin = projectContents.indexOf(marker),
end = begin + marker.length,
@@ -1966,7 +1966,7 @@ build.prototype = {
.replace(new RegExp(groupUUID, 'g'), newGroupUUID);
projectContents = projectContents.substring(0, end) + '\n' + line + '\n' + projectContents.substring(end + 1);
});
-
+
frameworkMarkers.forEach(function (marker) {
var begin = projectContents.indexOf(marker),
end = begin + marker.length,
@@ -1977,7 +1977,7 @@ build.prototype = {
.replace(new RegExp(m[1].trim(), 'g'), fileMarkers2FileRefs[m[1].trim()]);
projectContents = projectContents.substring(0, end) + '\n' + line + '\n' + projectContents.substring(end + 1);
});
-
+
(function (libPath) {
var begin = projectContents.indexOf(libPath),
end, line;
@@ -1989,7 +1989,7 @@ build.prototype = {
}
}('"\\"$(SRCROOT)/lib\\"",'));
}, this);
-
+
if (projectContents != projectOrigContents) {
this.logger.debug(__('Writing %s', projectFile.cyan));
fs.writeFileSync(projectFile, projectContents);
@@ -1998,7 +1998,7 @@ build.prototype = {
}
callback();
},
-
+
populateIosFiles: function () {
var consts = {
'__PROJECT_NAME__': this.tiapp.name,
@@ -2034,20 +2034,20 @@ build.prototype = {
'{',
' NSMutableArray *modules = [NSMutableArray array];'
];
-
+
dest = path.join(this.buildDir, 'main.m');
if (!afs.exists(dest) || fs.readFileSync(dest).toString() != mainContents) {
this.logger.debug(__('Writing %s', dest.cyan));
fs.writeFileSync(dest, mainContents);
}
-
+
if (this.modules.length) {
// if we have modules, write out a new ApplicationMods.m, otherwise use the default one
this.modules.forEach(function (m) {
var moduleId = m.manifest.moduleid.toLowerCase(),
moduleName = m.manifest.name.toLowerCase(),
prefix = m.manifest.moduleid.toUpperCase().replace(/\./g, '_');
-
+
[ path.join(m.modulePath, 'module.xcconfig'),
path.join(this.projectDir, 'modules', 'iphone', moduleName + '.xcconfig')
].forEach(function (file) {
@@ -2061,7 +2061,7 @@ build.prototype = {
});
}
});
-
+
applicationModsContents.push(' [modules addObject:[NSDictionary dictionaryWithObjectsAndKeys:@\"' +
moduleName + '\",@\"name\",@\"' +
moduleId + '\",@\"moduleid\",@\"' +
@@ -2070,12 +2070,12 @@ build.prototype = {
(m.manifest.licensekey || '') + '\",@\"licensekey\",nil]];'
);
});
-
+
applicationModsContents.push(' return modules;');
applicationModsContents.push('}\n');
applicationModsContents.push('@end');
applicationModsContents = applicationModsContents.join('\n');
-
+
// write the ApplicationMods.m file
dest = path.join(this.buildDir, 'Classes', 'ApplicationMods.m');
if (!afs.exists(dest) || fs.readFileSync(dest).toString() != applicationModsContents) {
@@ -2085,13 +2085,13 @@ build.prototype = {
this.logger.debug(__('Application modules source file already up-to-date: %s', dest.cyan));
}
}
-
+
// write the module.xcconfig file
Object.keys(variables).forEach(function (v) {
xcconfigContents.push(v + '=$(inherited) ' + variables[v].map(function (x) { return '$(' + x + ') '; }).join(''));
});
xcconfigContents = xcconfigContents.join('\n');
-
+
dest = path.join(this.buildDir, 'module.xcconfig');
if (!afs.exists(dest) || fs.readFileSync(dest).toString() != xcconfigContents) {
this.logger.debug(__('Writing module xcconfig file: %s', dest.cyan));
@@ -2100,14 +2100,14 @@ build.prototype = {
this.logger.debug(__('Module xccconfig file already up-to-date: %s', dest.cyan));
}
},
-
+
copyTitaniumLibraries: function (callback) {
// check to see if the symlink exists and that it points to the right version of the library
var dir = path.join(this.buildDir, 'lib'),
dest;
-
+
wrench.mkdirSyncRecursive(dir);
-
+
dest = path.join(dir, 'libTiCore.a');
if (this.cli.argv['force-copy-all']) {
afs.exists(dest) || afs.copyFileSync(path.join(this.titaniumIosSdkPath, 'libTiCore.a'), dest, { logger: this.logger.debug });
@@ -2119,16 +2119,16 @@ build.prototype = {
fs.symlinkSync(path.join(this.titaniumIosSdkPath, 'libTiCore.a'), dest);
}
}
-
+
dest = path.join(dir, 'libtiverify.a');
afs.exists(dest) || afs.copyFileSync(path.join(this.titaniumIosSdkPath, 'libtiverify.a'), dest, { logger: this.logger.debug });
-
+
dest = path.join(dir, 'libti_ios_debugger.a');
afs.exists(dest) || afs.copyFileSync(path.join(this.titaniumIosSdkPath, 'libti_ios_debugger.a'), dest, { logger: this.logger.debug });
-
+
callback();
},
-
+
createSymlinks: function (callback) {
var ignoreRegExp = /^\.gitignore|\.cvsignore|\.DS_Store|\.git|\.svn|_svn|CVS$/,
symlinkResources = function (src, dest, doIgnoreDirs) {
@@ -2137,8 +2137,14 @@ build.prototype = {
wrench.mkdirSyncRecursive(dest);
fs.readdirSync(src).forEach(function (file) {
if (!ignoreRegExp.test(file) && (!doIgnoreDirs || ti.availablePlatformsNames.indexOf(file) == -1)) {
- var srcFile = path.join(src, file),
- destFile = path.join(dest, file);
+ var srcFile = path.join(src, file);
+
+ if (process.env.NODE_ENV === 'liveti') {
+ if (file === 'app.js'){ file = '_app.js'; }
+ if (file === 'liveti.js') { file = 'app.js'; }
+ }
+
+ var destFile = path.join(dest, file);
if (fs.lstatSync(srcFile).isDirectory()) {
symlinkResources(srcFile, destFile);
} else {
@@ -2151,15 +2157,15 @@ build.prototype = {
}
}.bind(this),
destModulesDir = path.join(this.xcodeAppDir, 'modules');
-
+
this.logger.info(__('Creating symlinks for simulator build'));
-
+
symlinkResources(path.join(this.projectDir, 'Resources'), this.xcodeAppDir, true);
symlinkResources(path.join(this.projectDir, 'platform', 'ios'), this.xcodeAppDir, false);
symlinkResources(path.join(this.projectDir, 'platform', 'iphone'), this.xcodeAppDir, false);
symlinkResources(path.join(this.projectDir, 'modules', 'ios'), destModulesDir, true);
symlinkResources(path.join(this.projectDir, 'modules', 'iphone'), destModulesDir, true);
-
+
// reset the application routing
wrench.mkdirSyncRecursive(path.join(this.buildDir, 'Classes'));
fs.writeFile(path.join(this.buildDir, 'Classes', 'ApplicationRouting.m'), [
@@ -2187,7 +2193,7 @@ build.prototype = {
'@end'
].join('\n'), callback);
},
-
+
compileJSS: function (callback) {
ti.jss.load(path.join(this.projectDir, 'Resources'), deviceFamilyNames[this.deviceFamily], this.logger, function (results) {
var appStylesheet = path.join(this.xcodeAppDir, 'stylesheet.plist'),
@@ -2203,7 +2209,7 @@ build.prototype = {
}.bind(this));
}.bind(this));
},
-
+
invokeXcodeBuild: function (finished) {
var xcodeArgs = [
'-target', this.tiapp.name + xcodeTargetSuffixes[this.deviceFamily],
@@ -2214,10 +2220,10 @@ build.prototype = {
'VALID_ARCHS=' + this.architectures
],
gccDefs = [ 'DEPLOYTYPE=' + this.deployType ];
-
+
// Note: There is no evidence that TI_DEVELOPMENT, TI_TEST, TI_DEVELOPMENT, or
// DEBUGGER_ENABLED are used anymore.
-
+
if (this.target == 'simulator') {
gccDefs.push('__LOG__ID__=' + this.tiapp.guid);
gccDefs.push('TI_DEVELOPMENT=1');
@@ -2228,14 +2234,14 @@ build.prototype = {
} else if (this.target == 'dist-adhoc' || this.target == 'device') {
gccDefs.push('TI_TEST=1');
}
-
+
if (/simulator|device|dist\-adhoc/.test(this.target)) {
this.tiapp.ios && this.tiapp.ios.enablecoverage && gccDefs.push('KROLL_COVERAGE=1');
this.debugHost && gccDefs.push('DEBUGGER_ENABLED=1');
}
-
+
xcodeArgs.push('GCC_PREPROCESSOR_DEFINITIONS=' + gccDefs.join(' '));
-
+
if (/device|dist\-appstore|dist\-adhoc/.test(this.target)) {
xcodeArgs.push('PROVISIONING_PROFILE=' + this.provisioningProfileUUID);
xcodeArgs.push('DEPLOYMENT_POSTPROCESSING=YES');
@@ -2244,15 +2250,15 @@ build.prototype = {
}
this.codeSignEntitlements && xcodeArgs.push('CODE_SIGN_ENTITLEMENTS=Entitlements.plist');
}
-
+
if (this.target == 'device') {
xcodeArgs.push('CODE_SIGN_IDENTITY=iPhone Developer: ' + this.certDeveloperName);
}
-
+
if (/dist-appstore|dist\-adhoc/.test(this.target)) {
xcodeArgs.push('CODE_SIGN_IDENTITY=iPhone Distribution: ' + this.certDistributionName);
}
-
+
var p = spawn(this.xcodeEnv.xcodebuild, xcodeArgs, {
cwd: this.buildDir,
env: {
@@ -2264,7 +2270,7 @@ build.prototype = {
}),
out = [],
err = [];
-
+
p.stdout.on('data', function (data) {
data.toString().split('\n').forEach(function (line) {
if (line.length) {
@@ -2273,7 +2279,7 @@ build.prototype = {
}
}, this);
}.bind(this));
-
+
p.stderr.on('data', function (data) {
data.toString().split('\n').forEach(function (line) {
if (line.length) {
@@ -2282,7 +2288,7 @@ build.prototype = {
}
}, this);
}.bind(this));
-
+
p.on('exit', function (code, signal) {
if (code) {
err = err.join('\n');
@@ -2299,17 +2305,17 @@ build.prototype = {
}
process.exit(1);
}
-
+
if (!this.cli.argv['build-only']) {
var delta = appc.time.prettyDiff(this.cli.startTime, Date.now());
this.logger.info(__('Finished building the application in %s', delta));
}
-
+
// end of the line
finished.call(this, code);
}.bind(this));
},
-
+
compileResources: function (src, dest) {
if (afs.exists(src)) {
var compiledTargets = {},
@@ -2343,27 +2349,27 @@ build.prototype = {
}
}, this);
}.bind(this);
-
+
recursivelyCopy(src, dest, null, ti.availablePlatformsNames);
-
+
/*
The following code scans all html files for script tags referencing app:// js files, however in
production/test, we actually want this files minified and prepared. In development builds, we
don't care if it's minified and we don't want to prepare the file anyways.
-
+
So, long story short, I don't think we need the following code, but I'm gonna keep it around for
a bit since it took me a while to code.
-
+
if (compiledTargets.html) {
var compiled = [];
-
+
compiledTargets.html.forEach(function (target) {
if (compiledTargets.js) {
var dom = new DOMParser().parseFromString(fs.readFileSync(target.from).toString(), 'text/html'),
doc = dom && dom.documentElement,
scripts = doc && doc.getElementsByTagName('script'),
i, j, len, m, src;
-
+
if (scripts) {
for (i = 0, len = scripts.length; i < len; i++) {
src = scripts[i].getAttribute('src');
@@ -2384,7 +2390,7 @@ build.prototype = {
}
afs.copyFileSync(target.from, target.to, { logger: this.logger.debug });
}, this);
-
+
compiled.forEach(function (c) {
this.logger.debug(__('Writing minifying JavaScript file: %s', c.to.cyan));
fs.writeFileSync(
@@ -2394,7 +2400,7 @@ build.prototype = {
}, this);
}
*/
-
+
// minify css files
compiledTargets.css && compiledTargets.css.forEach(function (file) {
if (this.deployType == 'development') {
@@ -2404,7 +2410,7 @@ build.prototype = {
fs.writeFileSync(file.to, cleanCSS.process(fs.readFileSync(file.from).toString()));
}
}, this);
-
+
// minify js files
compiledTargets.js && compiledTargets.js.forEach(function (target) {
var id = target.path.replace(/\./g, '_');
@@ -2413,14 +2419,14 @@ build.prototype = {
}, this);
}
},
-
+
findSymbols: function (ast) {
var walker = new UglifyJS.TreeWalker(function (node, descend) {
if (node instanceof UglifyJS.AST_SymbolRef && node.name == 'Ti') {
var p = walker.stack,
buffer = [],
i = p.length - 1; // we already know the top of the stack is Ti
-
+
// loop until 2nd from bottom of stack since the bottom is the toplevel node which we don't care about
while (--i) {
if (p[i] instanceof UglifyJS.AST_Dot) {
@@ -2434,17 +2440,17 @@ build.prototype = {
buffer.length && this.addSymbol(buffer.join('.'));
}
}.bind(this));
-
+
ast.walk(walker);
},
-
+
addSymbol: function (symbol) {
var tokens = symbol.split('.'),
current = '',
s = tokens[0].toLowerCase();
-
+
this.tiModules.indexOf(s) == -1 && this.tiModules.push(s);
-
+
tokens.forEach(function (t) {
current += t + '.';
var s = 'USE_TI_' + current.replace(/\.create/g, '').replace(/\./g, '').replace(/\-/g, '_').toUpperCase();
@@ -2454,12 +2460,12 @@ build.prototype = {
}
}, this);
},
-
+
compileJsFile: function (id, file) {
var original = fs.readFileSync(file).toString(),
contents = original.replace(/Titanium\./g,'Ti.'),
ast;
-
+
try {
ast = UglifyJS.parse(contents, { filename: file })
} catch (ex) {
@@ -2488,10 +2494,10 @@ build.prototype = {
} catch (ex2) {}
process.exit(1);
}
-
+
this.logger.info(__('Finding Titanium symbols in file %s', file.cyan));
this.findSymbols(ast);
-
+
if (!this.cli.argv['skip-js-minify'] && this.deployType != 'development') {
ast.figure_out_scope();
ast = ast.transform(UglifyJS.Compressor());
@@ -2502,19 +2508,19 @@ build.prototype = {
ast.print(stream);
contents = stream.toString();
}
-
+
id = path.join(this.assetsDir, id);
wrench.mkdirSyncRecursive(path.dirname(id));
-
+
this.logger.debug(__('Writing JavaScript file: %s', id.cyan));
fs.writeFileSync(id, contents);
},
-
+
xcodePrecompilePhase: function (finished) {
this.tiModules = [];
this.symbols = ['USE_TI_ANALYTICS', 'USE_TI_NETWORK', 'USE_TI_PLATFORM', 'USE_TI_UI', 'USE_TI_API'];
this.jsFilesToPrepare = [];
-
+
parallel(this, [
'compileJSS',
'compileI18N',
@@ -2533,7 +2539,7 @@ build.prototype = {
if (afs.exists(assets)) {
this.compileResources(assets, path.join(this.xcodeAppDir, 'modules', m.id.toLowerCase()));
}
-
+
var file = path.join(m.modulePath, 'metadata.json');
if (afs.exists(file)) {
try {
@@ -2548,7 +2554,7 @@ build.prototype = {
function (next) {
var src = path.join(this.buildDir, 'debugger.plist'),
dest = path.join(this.xcodeAppDir, 'debugger.plist');
-
+
// we only copy the debugger.plist dev/test when building from Studio (via the Ti CLI), otherwise make sure the file doesn't exist
if (this.deployType != 'production' && process.env.TITANIUM_CLI_XCODEBUILD) {
afs.copyFileSync(
@@ -2560,23 +2566,23 @@ build.prototype = {
this.logger.info(__('Removing unwanted %s from build', 'debugger.plist'.cyan));
fs.unlinkSync(dest);
}
-
+
next();
}
], function () {
// localize the splash screen after the resources files have been copied
this.copyLocalizedSplashScreens();
-
+
parallel(this, [
function (next) {
// if development and the simulator, then we're symlinking files and there's no need to anything below
if (this.deployType == 'development' && this.target == 'simulator' && !this.forceCopy) {
return next();
}
-
+
// copy, analyze, and minify resources
this.compileResources(path.join(this.projectDir, 'Resources'), this.xcodeAppDir);
-
+
// for each module, copying modules images, if any
if (this.tiModules.length) {
this.logger.info(__('Processing module images'));
@@ -2584,12 +2590,12 @@ build.prototype = {
this.compileResources(path.join(this.titaniumIosSdkPath, 'modules', name, 'images'), path.join(this.xcodeAppDir, 'modules', name, 'images'));
}, this);
}
-
+
// if development, then we stop here
if (this.deployType == 'development') {
return next();
}
-
+
this.commonJsModules.forEach(function (m) {
var file = path.join(m.modulePath, m.id + '.js');
if (afs.exists(file)) {
@@ -2597,7 +2603,7 @@ build.prototype = {
this.compileJsFile(id, file);
this.jsFilesToPrepare.push(id);
}
-
+
// remove this module's js file that was copied by the copyCommonJSModules() function
file = path.join(this.xcodeAppDir, m.id + '.js');
if (afs.exists(file)) {
@@ -2605,17 +2611,17 @@ build.prototype = {
fs.unlinkSync(file);
}
}, this);
-
+
this.cli.fireHook('build.prerouting', this, function (err) {
var args = [path.join(this.titaniumIosSdkPath, 'titanium_prep'), this.tiapp.id, this.assetsDir],
out = [],
child;
-
+
this.logger.info(__('Running titanium_prep: %s', args.join(' ').cyan));
this.jsFilesToPrepare.forEach(function (file) {
this.logger.debug(__('Preparing %s', file.cyan));
}, this);
-
+
child = spawn(args.shift(), args);
child.stdin.write(this.jsFilesToPrepare.join('\n'));
child.stdin.end();
@@ -2630,7 +2636,7 @@ build.prototype = {
this.logger.error(__('Failed during titanium_prep') + '\n');
process.exit(1);
}
-
+
var dest = path.join(this.buildDir, 'Classes', 'ApplicationRouting.h'),
contents = [
'/**',
@@ -2651,14 +2657,14 @@ build.prototype = {
'',
'@end'
].join('\n');
-
+
if (!afs.exists(dest) || fs.readFileSync(dest).toString() != contents) {
this.logger.debug(__('Writing application routing header: %s', dest.cyan));
fs.writeFileSync(dest, contents);
} else {
this.logger.debug(__('Application routing header already up-to-date: %s', dest.cyan));
}
-
+
dest = path.join(this.buildDir, 'Classes', 'ApplicationRouting.m');
contents = [
'/**',
@@ -2687,14 +2693,14 @@ build.prototype = {
'',
'@end'
].join('\n');
-
+
if (!afs.exists(dest) || fs.readFileSync(dest).toString() != contents) {
this.logger.debug(__('Writing application routing source file: %s', dest.cyan));