Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Untitled diff

Created Diff never expires
1 removal
717 lines
1 addition
717 lines
/**
/**
* Titanium SDK Library for Node.js
* Titanium SDK Library for Node.js
* Copyright (c) 2012-2013 by Appcelerator, Inc. All Rights Reserved.
* Copyright (c) 2012-2013 by Appcelerator, Inc. All Rights Reserved.
* Please see the LICENSE file for information about licensing.
* Please see the LICENSE file for information about licensing.
*/
*/
var fs = require('fs'),
var fs = require('fs'),
path = require('path'),
path = require('path'),
async = require('async'),
async = require('async'),
appc = require('node-appc'),
appc = require('node-appc'),
__ = appc.i18n(__dirname).__,
__ = appc.i18n(__dirname).__,
spawn = require('child_process').spawn,
spawn = require('child_process').spawn,
afs = appc.fs,
afs = appc.fs,
version = appc.version,
version = appc.version,
manifest = appc.pkginfo.manifest(module),
manifest = appc.pkginfo.manifest(module),
platformAliases = {
platformAliases = {
// add additional aliases here for new platforms
// add additional aliases here for new platforms
'ipad': 'iphone',
'ipad': 'iphone',
'ios': 'iphone'
'ios': 'iphone'
};
};


exports.i18n = require('./i18n');
exports.i18n = require('./i18n');
exports.jss = require('./jss');
exports.jss = require('./jss');
exports.tiappxml = require('./tiappxml');
exports.tiappxml = require('./tiappxml');


exports.manifest = manifest;
exports.manifest = manifest;
exports.platforms = [].concat(manifest.platforms);
exports.platforms = [].concat(manifest.platforms);
exports.targetPlatforms = (manifest.platforms || []).map(function (p) {
exports.targetPlatforms = (manifest.platforms || []).map(function (p) {
return p == 'iphone' ? 'ios' : p;
return p == 'iphone' ? 'ios' : p;
}).sort();
}).sort();
exports.availablePlatforms = (manifest.platforms || []).sort();
exports.availablePlatforms = (manifest.platforms || []).sort();
exports.availablePlatformsNames = function (platforms) {
exports.availablePlatformsNames = function (platforms) {
Object.keys(platformAliases).forEach(function (alias) {
Object.keys(platformAliases).forEach(function (alias) {
if (platforms.indexOf(platformAliases[alias]) != -1) {
if (platforms.indexOf(platformAliases[alias]) != -1) {
platforms.push(alias);
platforms.push(alias);
}
}
});
});
return platforms.sort();
return platforms.sort();
}(manifest.platforms || []);
}(manifest.platforms || []);


exports.commonOptions = function (logger, config) {
exports.commonOptions = function (logger, config) {
return {
return {
'log-level': {
'log-level': {
callback: function (value) {
callback: function (value) {
logger.levels.hasOwnProperty(value) && logger.setLevel(value);
logger.levels.hasOwnProperty(value) && logger.setLevel(value);
},
},
desc: __('minimum logging level'),
desc: __('minimum logging level'),
default: config.cli.logLevel || 'trace',
default: config.cli.logLevel || 'trace',
hint: __('level'),
hint: __('level'),
values: logger.getLevels()
values: logger.getLevels()
}
}
};
};
};
};


exports.platformOptions = function (logger, config, cli, commandName, finished) {
exports.platformOptions = function (logger, config, cli, commandName, finished) {
var result = {};
var result = {};


if (!commandName) {
if (!commandName) {
finished(result);
finished(result);
return;
return;
}
}


function set(obj, title, platform) {
function set(obj, title, platform) {
// add the platform and title to the options and flags
// add the platform and title to the options and flags
['options', 'flags'].forEach(function (type) {
['options', 'flags'].forEach(function (type) {
if (obj[type]) {
if (obj && obj[type]) {
result[platform] || (result[platform] = {
result[platform] || (result[platform] = {
platform: platform,
platform: platform,
title: title || platform
title: title || platform
});
});
result[platform][type] = obj[type];
result[platform][type] = obj[type];
}
}
});
});
}
}


// for each platform, fetch their specific flags/options
// for each platform, fetch their specific flags/options
async.parallel(manifest.platforms.map(function (platform) {
async.parallel(manifest.platforms.map(function (platform) {
return function (callback) {
return function (callback) {
var platformDir = path.join(path.dirname(module.filename), '..', '..', '..', platform),
var platformDir = path.join(path.dirname(module.filename), '..', '..', '..', platform),
platformCommand = path.join(platformDir, 'cli', 'commands', '_' + commandName + '.js'),
platformCommand = path.join(platformDir, 'cli', 'commands', '_' + commandName + '.js'),
command,
command,
conf,
conf,
title;
title;


if (!fs.existsSync(platformCommand)) return callback();
if (!fs.existsSync(platformCommand)) return callback();


command = require(platformCommand);
command = require(platformCommand);
if (!command || !command.config) return callback();
if (!command || !command.config) return callback();


// try to get the platform specific configuration
// try to get the platform specific configuration
conf = command.config(logger, config, cli);
conf = command.config(logger, config, cli);


try {
try {
// try to read a title from the platform's package.json
// try to read a title from the platform's package.json
title = JSON.parse(fs.readFileSync(path.join(platformDir, 'package.json'))).title;
title = JSON.parse(fs.readFileSync(path.join(platformDir, 'package.json'))).title;
} catch (e) {}
} catch (e) {}


if (typeof conf == 'function') {
if (typeof conf == 'function') {
// async callback
// async callback
conf(function (obj) {
conf(function (obj) {
set(obj, title, platform);
set(obj, title, platform);
callback();
callback();
});
});
return;
return;
}
}


set(conf, title, platform);
set(conf, title, platform);
callback();
callback();
};
};
}), function () {
}), function () {
finished(result);
finished(result);
});
});
};
};


