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);
}
}
};
};