@extjs/sencha-cmd-linux-32
Version:
Productivity and performance optimization tool for building applications with Sencha Ext JS and Sencha Touch.
1,087 lines (921 loc) • 34.9 kB
JavaScript
importPackage(java.lang);
importPackage(java.io);
importPackage(java.net);
importPackage(javax.script);
importPackage(com.sencha.util);
importPackage(com.sencha.logging);
importPackage(com.sencha.util.filters);
importPackage(com.sencha.exceptions);
importPackage(com.sencha.command);
importPackage(com.sencha.tools.generator);
(function(Scope){
var CR = StringUtil.NewLine,
applyTo = Scope.applyTo = function(dest, src) {
if (dest && src) {
for (var name in src) {
dest[name] = src[name];
}
}
return dest;
},
xpathCache = {},
_logger = SenchaLogManager.getLogger("ant-util");
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (callback, scope) {
for (var array = this, i = 0, n = array.length; i < n; ++i) {
callback.call(scope, array[i], i, array);
}
};
}
applyTo(Scope, {
'CR' : CR,
/**
* Returns true if the passed value is a JavaScript Array, false otherwise.
*
* @param {Object} target The target to test
* @return {Boolean}
* @method
*/
isArray: (('isArray' in Array) ? Array.isArray : function(value) {
return toString.call(value) === '[object Array]';
}),
/**
* Returns true if the passed value is a JavaScript Date object, false otherwise.
* @param {Object} object The object to test
* @return {Boolean}
*/
isDate: function(value) {
return toString.call(value) === '[object Date]';
},
/**
* Returns true if the passed value is a JavaScript Object, false otherwise.
* @param {Object} value The value to test
* @return {Boolean}
* @method
*/
isObject: function(value) {
return toString.call(value) === '[object Object]';
},
/**
* @private
*/
isSimpleObject: function(value) {
return value instanceof Object && value.constructor === Object;
},
/**
* Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
* @param {Object} value The value to test
* @return {Boolean}
*/
isPrimitive: function(value) {
var type = typeof value;
return type === 'string' || type === 'number' || type === 'boolean';
},
/**
* Returns true if the passed value is a JavaScript Function, false otherwise.
* @param {Object} value The value to test
* @return {Boolean}
* @method
*/
isFunction: function(value) {
return !!(value && value.$extIsFunction);
},
/**
* Returns true if the passed value is a number. Returns false for non-finite numbers.
* @param {Object} value The value to test
* @return {Boolean}
*/
isNumber: function(value) {
return typeof value === 'number' && isFinite(value);
},
/**
* Validates that a value is numeric.
* @param {Object} value Examples: 1, '1', '2.34'
* @return {Boolean} True if numeric, false otherwise
*/
isNumeric: function(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
},
/**
* Returns true if the passed value is a string.
* @param {Object} value The value to test
* @return {Boolean}
*/
isString: function(value) {
return typeof value === 'string';
},
/**
* Returns true if the passed value is a boolean.
*
* @param {Object} value The value to test
* @return {Boolean}
*/
isBoolean: function(value) {
return typeof value === 'boolean';
},
/**
* Returns true if the passed value is an HTMLElement
* @param {Object} value The value to test
* @return {Boolean}
*/
isElement: function(value) {
return value ? value.nodeType === 1 : false;
},
/**
* Returns true if the passed value is a TextNode
* @param {Object} value The value to test
* @return {Boolean}
*/
isTextNode: function(value) {
return value ? value.nodeName === "#text" : false;
},
/**
* Returns true if the passed value is defined.
* @param {Object} value The value to test
* @return {Boolean}
*/
isDefined: function(value) {
return typeof value !== 'undefined';
},
/**
* Returns true if the passed value is iterable, false otherwise
* @param {Object} value The value to test
* @return {Boolean}
*/
isIterable: function(value) {
var type = typeof value,
checkLength = false;
if (value && type != 'string') {
// Functions have a length property, so we need to filter them out
if (type == 'function') {
// In Safari, NodeList/HTMLCollection both return "function" when using typeof, so we need
// to explicitly check them here.
if (Ext.isSafari) {
checkLength = value instanceof NodeList || value instanceof HTMLCollection;
}
} else {
checkLength = true;
}
}
return checkLength ? value.length !== undefined : false;
},
isJavaCollection: function (obj) {
try {
return obj.iterator();
} catch (e) {
// not a Map
}
return false;
},
isJavaMap: function (obj) {
try {
return obj.entrySet();
} catch (e) {
// not a Map
}
return false;
},
isJavaArray: function (obj) {
try {
return obj.getClass().isArray();
} catch (e) {
}
return false;
},
isJavaString: function (obj) {
try {
obj.equalsIgnoreCase(obj.length() + '');
return true;
} catch (e) {
return false; // not a String
}
},
toJS: function (obj) {
var entrySet = isJavaMap(obj);
var ret = obj;
var it;
if (entrySet) {
ret = {};
for (it = entrySet.iterator(); it.hasNext(); ) {
var entry = it.next();
var key = entry.getKey();
var val = entry.getValue();
ret[toJS(key)] = toJS(val);
}
} else if(isJavaArray(obj)) {
ret = [];
for(var i = 0; i < obj.length; i++) {
ret.push(toJS(obj[i]));
}
} else {
it = isJavaCollection(obj);
if (it) {
ret = [];
while (it.hasNext()) {
ret.push(toJS(it.next()));
}
} else if (isJavaString(obj)) {
ret = obj + '';
}
}
return ret;
},
deleteFile: function(fileName) {
try {
FileUtil['delete'](new File(fileName));
} catch (e) {
_logger.warn('Warning: Cannot delete "' + fileName + '"');
}
},
/**
* Writes the specified message to the log.
* @param {String} message The message to log
*/
echo: function(message) {
_logger.info(message);
},
/**
* Writes the specified message to the log and prefix it with the local time.
* @param {String} message The message to log
*/
echoWithTime: function(message) {
Scope.echo(new Date().toLocaleTimeString() + ' - ' + message);
},
escapeXml: (function () {
var map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
function replacer (m) {
return map[m];
}
return function (string) {
return String(string).replace(/[&<>'"]/g, replacer);
};
}()),
generateGuid: function () {
return '' + java.util.UUID.randomUUID().toString();
},
/**
* Advanced attributes.get wrapper that check that the value is not a property
* cast the result to a string and eventually fail the build if the attribute is
* not present.
*/
getValueFromAttribute: function(name, fail) {
var value = attributes.get(name) + '';
if (!value || value == '' || value.match(/\$\{[^\}]*\}/)) {
if (fail) {
self.fail("Unable to find attribute " + name);
}
return null;
} else {
return value;
}
},
/**
* Executes a command given an object describing the application and arguments.
*
* exec({
* app: 'git',
* args: [ 'log', '-1', '--format=%H' ]
* });
*
* // the above is equivalent to the following:
*
* exec('git', ['log', '-1', '--format:%H']);
*
* The important feature provided by this method (beyond convenience) is that the app name
* (e.g., "git") is used to lookup an optional property (e.g., "x-git.exe"). That property
* can be defined to deal with local path issues. When it is undefined, the raw app name is
* used for the underlying exec task and must be found in the system path.
*
* @param {Object/String} cmd An object describing the command.
* @param {String} cmd.app The name of the application (e.g., "jsduck" or "git").
* @param {String[]} cmd.args The arguments to pass to the application.
* @param {Boolean} cmd.failOnError False to continue if the command fails (default is true).
* @param {String[]} [args] The arguments to pass to the application.
* @param {Object} [opt] Extra options (e.g., "failOnError").
* @return {Object} The result of the command
* @return {Number} return.exitCode The exit code of the command
* @return {String} return.output The stdout generated from the command
*/
exec: function(cmd, args, opt) {
if (typeof cmd == 'string') {
cmd = applyTo({
app: cmd,
args: args || []
}, opt);
}
var task = project.createTask('exec'),
exe = cmd.app,
outProp = makeUniqueProperty(),
resultProp = makeUniqueProperty(),
arg;
// The x-foo.exe property is needed if the app is not in the PATH (or you are on
// Windows w/Cygwin or Mingw32 and the Unix-like PATH breaks the PATH search):
//
exe = project.getProperty('x-'+exe+'.exe') || exe;
task.setExecutable(exe);
task.setSearchPath(true);
if (cmd.capture !== false) {
// we sometimes need to not capture input our the child process may hang... it is
// useful for simple tools only
task.setOutputproperty(outProp);
}
task.setResultProperty(resultProp);
task.setFailonerror(cmd.failOnError !== false);
if (cmd.dir) {
task.setDir(new File(project.resolveFile(cmd.dir)));
}
var path = new org.apache.tools.ant.types.Environment.Variable();
path.setKey('PATH');
// this ensures the separators are correct (Ant likes ';' even w/platform uses ':'):
path.setPath(new org.apache.tools.ant.types.Path(project, project.getProperty('x-env.PATH')));
task.addEnv(path);
for (var i = 0, n = cmd.args.length; i < n; ++i) {
arg = task.createArg();
arg.setValue(cmd.args[i]);
}
Scope.echo(exe + ' ' + cmd.args.join(' '));
task.execute();
return {
exitCode: project.getProperty(resultProp),
output: project.getProperty(outProp)
};
},
getCurrentCommitHash: function() {
return Scope.exec('git', ['log', '-1', '--format=%H']).output;
},
makeUniqueProperty: function() {
var i = 0,
prop;
do {
prop = 'x-genprop-' + (++i);
} while (project.getProperty(prop + '-taken'));
project.setProperty(prop + '-taken', '1');
return prop;
},
/**
* Makes the URL as described by the params.
* @param {Object} params The parameters for the task
* @param {String} params.host The host (machine name or IP address) of the URL.
* @param {String} [params.scheme="http"] The protocol of the URL (e.g., "https").
* @param {int} [params.port] The port number
* @param {String} [params.path="/"] The path of the URL.
* @param {String[]} [params.query] The query parameters of the URL.
* @param {String} [params.fragment] The fragment of the URL.
* @return {String} The URL
*/
makeUrl: function(params) {
var uri = new URI(params.scheme || 'http',
null,
params.host,
Number(params.port) || -1, // on Mac, null||-1 is a boolean!
params.path || '/',
null, null),
url = String(uri.toString());
if (params.query.length) {
for (var a = [], i = 0, q = params.query, n = q.length; i < n; ++i) {
if (typeof q[i] == 'string') {
a.push(q[i]);
} else {
a.push((q[i].name + '=' + encodeURIComponent(q[i].value)));
}
}
url += '?' + a.join('&');
}
if (params.fragment) {
url += '#' + params.fragment;
}
//self.log(url);
return url;
},
/**
* Creates the specified directory or directories if multiple need to be created. The goal
* being to ensure that the specified directory exists if at all possible.
*
* @param {String} dir The name of the directory to create
* @return {String} The full path to the directory
*/
mkdir: function(dir) {
var task = project.createTask("mkdir"),
resolvedDir = project.resolveFile(dir);
task.dir = resolvedDir;
task.execute();
return resolvedDir.toString();
},
parseBool: function(bool) {
return /true|yes|on|1|y/i.test(String(bool));
},
/**
* Splits a set of paths (filenames) into an array. These paths should be separated by the
* platform's path separator (typically, ';' or ':').
* @param {String} paths The paths string to split
* @param {String} sep The path separator
* @return {String[]} The paths as individual strings.
*/
splitPaths: function(paths, sep) {
if (paths.indexOf(sep) >= 0) {
return paths.split(sep);
}
// On Linux, the Java File.pathSeparator is ":", but Ant still uses ";"
if (sep == ':' && paths.indexOf(';') >= 0) {
return paths.split(';');
}
return [ paths ];
},
readJson: function (fileName) {
var text = readFile(fileName);
return Scope.jsonDecode(text);
},
writeJson: function (fileName, object) {
var text = JSON.stringify(object, null, ' ');
writeFile(fileName, text + '\n');
},
/**
* Read the specified file and return an array of lines.
* @param {String} fileName The file name
* @param {Boolean} failIfNotFound False to return null if file not found (default is true).
* @return {String[]} The array containing the lines of the file or null.
*/
readLines: function(fileName, failIfNotFound) {
var f, lines = [ ], s;
try {
f = new BufferedReader(new FileReader(fileName));
while ((s = f.readLine()) !== null) {
lines.push(String(s));
}
} catch (e) {
if (failIfNotFound === false) {
return null;
}
self.fail('Cannot read file: ' + fileName + ': ' + e.message);
} finally {
if (f) {
f.close();
}
}
return lines;
},
/**
* Read the specified file and returns the file as a single string.
* @param {String} fileName The file name
* @param {Boolean} failIfNotFound False to return null if file not found (default is true).
* @return {String} The string with the content of the file or null.
*/
readFile: function(fileName, failIfNotFound) {
return FileUtil.readUnicodeFile(fileName) + '';
},
/**
* Writes the specified string to a file.
* @param {String} fileName The file name
* @param {String} text The string with the content of the file
*/
writeFile: function(fileName, text) {
FileUtil.writeFile(fileName, text);
},
/**
* Writes the specified array of lines to a file.
* @param {String} fileName The file name
* @param {String[]} lines The array containing the lines of the file
*/
writeLines: function(fileName, lines) {
FileUtil.writeFile(fileName, lines.join(CR));
},
xpathFind: function(node, xpath, type) {
var child = xpathFindNodes(node, xpath, type || 'NODE');
return child ? child.getNodeValue() : null;
},
xpathFindNodes: function(node, xpath, type) {
var xp = xpathCache[xpath];
if (!xp) {
var factory = javax.xml.xpath.XPathFactory.newInstance(),
instance = factory.newXPath();
xpathCache[xpath] = xp = instance.compile(xpath);
}
return xp.evaluate(node, XPathConstants[type || 'NODESET']);
},
jsonDecode: function(data) {
_logger.trace("decoding json data");
return JSON.parse(Scope.stripCommentsFromJson(data));
},
stripCommentsFromJson: function(json) {
return StringUtil.stripCommentsFromJson(json);
},
jsonEncode: function(obj, readable) {
_logger.debug("encoding json data");
var indent = null;
if(readable) {
indent = (typeof readable === 'string')
? readable
: ' ';
}
if(indent) {
return JSON.stringify(obj, null, indent);
} else {
return JSON.stringify(obj);
}
},
readConfig: function(configFile) {
_logger.trace("reading config data from {}", configFile);
var configData = null;
// eval the data to an object to remove comments and
// fix up escape sequences
eval('var configData = (' +
Scope.readFile(configFile)
+ ");");
return configData;
},
joinPath: function() {
var len = arguments.length, i, paths = [];
for (i = 0; i < len; i++) {
if (_logger.isTraceEnabled()) {
_logger.trace("adding path arg : {}", arguments[i]);
}
paths.push(arguments[i]);
}
return PathUtil.getAbsolutePath(paths.join(File.separator));
},
resolvePath: function() {
return new File(Scope.joinPath.apply(this, arguments)).getAbsolutePath();
},
copy: function(src, dest, filter) {
_logger.debug("copying {} to {}", src, dest);
if(filter) {
FileUtil.copy(src, dest, filter);
} else {
FileUtil.copy(src, dest);
}
},
copyFiles: function(proj, dir, todir, includes, excludes) {
var task = proj.createTask("copy"),
fileset = proj.createDataType('fileset');
fileset.setDir(new File(dir));
if(includes) {
fileset.setIncludes(includes);
}
if(excludes) {
fileset.setExcludes(excludes);
}
task.setTodir(new File(todir));
task.addFileset(fileset);
task.execute();
},
moveFiles: function(proj, from, to, includes, excludes) {
var task = proj.createTask("move"),
fileset = proj.createDataType('fileset');
fileset.setDir(new File(from));
if(includes) {
fileset.setIncludes(includes);
}
if(excludes) {
fileset.setExcludes(excludes);
}
task.setTodir(new File(to));
task.addFileset(fileset);
task.execute();
},
moveFile: function(proj, from, to) {
var task = proj.createTask("move");
task.setTofile(new File(to));
task.setFile(new File(from));
task.execute();
},
readFileContent: function(file) {
return FileUtil.readUnicodeFile(file);
},
readFileData: function(file) {
return FileUtil.readFileData(file);
},
writeFileContent: function(file, content) {
FileUtil.writeFile(file, content);
},
each: function(list, func) {
var len = list.length,
i;
for (i = 0; i < len; i++) {
func(list[i]);
}
return list;
},
map: function(list, func) {
var out = [],
len = list.length,
i;
for (i = 0; i < len; i++) {
out.push(func(list[i]));
}
return out;
},
endsWith: function(input, substr) {
// ensure js strings, not java strings
input = input + '';
substr = substr + '';
return input.indexOf(substr, input.length - substr.length) !== -1;
},
isChildPath: function(parent, child) {
var parentPath = PathUtil.getCanonicalPath(parent) + '',
childPath = PathUtil.getCanonicalPath(child) + '';
return childPath.indexOf(parentPath) === 0;
},
exists: function(path) {
return new File(path).exists();
},
filter: function(list, func) {
var newlist = [],
len = list.length,
i, item;
for (i = 0; i < len; i++) {
item = list[i];
if (func(item)) {
newlist.push(item);
}
}
return newlist;
},
concat: function(list1, list2) {
var newlist = [],
func = function (item) {
newlist.push(item);
};
Scope.each(list1, func);
Scope.each(list2, func);
return newlist;
},
stripSpecialDirNames: function(path) {
var cleanPath = (path + '')
.replace(/\.\.\\/g, "")
.replace(/\.\.\//g, "")
.replace(/\.\\/g, "")
.replace(/\.\//g, "")
.replace(/\~\//g, "");
return cleanPath;
},
/*
* Evaluates a template / directory of templates to a specified location
* with optional context variables
*
* @param {String} templatePath the path to tempmlate files to generate
* @param {String} outputPath the output path of evaluated templates
* @param {Object} params context variables for template evaluation
*/
generateTemplates: function (templatePath, outputPath, params) {
var generator = new Generator(),
context = generator.getContext();
if (params) {
for (var name in params) {
if (params.hasOwnProperty(name)) {
context.put(name, params[name]);
}
}
}
generator.setSource(templatePath);
generator.setTarget(outputPath);
generator.generate();
},
/**
* Set / update properties in a properties file
*
* @param {type} filename
* @param {type} name
* @param {type} value
*/
setProperty: function(filename, name, value) {
AntUtil.updatePropertyFile(project, filename, name, value);
},
/**
* Get a property from a properties file
*
* @param {type} filename
* @param {type} name
* @return {String} value
*/
getProperty: function(filename, name, value) {
return AntUtil.getProperty(project, filename, name);
},
/**
* Dispatches a sencha command
* @param {type} args
* @param {type} dir
* @param {type} inherit
* @param {type} customProps
*/
runSencha: function(args, dir, inherit, customProps) {
if(typeof inherit === 'undefined') {
inherit = true;
}
var sencha = new Sencha(false),
customConfig = sencha.getCustomConfiguration();
if(inherit === true) {
var antprops = project.getProperties(),
keys = antprops.keySet().toArray(),
key, k, value;
for(k=0; k < keys.length; k++) {
key = keys[k];
value = project.getProperty(key);
_logger.trace("setting sencha config prop " + key + " : " + value);
customConfig.set(key, value);
}
}
if(customProps) {
for(key in customProps) {
_logger.trace("setting sencha config prop " + key + " : " + value);
customConfig.set(key, customProps[key] + '');
}
}
if(dir) {
_logger.trace("setting sencha cwd to " + dir);
sencha.setCwd(dir);
}
sencha.loadBuildEnvironment();
sencha.dispatch(args);
}
});
//-----------------------------------------------------------------------------
/**
* This class provides an iterator of a set of single file or directory elements.
*/
function FileSequence (config) {
applyTo(this, config);
this.index = 0;
this.count = this.dirs ? this.dirs.size() : 0;
}
FileSequence.prototype = {
/**
* @cfg
* The name of the attribute on each element that contains the path.
*/
attr: 'path',
append: function (seq) {
return new UnionSequence({
sets: [this, seq]
});
},
next: function () {
var dir = this.peek();
this.currentDir = null; // forces us to advance
return dir;
},
peek: function () {
var currentDir = this.currentDir;
if (!currentDir && this.index < this.count) {
var dir = this.dirs.get(this.index++);
dir = dir.getRuntimeConfigurableWrapper();
currentDir = project.resolveFile(dir.getAttributeMap().get(this.attr));
this.currentDir = currentDir;
}
return currentDir;
}
};
//-----------------------------------------------------------------------------
/**
* This class provides an iterator of the files in a set of filesets.
*/
function FileSetSequence (config) {
applyTo(this, config);
this.pathSep = String(File.pathSeparator);
this.fileSep = String(File.separator);
this.fileSetIndex = 0;
this.nameIndex = 0;
this.numFileSets = this.fileSets ? this.fileSets.size() : 0;
}
FileSetSequence.prototype = {
append: function (seq) {
return new UnionSequence({
sets: [this, seq]
});
},
next: function () {
var file = this.peek();
this.currentFile = null; // forces us to advance
return file;
},
peek: function () {
var currentFile = this.currentFile;
if (!currentFile) {
var fileNames = this.fileNames;
// This is a loop since a fileset can be empty...
while (!fileNames || this.nameIndex == fileNames.length) {
if (this.fileSetIndex == this.numFileSets) {
return null;
}
var fileSet = this.fileSets.get(this.fileSetIndex++);
if (this.dirsOnly) {
fileNames = [''];
} else {
fileNames = splitPaths(String(fileSet), this.pathSep);
}
this.currentDir = fileSet.getDir(project) + this.fileSep;
this.fileNames = fileNames;
this.nameIndex = 0;
}
this.currentFile = currentFile = this.currentDir + fileNames[this.nameIndex++];
}
return currentFile;
}
};
//-----------------------------------------------------------------------------
/**
* This class provides an iterator over a set of iteratable collections.
*/
function UnionSequence (config) {
applyTo(this, config);
this.index = 0;
}
UnionSequence.prototype = {
append: function (seq) {
this.sets.push(seq);
return this;
},
next: function () {
var ret = this.peek();
this.current = null;
return ret;
},
peek: function () {
var current = this.current;
while (!current && this.index < this.sets.length) {
current = this.sets[this.index].next();
if (!current) {
++this.index;
}
}
return current;
}
};
/**
*
* Small utility class that extract github user and repo from a git url
*/
var Github = {
httpRe: /https:\/\/[^@]+@github.com\/([^\/]+)\/([^\.]+).git/,
gitRe: /git@github.com:([^\/]+)\/([^\.]+).git/,
extract: function(url) {
var match = url.match(this.httpRe) || url.match(this.gitRe);
if (match && match.length == 3) {
return {
user: match[1],
repo: match[2]
}
}
return match;
},
extractUser: function(url) {
var extract = this.extract(url);
if (extract === null) {
self.fail("Github.extractUser: Unable to extract user from git url " + url);
}
return extract.user;
},
extractRepo: function(url) {
var extract = this.extract(url);
if (extract === null) {
self.fail("Github.extractRepo: Unable to extract repo from git url " + url);
}
return extract.repo;
}
};
applyTo(Scope, {
'FileSequence' : FileSequence,
'FileSetSequence' : FileSetSequence,
'UnionSequence' : UnionSequence,
'Github' : Github
});
})(this);
/*
* This file contains the guts of the <process-includes> task.
*/
/**
* Process the includes as described by the params.
* @param {Object} params The parameters for the task
* @param {String} params.src The srcfile attribute fully resolved
* @param {String} params.out The outfile attribute fully resolved and defaulting to src.
* @param {RegExp} params.regex The regex to match include statements.
*/
function processIncludes (params) {
self.log('Read: ' + params.src);
var lines = readLines(params.src),
include, match;
for (var i = 0, n = lines.length; i < n; ++i) {
match = params.regex.exec(lines[i]);
if (match) {
include = project.resolveFile(match[1]);
self.log('Include: ' + include);
lines[i] = readFile(include);
}
}
self.log('Write: ' + params.out);
writeLines(params.out, lines);
}
// Packs up the task parameters from the funky Ant way of retrieving them.
(function () {
var src = project.resolveFile(attributes.get('srcfile'));
processIncludes({
src: src,
out: attributes.get('outfile') || src,
regex: new RegExp(attributes.get('regex') || '^\\s*#include\\s+\\"(.+)\\"\\s*$')
});
})();