exports.validateProjectDir = function(logger, cli, argv, name) {
exports.validateProjectDir = function(logger, cli, argv, name) {
var dir = argv[name] || (process.env.SOURCE_ROOT ? path.join(process.env.SOURCE_ROOT, '..', '..') : '.'),
var dir = argv[name] || (process.env.SOURCE_ROOT ? path.join(process.env.SOURCE_ROOT, '..', '..') : '.'),
projectDir = argv[name] = appc.fs.resolvePath(dir);
projectDir = argv[name] = appc.fs.resolvePath(dir);


if (!fs.existsSync(projectDir)) {
if (!fs.existsSync(projectDir)) {
logger.banner();
logger.banner();
logger.error(__('Project directory does not exist') + '\n');
logger.error(__('Project directory does not exist') + '\n');
process.exit(1);
process.exit(1);
}
}


var tiapp = path.join(projectDir, 'tiapp.xml');
var tiapp = path.join(projectDir, 'tiapp.xml');
while (!fs.existsSync(tiapp) && tiapp.split(path.sep).length > 2) {
while (!fs.existsSync(tiapp) && tiapp.split(path.sep).length > 2) {
projectDir = argv[name] = path.dirname(projectDir);
projectDir = argv[name] = path.dirname(projectDir);
tiapp = path.join(projectDir, 'tiapp.xml');
tiapp = path.join(projectDir, 'tiapp.xml');
}
}


if (tiapp.split(path.sep).length == 2) {
if (tiapp.split(path.sep).length == 2) {
logger.banner();
logger.banner();
logger.error(__('Invalid project directory "%s"', dir) + '\n');
logger.error(__('Invalid project directory "%s"', dir) + '\n');
dir == '.' && logger.log(__("Use the %s property to specify the project's directory", '--project-dir'.cyan) + '\n');
dir == '.' && logger.log(__("Use the %s property to specify the project's directory", '--project-dir'.cyan) + '\n');
process.exit(1);
process.exit(1);
}
}


// load the tiapp.xml
// load the tiapp.xml
cli.tiapp = new exports.tiappxml(path.join(projectDir, 'tiapp.xml'));
cli.tiapp = new exports.tiappxml(path.join(projectDir, 'tiapp.xml'));
};
};


exports.validateTiappXml = function (logger, config, tiapp) {
exports.validateTiappXml = function (logger, config, tiapp) {
if (!tiapp.id) {
if (!tiapp.id) {
logger.error(__('tiapp.xml is missing the <id> element'));
logger.error(__('tiapp.xml is missing the <id> element'));
logger.error(__('The app id must consist of letters, numbers, and underscores.'));
logger.error(__('The app id must consist of letters, numbers, and underscores.'));
logger.error(__('Note: Android does not allow dashes and iOS does not allow underscores.'));
logger.error(__('Note: Android does not allow dashes and iOS does not allow underscores.'));
logger.error(__('The first character must be a letter or underscore.'));
logger.error(__('The first character must be a letter or underscore.'));
logger.error(__("Usually the app id is your company's reversed Internet domain name. (i.e. com.example.myapp)") + '\n');
logger.error(__("Usually the app id is your company's reversed Internet domain name. (i.e. com.example.myapp)") + '\n');
process.exit(1);
process.exit(1);
}
}


if (!tiapp.name) {
if (!tiapp.name) {
logger.error(__('tiapp.xml is missing the <name> element'));
logger.error(__('tiapp.xml is missing the <name> element'));
logger.error(__('The project name must consist of letters, numbers, dashes, and underscores.'));
logger.error(__('The project name must consist of letters, numbers, dashes, and underscores.'));
logger.error(__('The first character must be a letter.') + '\n');
logger.error(__('The first character must be a letter.') + '\n');
process.exit(1);
process.exit(1);
}
}


if (!tiapp.guid) {
if (!tiapp.guid) {
logger.error(__('tiapp.xml is missing the <guid> element'));
logger.error(__('tiapp.xml is missing the <guid> element'));
logger.error(__('The guid must be in the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX and consist of letters and numbers.') + '\n');
logger.error(__('The guid must be in the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX and consist of letters and numbers.') + '\n');
logger.log(__('If you need a new guid, below are 5 freshly generated new ones that you can choose from:'));
logger.log(__('If you need a new guid, below are 5 freshly generated new ones that you can choose from:'));
for (var i = 0, uuid = require('node-uuid'); i < 5; i++) {
for (var i = 0, uuid = require('node-uuid'); i < 5; i++) {
logger.log(' ' + uuid.v4().cyan);
logger.log(' ' + uuid.v4().cyan);
}
}
logger.log();
logger.log();
process.exit(1);
process.exit(1);
}
}


if (!/^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$/.test(tiapp.guid)) {
if (!/^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$/.test(tiapp.guid)) {
logger.error(__('tiapp.xml contains an invalid guid "%s"', tiapp.guid));
logger.error(__('tiapp.xml contains an invalid guid "%s"', tiapp.guid));
logger.error(__('The guid must be in the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX and consist of letters and numbers.') + '\n');
logger.error(__('The guid must be in the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX and consist of letters and numbers.') + '\n');
logger.log(__('If you need a new guid, below are 5 freshly generated new ones that you can choose from:'));
logger.log(__('If you need a new guid, below are 5 freshly generated new ones that you can choose from:'));
for (var i = 0, uuid = require('node-uuid'); i < 5; i++) {
for (var i = 0, uuid = require('node-uuid'); i < 5; i++) {
logger.log(' ' + uuid.v4().cyan);
logger.log(' ' + uuid.v4().cyan);
}
}
logger.log();
logger.log();
process.exit(1);
process.exit(1);
}
}


tiapp.version || (tiapp.version = '1.0');
tiapp.version || (tiapp.version = '1.0');


if (!config.get('app.skipVersionValidation') && !tiapp.properties['ti.skipVersionValidation']) {
if (!config.get('app.skipVersionValidation') && !tiapp.properties['ti.skipVersionValidation']) {
if (!/^\d+(\.\d+(\.\d+(\..+)?)?)?$/.test(tiapp.version)) {
if (!/^\d+(\.\d+(\.\d+(\..+)?)?)?$/.test(tiapp.version)) {
logger.error(__('tiapp.xml contains an invalid version "%s"', tiapp.version));
logger.error(__('tiapp.xml contains an invalid version "%s"', tiapp.version));
logger.error(__('The version must consist of three positive integers in the format "X.Y.Z".') + '\n');
logger.error(__('The version must consist of three positive integers in the format "X.Y.Z".') + '\n');
process.exit(1);
process.exit(1);
}
}


if ((''+tiapp.version).charAt(0) == '0') {
if ((''+tiapp.version).charAt(0) == '0') {
logger.warn(__('tiapp.xml contains an invalid version "%s"', tiapp.version));
logger.warn(__('tiapp.xml contains an invalid version "%s"', tiapp.version));
logger.warn(__('The app version major number must be greater than zero.'));
logger.warn(__('The app version major number must be greater than zero.'));
}
}
}
}
};
};


exports.validAppId = function (id) {
exports.validAppId = function (id) {
var words = {
var words = {
'abstract': 1,
'abstract': 1,
'assert': 1,
'assert': 1,
'boolean': 1,
'boolean': 1,
'break': 1,
'break': 1,
'byte': 1,
'byte': 1,
'case': 1,
'case': 1,
'catch': 1,
'catch': 1,
'char': 1,
'char': 1,
'class': 1,
'class': 1,
'const': 1,
'const': 1,
'continue': 1,
'continue': 1,
'default': 1,
'default': 1,
'do': 1,
'do': 1,
'double': 1,
'double': 1,
'else': 1,
'else': 1,
'enum': 1,
'enum': 1,
'extends': 1,
'extends': 1,
'false': 1,
'false': 1,
'final': 1,
'final': 1,
'finally': 1,
'finally': 1,
'float': 1,
'float': 1,
'for': 1,
'for': 1,
'goto': 1,
'goto': 1,
'if': 1,
'if': 1,
'implements': 1,
'implements': 1,
'import': 1,
'import': 1,
'instanceof': 1,
'instanceof': 1,
'int': 1,
'int': 1,
'interface': 1,
'interface': 1,
'long': 1,
'long': 1,
'native': 1,
'native': 1,
'new': 1,
'new': 1,
'null': 1,
'null': 1,
'package': 1,
'package': 1,
'private': 1,
'private': 1,
'protected': 1,
'protected': 1,
'public': 1,
'public': 1,
'return': 1,
'return': 1,
'short': 1,
'short': 1,
'static': 1,
'static': 1,
'strictfp': 1,
'strictfp': 1,
'super': 1,
'super': 1,
'switch': 1,
'switch': 1,
'synchronized': 1,
'synchronized': 1,
'this': 1,
'this': 1,
'throw': 1,
'throw': 1,
'throws': 1,
'throws': 1,
'transient': 1,
'transient': 1,
'true': 1,
'true': 1,
'try': 1,
'try': 1,
'void': 1,
'void': 1,
'volatile': 1,
'volatile': 1,
'while': 1
'while': 1
},
},
parts = id.split('.'),
parts = id.split('.'),
i = 0,
i = 0,
l = parts.length;
l = parts.length;


for (; i < l; i++) {
for (; i < l; i++) {
if (words[parts[i]]) {
if (words[parts[i]]) {
return false;
return false;
}
}
}
}


return true;
return true;
};
};


exports.loadPlugins = function (logger, config, cli, projectDir, finished, silent, compact) {
exports.loadPlugins = function (logger, config, cli, projectDir, finished, silent, compact) {
var searchPaths = {
var searchPaths = {
project: [ path.join(projectDir, 'plugins') ],
project: [ path.join(projectDir, 'plugins') ],
config: [],
config: [],
global: []
global: []
},
},
confPaths = config.get('paths.plugins'),
confPaths = config.get('paths.plugins'),
defaultInstallLocation = cli.env.installPath,
defaultInstallLocation = cli.env.installPath,
sdkLocations = cli.env.os.sdkPaths.map(function (p) { return afs.resolvePath(p); });
sdkLocations = cli.env.os.sdkPaths.map(function (p) { return afs.resolvePath(p); });


// set our paths from the config file
// set our paths from the config file
Array.isArray(confPaths) || (confPaths = [ confPaths ]);
Array.isArray(confPaths) || (confPaths = [ confPaths ]);
confPaths.forEach(function (p) {
confPaths.forEach(function (p) {
p && fs.existsSync(p = afs.resolvePath(p)) && searchPaths.project.indexOf(p) == -1 && searchPaths.config.indexOf(p) == -1 && (searchPaths.config.push(p));
p && fs.existsSync(p = afs.resolvePath(p)) && searchPaths.project.indexOf(p) == -1 && searchPaths.config.indexOf(p) == -1 && (searchPaths.config.push(p));
});
});


// add any plugins from various sdk locations
// add any plugins from various sdk locations
sdkLocations.indexOf(defaultInstallLocation) == -1 && sdkLocations.push(defaultInstallLocation);
sdkLocations.indexOf(defaultInstallLocation) == -1 && sdkLocations.push(defaultInstallLocation);
cli.sdk && sdkLocations.push(afs.resolvePath(cli.sdk.path, '..', '..', '..'));
cli.sdk && sdkLocations.push(afs.resolvePath(cli.sdk.path, '..', '..', '..'));
sdkLocations.forEach(function (p) {
sdkLocations.forEach(function (p) {
fs.existsSync(p = afs.resolvePath(p, 'plugins')) && searchPaths.project.indexOf(p) == -1 && searchPaths.config.indexOf(p) == -1 && searchPaths.global.indexOf(p) == -1 && (searchPaths.global.push(p));
fs.existsSync(p = afs.resolvePath(p, 'plugins')) && searchPaths.project.indexOf(p) == -1 && searchPaths.config.indexOf(p) == -1 && searchPaths.global.indexOf(p) == -1 && (searchPaths.global.push(p));
});
});


// find all hooks for active plugins
// find all hooks for active plugins
appc.tiplugin.find(cli.tiapp.plugins, searchPaths, config, logger, function (plugins) {
appc.tiplugin.find(cli.tiapp.plugins, searchPaths, config, logger, function (plugins) {
if (plugins.missing.length) {
if (plugins.missing.length) {
if (logger) {
if (logger) {
logger.error(__('Could not find all required Titanium plugins:'))
logger.error(__('Could not find all required Titanium plugins:'))
plugins.missing.forEach(function (m) {
plugins.missing.forEach(function (m) {
logger.error(' id: ' + m.id + '\t version: ' + m.version);
logger.error(' id: ' + m.id + '\t version: ' + m.version);
});
});
logger.log();
logger.log();
}
}
process.exit(1);
process.exit(1);
}
}


if (plugins.found.length) {
if (plugins.found.length) {
plugins.found.forEach(function (plugin) {
plugins.found.forEach(function (plugin) {
cli.scanHooks(afs.resolvePath(plugin.pluginPath, 'hooks'));
cli.scanHooks(afs.resolvePath(plugin.pluginPath, 'hooks'));
});
});
} else {
} else {
logger && logger.debug(__('No project level plugins to load'));
logger && logger.debug(__('No project level plugins to load'));
}
}


silent || cli.emit('cli:check-plugins', { compact: compact === undefined ? true : compact });
silent || cli.emit('cli:check-plugins', { compact: compact === undefined ? true : compact });


finished();
finished();
});
});
};
};


exports.loadModuleManifest = function (logger, manifestFile) {
exports.loadModuleManifest = function (logger, manifestFile) {
var re = /^(\S+)\s*:\s*(.*)$/,
var re = /^(\S+)\s*:\s*(.*)$/,
match,
match,
manifest = {};
manifest = {};


if (!fs.existsSync(manifestFile)) {
if (!fs.existsSync(manifestFile)) {
logger.error(__('Missing %s', manifestFile));
logger.error(__('Missing %s', manifestFile));
logger.log();
logger.log();
process.exit(1);
process.exit(1);
}
}


fs.readFileSync(manifestFile).toString().split('\n').forEach(function (line) {
fs.readFileSync(manifestFile).toString().split('\n').forEach(function (line) {
match = line.match(re);
match = line.match(re);
if (match) {
if (match) {
manifest[match[1].trim()] = match[2].trim();
manifest[match[1].trim()] = match[2].trim();
}
}
});
});


return manifest;
return manifest;
};
};


exports.validateModuleManifest = function (logger, cli, manifest) {
exports.validateModuleManifest = function (logger, cli, manifest) {
var requiredModuleKeys = [
var requiredModuleKeys = [
'name',
'name',
'version',
'version',
'moduleid',
'moduleid',
'description',
'description',
'copyright',
'copyright',
'license',
'license',
'copyright',
'copyright',
'platform',
'platform',
'minsdk',
'minsdk',
'architectures'
'architectures'
];
];


// check if all the required module keys are in the list
// check if all the required module keys are in the list
requiredModuleKeys.forEach(function (key) {
requiredModuleKeys.forEach(function (key) {
if (!manifest[key]) {
if (!manifest[key]) {
logger.error(__('Missing required manifest key "%s"', key));
logger.error(__('Missing required manifest key "%s"', key));
logger.log();
logger.log();
process.exit(1);
process.exit(1);
}
}
});
});


if (cli.argv.platform !== exports.resolvePlatform(manifest.platform)) {
if (cli.argv.platform !== exports.resolvePlatform(manifest.platform)) {
logger.error(__('Unable to find "%s" module', cli.argv.platform));
logger.error(__('Unable to find "%s" module', cli.argv.platform));
logger.log();
logger.log();
process.exit(1);
process.exit(1);
}
}
};
};


exports.validateCorrectSDK = function (logger, config, cli, commandName) {
exports.validateCorrectSDK = function (logger, config, cli, commandName) {
// tiapp.xml should exist by the time we get here
// tiapp.xml should exist by the time we get here
var argv = cli.argv,
var argv = cli.argv,
tiapp = cli.tiapp,
tiapp = cli.tiapp,
sdkName = tiapp['sdk-version'],
sdkName = tiapp['sdk-version'],
selectedSdk = cli.sdk && cli.sdk.name || manifest.version;
selectedSdk = cli.sdk && cli.sdk.name || manifest.version;


if (!sdkName) {
if (!sdkName) {
sdkName = tiapp['sdk-version'] = cli.sdk && cli.sdk.name || Object.keys(cli.env.sdks).sort().pop();
sdkName = tiapp['sdk-version'] = cli.sdk && cli.sdk.name || Object.keys(cli.env.sdks).sort().pop();
}
}


if (argv.legacy !== true && (!sdkName || sdkName === selectedSdk)) {
if (argv.legacy !== true && (!sdkName || sdkName === selectedSdk)) {
return true;
return true;
}
}


// check the project's preferred sdk is even installed
// check the project's preferred sdk is even installed
if (sdkName == '__global__' || !cli.env.sdks[sdkName]) {
if (sdkName == '__global__' || !cli.env.sdks[sdkName]) {
logger.banner();
logger.banner();
logger.error(__("Unable to compile project because the 'sdk-version' in the tiapp.xml is not installed") + '\n');
logger.error(__("Unable to compile project because the 'sdk-version' in the tiapp.xml is not installed") + '\n');
logger.log(__("The project's %s is currently set to %s, which is not installed.", 'sdk-version'.cyan, sdkName.cyan) + '\n');
logger.log(__("The project's %s is currently set to %s, which is not installed.", 'sdk-version'.cyan, sdkName.cyan) + '\n');
logger.log(__("Update the %s in the tiapp.xml to one of the installed Titaniums SDKs:", 'sdk-version'.cyan));
logger.log(__("Update the %s in the tiapp.xml to one of the installed Titaniums SDKs:", 'sdk-version'.cyan));
Object.keys(cli.env.sdks).sort().forEach(function (ver) {
Object.keys(cli.env.sdks).sort().forEach(function (ver) {
if (ver != '__global__') {
if (ver != '__global__') {
logger.log(' ' + ver.cyan);
logger.log(' ' + ver.cyan);
}
}
});
});
logger.log(__("or run '%s' to download and install Titanium SDK %s", ('titanium sdk install ' + sdkName).cyan, sdkName) + '\n');
logger.log(__("or run '%s' to download and install Titanium SDK %s", ('titanium sdk install ' + sdkName).cyan, sdkName) + '\n');
process.exit(1);
process.exit(1);
}
}


var sdkVersion = cli.env.sdks[sdkName].manifest && cli.env.sdks[sdkName].manifest.version || sdkName;
var sdkVersion = cli.env.sdks[sdkName].manifest && cli.env.sdks[sdkName].manifest.version || sdkName;
if (version.gte(sdkVersion, '3.0.0') && version.lte(sdkVersion, '3.0.2') && version.gte(process.version, '0.9.0')) {
if (version.gte(sdkVersion, '3.0.0') && version.lte(sdkVersion, '3.0.2') && version.gte(process.version, '0.9.0')) {
logger.banner();
logger.banner();
logger.error(__('Unable to compile project using Titanium SDK %s with Node.js %s', sdkName, process.version) + '\n');
logger.error(__('Unable to compile project using Titanium SDK %s with Node.js %s', sdkName, process.version) + '\n');
logger.log(__('Titanium SDK %s requires Node.js v0.8. Node.js v0.10 and newer will not work.', sdkName.cyan) + '\n');
logger.log(__('Titanium SDK %s requires Node.js v0.8. Node.js v0.10 and newer will not work.', sdkName.cyan) + '\n');
logger.log(__('Either update your application to Titanium SDK %s or newer or download Node.js %s from %s.', '3.1.0.GA'.cyan, 'v0.8'.cyan, 'http://nodejs.org/dist/'.cyan) + '\n');
logger.log(__('Either update your application to Titanium SDK %s or newer or download Node.js %s from %s.', '3.1.0.GA'.cyan, 'v0.8'.cyan, 'http://nodejs.org/dist/'.cyan) + '\n');
process.exit(1);
process.exit(1);
}
}


// fork or die
// fork or die
if (config.cli.failOnWrongSDK) {
if (config.cli.failOnWrongSDK) {
logger.banner();
logger.banner();
logger.error(__('Unable to compile a %s project with Titanium SDK %s', sdkName, selectedSdk));
logger.error(__('Unable to compile a %s project with Titanium SDK %s', sdkName, selectedSdk));
logger.error(__('To build this application, set the <sdk-version> in the tiapp.xml to the current Titaniums SDK: %s', selectedSdk) + '\n');
logger.error(__('To build this application, set the <sdk-version> in the tiapp.xml to the current Titaniums SDK: %s', selectedSdk) + '\n');
process.exit(1);
process.exit(1);
}
}


var args = argv.$_,
var args = argv.$_,
p = args.indexOf('--sdk'),
p = args.indexOf('--sdk'),
platform = exports.resolvePlatform(argv.platform),
platform = exports.resolvePlatform(argv.platform),
cmd = [],
cmd = [],
cmdSafe = [],
cmdSafe = [],
cmdRoot,
cmdRoot,
hideBanner = false,
hideBanner = false,
delayCmd = false;
delayCmd = false;


function cmdAdd() {
function cmdAdd() {
for (var i = 0; i < arguments.length; i++) {
for (var i = 0; i < arguments.length; i++) {
cmd.push(arguments[i]);
cmd.push(arguments[i]);
cmdSafe.push(arguments[i]);
cmdSafe.push(arguments[i]);
}
}
}
}


function cmdAddSecret(param) {
function cmdAddSecret(param) {
for (var i = 0; i < arguments.length; i++) {
for (var i = 0; i < arguments.length; i++) {
cmd.push(arguments[i]);
cmd.push(arguments[i]);
cmdSafe.push('*******');
cmdSafe.push('*******');
}
}
}
}


if (p != -1) {
if (p != -1) {
args.splice(p, 2);
args.splice(p, 2);
}
}


if (!argv.legacy) {
if (!argv.legacy) {
logger.info(__('tiapp.xml <sdk-version> set to %s, but current Titanium SDK set to %s', sdkName.cyan, selectedSdk.cyan));
logger.info(__('tiapp.xml <sdk-version> set to %s, but current Titanium SDK set to %s', sdkName.cyan, selectedSdk.cyan));
}
}


if (argv.legacy || version.lt(sdkVersion, '2.2.0')) { // technically, there is no 2.2, it was released as 3.0
if (argv.legacy || version.lt(sdkVersion, '2.2.0')) { // technically, there is no 2.2, it was released as 3.0
// in 3.2, we renamed --password to --store-password as to not conflict with the
// in 3.2, we renamed --password to --store-password as to not conflict with the
// authentication --password option
// authentication --password option
if (argv.platform == 'android' && argv['store-password']) {
if (argv.platform == 'android' && argv['store-password']) {
argv.password = argv['store-password'];
argv.password = argv['store-password'];
}
}


cmdRoot = 'python';
cmdRoot = 'python';


var builderPy = path.join(path.resolve(cli.env.sdks[sdkName].path), platform, 'builder.py');
var builderPy = path.join(path.resolve(cli.env.sdks[sdkName].path), platform, 'builder.py');
cmdAdd(builderPy);
cmdAdd(builderPy);


switch (platform) {
switch (platform) {
case 'iphone':
case 'iphone':
switch (argv.target) {
switch (argv.target) {
case 'simulator':
case 'simulator':
if (argv['build-only']) {
if (argv['build-only']) {
cmdAdd('build', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['device-family'], argv['sim-type'], argv['debug-host']);
cmdAdd('build', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['device-family'], argv['sim-type'], argv['debug-host']);
} else {
} else {
cmdAdd('run', argv['project-dir'], argv['ios-version'], '', '', argv['device-family'], argv['sim-type'], argv['debug-host']);
cmdAdd('run', argv['project-dir'], argv['ios-version'], '', '', argv['device-family'], argv['sim-type'], argv['debug-host']);
}
}
break;
break;


case 'device':
case 'device':
cmdAdd('install', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['pp-uuid'], argv['developer-name'], argv['device-family'], argv.keychain, argv['debug-host']);
cmdAdd('install', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['pp-uuid'], argv['developer-name'], argv['device-family'], argv.keychain, argv['debug-host']);
break;
break;


case 'dist-appstore':
case 'dist-appstore':
cmdAdd('distribute', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['pp-uuid'], argv['distribution-name'], '.', argv['device-family'], argv.keychain);
cmdAdd('distribute', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['pp-uuid'], argv['distribution-name'], '.', argv['device-family'], argv.keychain);
break;
break;


case 'dist-adhoc':
case 'dist-adhoc':
cmdAdd('adhoc', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['pp-uuid'], argv['distribution-name'], argv['device-family'], argv.keychain, argv['debug-host']);
cmdAdd('adhoc', argv['ios-version'], argv['project-dir'], tiapp.id, tiapp.name, argv['pp-uuid'], argv['distribution-name'], argv['device-family'], argv.keychain, argv['debug-host']);
break;
break;
}
}
break;
break;


case 'mobileweb':
case 'mobileweb':
cmdAdd(argv['project-dir'], argv['deploy-type']);
cmdAdd(argv['project-dir'], argv['deploy-type']);
break;
break;


case 'android':
case 'android':
if (argv['build-only']) {
if (argv['build-only']) {
cmdAdd('build', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id);
cmdAdd('build', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id);
} else {
} else {
if (argv.target == 'emulator') {
if (argv.target == 'emulator') {
if (!argv['avd-id']) {
if (!argv['avd-id']) {
logger.error(__('Missing required option "%s"', '--avd-id') + '\n');
logger.error(__('Missing required option "%s"', '--avd-id') + '\n');
process.exit(1);
process.exit(1);
}
}
if (!argv['avd-skin']) {
if (!argv['avd-skin']) {
logger.error(__('Missing required option "%s"', '--avd-skin') + '\n');
logger.error(__('Missing required option "%s"', '--avd-skin') + '\n');
process.exit(1);
process.exit(1);
}
}
}
}


switch (argv.target) {
switch (argv.target) {
case 'emulator':
case 'emulator':
cmdAdd('simulator', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, argv['avd-id'], argv['avd-skin']);
cmdAdd('simulator', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, argv['avd-id'], argv['avd-skin']);
delayCmd = true;
delayCmd = true;


// launch the emulator
// launch the emulator
var emuArgs = [ builderPy, 'emulator', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, argv['avd-id'], argv['avd-skin'] ];
var emuArgs = [ builderPy, 'emulator', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, argv['avd-id'], argv['avd-skin'] ];
argv['avd-abi'] && emuArgs.push(argv['avd-abi']);
argv['avd-abi'] && emuArgs.push(argv['avd-abi']);
logger.info(__('Launching Android emulator: %s', ('"' + cmdRoot + '" "' + emuArgs.join('" "') + '"').cyan));
logger.info(__('Launching Android emulator: %s', ('"' + cmdRoot + '" "' + emuArgs.join('" "') + '"').cyan));
spawn(cmdRoot, emuArgs, {
spawn(cmdRoot, emuArgs, {
detached: true,
detached: true,
stdio: 'ignore'
stdio: 'ignore'
}).on('exit', function (code, signal) {
}).on('exit', function (code, signal) {
console.log('EMULATOR EXITED', code, signal);
console.log('EMULATOR EXITED', code, signal);
});
});
break;
break;


case 'device':
case 'device':
cmdAdd('install', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, 1);
cmdAdd('install', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, 1);
break;
break;


case 'dist-playstore':
case 'dist-playstore':
cmdAdd('distribute', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, argv['keystore']);
cmdAdd('distribute', tiapp.name, argv['android-sdk'], argv['project-dir'] , tiapp.id, argv['keystore']);
cmdAddSecret(argv['password']);
cmdAddSecret(argv['password']);
cmdAdd(argv['alias'], argv['output-dir']);
cmdAdd(argv['alias'], argv['output-dir']);
break;
break;
}
}
}
}


// Add debug host if it's defined
// Add debug host if it's defined
if (argv['debug-host']) {
if (argv['debug-host']) {
if (argv.target == 'device') {
if (argv.target == 'device') {
cmdAdd('');
cmdAdd('');
}
}
cmdAdd(argv['debug-host']);
cmdAdd(argv['debug-host']);
}
}
// Add profiler host if it's defined
// Add profiler host if it's defined
if (argv['profiler-host']) {
if (argv['profiler-host']) {
if (argv.target == 'device') {
if (argv.target == 'device') {
cmdAdd('');
cmdAdd('');
}
}
cmdAdd(argv['profiler-host']);
cmdAdd(argv['profiler-host']);
cmdAdd('profiler');
cmdAdd('profiler');
}
}
}
}


} else {
} else {


// 3.0.0's iOS build does not like it if node has a full path, so we hope they have node in the path
// 3.0.0's iOS build does not like it if node has a full path, so we hope they have node in the path
cmdRoot = version.gte(sdkVersion, '3.0.2') ? (process.execPath || 'node') : 'node';
cmdRoot = version.gte(sdkVersion, '3.0.2') ? (process.execPath || 'node') : 'node';


hideBanner = true;
hideBanner = true;


// If the titanium path has spaces, then we are trying to combine the paths and verify after they were split.
// If the titanium path has spaces, then we are trying to combine the paths and verify after they were split.
var titaniumPath = function getTitaniumPath (params) {
var titaniumPath = function getTitaniumPath (params) {
var paramsArray = params.split(' '),
var paramsArray = params.split(' '),
pathSegment,
pathSegment,
prevPath = '';
prevPath = '';
while ((pathSegment = paramsArray.pop())) {
while ((pathSegment = paramsArray.pop())) {
if (fs.existsSync(pathSegment + prevPath)) {
if (fs.existsSync(pathSegment + prevPath)) {
return pathSegment + prevPath;
return pathSegment + prevPath;
}
}
prevPath = ' ' + pathSegment;
prevPath = ' ' + pathSegment;
}
}
// fallback to default last segment, if we fail for any reason.
// fallback to default last segment, if we fail for any reason.
return params.split(' ').pop();
return params.split(' ').pop();
}(argv.$0);
}(argv.$0);


cmdAdd(titaniumPath);
cmdAdd(titaniumPath);
cmdAdd(commandName, '--sdk', sdkName);
cmdAdd(commandName, '--sdk', sdkName);


var flags = {},
var flags = {},
options = {};
options = {};


// mix the command and platform specific options together
// mix the command and platform specific options together
[cli.globalContext, cli.command, cli.command.platform].forEach(function (ctx) {
[cli.globalContext, cli.command, cli.command.platform].forEach(function (ctx) {
if (ctx && ctx.conf) {
if (ctx && ctx.conf) {
ctx.conf.flags && appc.util.mix(flags, ctx.conf.flags);
ctx.conf.flags && appc.util.mix(flags, ctx.conf.flags);
ctx.conf.options && appc.util.mix(options, ctx.conf.options);
ctx.conf.options && appc.util.mix(options, ctx.conf.options);
}
}
});
});


Object.keys(flags).forEach(function (name) {
Object.keys(flags).forEach(function (name) {
var def = flags[name].hasOwnProperty('default') ? flags[name].default : false;
var def = flags[name].hasOwnProperty('default') ? flags[name].default : false;
if (argv[name] !== undefined && def !== argv[name]) {
if (argv[name] !== undefined && def !== argv[name]) {
cmdAdd('--' + (!!argv[name] ? '' : 'no-') + name);
cmdAdd('--' + (!!argv[name] ? '' : 'no-') + name);
}
}
});
});


Object.keys(options).forEach(function (name) {
Object.keys(options).forEach(function (name) {
if (name != 'sdk' && argv[name] !== undefined) {
if (name != 'sdk' && argv[name] !== undefined) {
// in 3.2, we renamed --password to --store-password as to not conflict with the
// in 3.2, we renamed --password to --store-password as to not conflict with the
// authentication --password option
// authentication --password option
var arg = name;
var arg = name;
if (argv.platform == 'android' && arg == 'store-password' && version.lt(sdkVersion, '3.2.0')) {
if (argv.platform == 'android' && arg == 'store-password' && version.lt(sdkVersion, '3.2.0')) {
arg = 'password';
arg = 'password';
}
}


cmdAdd('--' + arg);
cmdAdd('--' + arg);
if (options[name].secret) {
if (options[name].secret) {
cmdAddSecret(argv[name]);
cmdAddSecret(argv[name]);
} else {
} else {
cmdAdd(argv[name]);
cmdAdd(argv[name]);
}
}
}
}
});
});
}
}


// trim off the empty trailing args
// trim off the empty trailing args
while (!cmd[cmd.length-1]) {
while (!cmd[cmd.length-1]) {
cmd.pop();
cmd.pop();
cmdSafe.pop();
cmdSafe.pop();
}
}


if (argv.legacy) {
if (argv.legacy) {
logger.info(__('Forking legacy SDK command: %s', (cmdRoot + ' "' + cmdSafe.join('" "') + '"').cyan) + '\n');
logger.info(__('Forking legacy SDK command: %s', (cmdRoot + ' "' + cmdSafe.join('" "') + '"').cyan) + '\n');
} else {
} else {
logger.info(__('Forking correct SDK command: %s', ('"' + cmdRoot + '" "' + cmdSafe.join('" "') + '"').cyan) + '\n');
logger.info(__('Forking correct SDK command: %s', ('"' + cmdRoot + '" "' + cmdSafe.join('" "') + '"').cyan) + '\n');
}
}


hideBanner && cmd.push('--no-banner');
hideBanner && cmd.push('--no-banner');


// when doing a legacy Android build (1.X or 2.X), then we delay the build to
// when doing a legacy Android build (1.X or 2.X), then we delay the build to
// allow the emulator to start because there is a bug where the builder.py
// allow the emulator to start because there is a bug where the builder.py
// doesn't like to be run concurrently
// doesn't like to be run concurrently
setTimeout(function () {
setTimeout(function () {
spawn(cmdRoot, cmd, {
spawn(cmdRoot, cmd, {
stdio: 'inherit'
stdio: 'inherit'
}).on('exit', function (code, signal) {
}).on('exit', function (code, signal) {
code && process.exit(code);
code && process.exit(code);
});
});
}, delayCmd ? 1000 : 0);
}, delayCmd ? 1000 : 0);
};
};


exports.validateAppJsExists = function (projectDir, logger, platformDirs) {
exports.validateAppJsExists = function (projectDir, logger, platformDirs) {
if (!fs.existsSync(path.join(projectDir, 'Resources'))) {
if (!fs.existsSync(path.join(projectDir, 'Resources'))) {
logger.error(__('"Resources" directory not found'));
logger.error(__('"Resources" directory not found'));
logger.error(__("Ensure the \"Resources\" directory exists and contains an \"app.js\" file.") + '\n');
logger.error(__("Ensure the \"Resources\" directory exists and contains an \"app.js\" file.") + '\n');
process.exit(1);
process.exit(1);
}
}


var files = [
var files = [
path.join(projectDir, 'Resources', 'app.js')
path.join(projectDir, 'Resources', 'app.js')
];
];


Array.isArray(platformDirs) || (platformDirs = [ platformDirs ]);
Array.isArray(platformDirs) || (platformDirs = [ platformDirs ]);
platformDirs.forEach(function (platformDir) {
platformDirs.forEach(function (platformDir) {
files.push(path.join(projectDir, 'Resources', platformDir, 'app.js'));
files.push(path.join(projectDir, 'Resources', platformDir, 'app.js'));
});
});


if (!files.some(function (file) { return fs.existsSync(file); })) {
if (!files.some(function (file) { return fs.existsSync(file); })) {
logger.error(__('"app.js" not found'));
logger.error(__('"app.js" not found'));
logger.error(__("Ensure the \"app.js\" file exists in your project's \"Resources\" directory.") + '\n');
logger.error(__("Ensure the \"app.js\" file exists in your project's \"Resources\" directory.") + '\n');
process.exit(1);
process.exit(1);
}
}
};
};


exports.validatePlatformOptions = function (logger, config, cli, commandName) {
exports.validatePlatformOptions = function (logger, config, cli, commandName) {
var platform = exports.resolvePlatform(cli.argv.platform),
var platform = exports.resolvePlatform(cli.argv.platform),
platformCommand = path.join(path.dirname(module.filename), '..', '..', '..', manifest.platforms[manifest.platforms.indexOf(platform)], 'cli', 'commands', '_' + commandName + '.js');
platformCommand = path.join(path.dirname(module.filename), '..', '..', '..', manifest.platforms[manifest.platforms.indexOf(platform)], 'cli', 'commands', '_' + commandName + '.js');
if (fs.existsSync(platformCommand)) {
if (fs.existsSync(platformCommand)) {
var command = require(platformCommand);
var command = require(platformCommand);
return command && typeof command.validate == 'function' ? command.validate(logger, config, cli) : null;
return command && typeof command.validate == 'function' ? command.validate(logger, config, cli) : null;
}
}
};
};


exports.scrubPlatforms = function (platforms) {
exports.scrubPlatforms = function (platforms) {
var scrubbed = {}, // distinct list of un-aliased platforms
var scrubbed = {}, // distinct list of un-aliased platforms
original = {},
original = {},
bad = {};
bad = {};


platforms.toLowerCase().split(',').forEach(function (platform) {
platforms.toLowerCase().split(',').forEach(function (platform) {
var name = platformAliases[platform] || platform;
var name = platformAliases[platform] || platform;
// if name is falsey, then it's invalid anyways
// if name is falsey, then it's invalid anyways
if (name) {
if (name) {
if (manifest.platforms.indexOf(name) == -1) {
if (manifest.platforms.indexOf(name) == -1) {
bad[platform] = 1;
bad[platform] = 1;
} else {
} else {
scrubbed[name] = 1;
scrubbed[name] = 1;
original[platform] = 1;
original[platform] = 1;
}
}
}
}
});
});


return {
return {
scrubbed: Object.keys(scrubbed).sort(), // distinct list of un-aliased platforms
scrubbed: Object.keys(scrubbed).sort(), // distinct list of un-aliased platforms
original: Object.keys(original).sort(),
original: Object.keys(original).sort(),
bad: Object.keys(bad).sort()
bad: Object.keys(bad).sort()
};
};
};
};


exports.resolvePlatform = function (platform) {
exports.resolvePlatform = function (platform) {
return platformAliases[platform] || platform;
return platformAliases[platform] || platform;
};
};


exports.filterPlatforms = function (platform) {
exports.filterPlatforms = function (platform) {
platform = platformAliases[platform] || platform;
platform = platformAliases[platform] || platform;
return exports.availablePlatformsNames.filter(function (name) {
return exports.availablePlatformsNames.filter(function (name) {
return name != platform;
return name != platform;
});
});
};
};


exports.validatePlatform = function (logger, cli, name) {
exports.validatePlatform = function (logger, cli, name) {
var platform = name ? cli.argv[name] : cli.argv,
var platform = name ? cli.argv[name] : cli.argv,
p = cli.argv[name] = platformAliases[platform] || platform;
p = cli.argv[name] = platformAliases[platform] || platform;
if (!p || manifest.platforms.indexOf(p) == -1) {
if (!p || manifest.platforms.indexOf(p) == -1) {
logger.banner();
logger.banner();
logger.error(__('Invalid platform "%s"', platform) + '\n');
logger.error(__('Invalid platform "%s"', platform) + '\n');
appc.string.suggest(platform, exports.targetPlatforms, logger.log);
appc.string.suggest(platform, exports.targetPlatforms, logger.log);
logger.log(__('Available platforms for SDK version %s:', cli.sdk && cli.sdk.name || manifest.version));
logger.log(__('Available platforms for SDK version %s:', cli.sdk && cli.sdk.name || manifest.version));
exports.targetPlatforms.forEach(function (p) {
exports.targetPlatforms.forEach(function (p) {
logger.log(' ' + p.cyan);
logger.log(' ' + p.cyan);
});
});
logger.log();
logger.log();
process.exit(1);
process.exit(1);
}
}
};
};