stop tracking build artifacts

This commit is contained in:
daboe01
2026-03-11 06:35:24 +01:00
parent 37a6e8fde5
commit ea425ae4c4
11 changed files with 0 additions and 1508 deletions
-9
View File
@@ -1,9 +0,0 @@
#!/usr/bin/env node
const path = require("path");
var commandName = path.basename(process.argv[1]);
var mainPath = path.resolve(__dirname, "..", "lib", commandName, "main.j");
var args = ["objj", mainPath].concat(process.argv.slice(2));
require("@objj/runtime").run(args);
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env objj
@import <Foundation/Foundation.j>
@import "../lib/cappuccino/cib-analysis-tools.j"
var FILE = require("file");
CPLogRegister(CPLogPrint);
function main(args)
{
args.slice(1).forEach(function(path) {
var classes = findCibClassDependencies(FILE.absolute(path));
print(path+":");
classes.sort().forEach(function(className) { print(" + " + className) });
});
}
-368
View File
@@ -1,368 +0,0 @@
#!/usr/bin/env objj
require("narwhal").ensureEngine("rhino");
@import <Foundation/Foundation.j>
@import "../lib/cappuccino/objj-analysis-tools.j"
var FILE = require("file");
var OS = require("os");
var UTIL = require("narwhal/util");
var CACHEMANIFEST = require("objective-j/cache-manifest");
var stream = require("narwhal/term").stream;
var parser = new (require("narwhal/args").Parser)();
parser.usage("INPUT_PROJECT OUTPUT_PROJECT");
parser.help("Combine a Cappuccino application into a single JavaScript file.");
parser.option("-m", "--main", "main")
.def("main.j")
.set()
.help("The relative path (from INPUT_PROJECT) to the main file (default: 'main.j')");
parser.option("-F", "--framework", "frameworks")
.push()
.help("Add a frameworks directory, relative to INPUT_PROJECT (default: ['Frameworks'])");
parser.option("-P", "--path", "paths")
.push()
.help("Add a path (relative to the application root) to inline.");
parser.option("-f", "--force", "force")
.def(false)
.set(true)
.help("Force overwriting OUTPUT_PROJECT if it exists");
parser.option("--index", "index")
.def("index.html")
.set()
.help("The root HTML file to modify (default: index.html)");
parser.option("-s", "--split", "number", "split")
.natural()
.def(0)
.help("Split into multiple files");
parser.option("-c", "--compressor", "compressor")
.def("shrinksafe")
.set()
.help("Select a compressor to use (closure-compiler, yuicompressor, shrinksafe), or \"none\" (default: shrinksafe)");
parser.option("--manifest", "manifest")
.set(true)
.help("Generate HTML5 cache manifest.");
parser.option("-v", "--verbose", "verbose")
.def(false)
.set(true)
.help("Verbose logging");
parser.helpful();
function main(args)
{
var options = parser.parse(args);
if (options.args.length < 2) {
parser.printUsage(options);
return;
}
var rootPath = FILE.path(options.args[0]).join("").absolute();
var outputPath = FILE.path(options.args[1]).join("").absolute();
if (outputPath.exists()) {
if (options.force) {
// FIXME: why doesn't this work?!
//outputPath.rmtree();
OS.system(["rm", "-rf", outputPath]);
} else {
stream.print("\0red(OUTPUT_PROJECT " + outputPath + " exists. Use -f to overwrite.\0)");
OS.exit(1);
}
}
options.frameworks.push("Frameworks");
var mainPath = String(rootPath.join(options.main));
var frameworks = options.frameworks.map(function(framework) { return rootPath.join(framework); });
var environment = "Browser";
stream.print("\0yellow("+Array(81).join("=")+"\0)");
stream.print("Application root: \0green(" + rootPath + "\0)");
stream.print("Output directory: \0green(" + outputPath + "\0)");
stream.print("\0yellow("+Array(81).join("=")+"\0)");
stream.print("Main file: \0green(" + mainPath + "\0)");
stream.print("Frameworks: \0green(" + frameworks + "\0)");
stream.print("Environment: \0green(" + environment + "\0)");
var flattener = new ObjectiveJFlattener(rootPath);
flattener.options = options;
flattener.setIncludePaths(frameworks);
flattener.setEnvironments([environment, "ObjJ"]);
print("Loading application.");
flattener.load(mainPath);
print("Loading default theme.");
flattener.require("objective-j").objj_eval("("+(function() {
var defaultThemeName = [CPApplication defaultThemeName],
bundle = nil;
if (defaultThemeName === @"Aristo" || defaultThemeName === @"Aristo2")
bundle = [CPBundle bundleForClass:[CPApplication class]];
else
bundle = [CPBundle mainBundle];
var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[bundle pathForResource:defaultThemeName + @".blend"]];
[blend loadWithDelegate:nil];
})+")")();
var applicationJSs = flattener.buildApplicationJS();
FILE.copyTree(rootPath, outputPath);
applicationJSs.forEach(function(applicationJS, n) {
var name = "Application"+(n||"")+".js";
if (options.compressor === "none") {
print("skipping compression: " + name);
} else {
print("compressing: " + name);
applicationJS = require("minify/"+options.compressor).compress(applicationJS, { charset : "UTF-8", useServer : true });
}
outputPath.join(name).write(applicationJS, { charset : "UTF-8" });
});
rewriteMainHTML(outputPath.join(options.index));
if (options.manifest) {
CACHEMANIFEST.generateManifest(outputPath, {
index : outputPath.join(options.index),
exclude : Object.keys(flattener.filesToCache).map(function(path) { return outputPath.join(path).toString(); })
});
}
}
// ObjectiveJFlattener inherits from ObjectiveJRuntimeAnalyzer
function ObjectiveJFlattener(rootPath) {
ObjectiveJRuntimeAnalyzer.apply(this, arguments);
this.filesToCache = {};
this.fileCacheBuffer = [];
this.functionsBuffer = [];
}
ObjectiveJFlattener.prototype = Object.create(ObjectiveJRuntimeAnalyzer.prototype);
ObjectiveJFlattener.prototype.buildApplicationJS = function() {
this.setupFileCache();
this.serializeFunctions();
this.serializeFileCache();
var additions = FILE.read(FILE.join(FILE.dirname(module.path), "..", "..", "cappuccino", "lib", "cappuccino", "objj-flatten-additions.js"), { charset:"UTF-8" });
var applicationJSs = [];
if (this.options.split === 0) {
var buffer = [];
buffer.push("var baseURL = new CFURL(\".\", ObjectiveJ.pageURL);");
buffer.push(additions);
buffer.push(this.fileCacheBuffer.join("\n"));
buffer.push(this.functionsBuffer.join("\n"));
buffer.push("ObjectiveJ.bootstrap();");
applicationJSs.push(buffer.join("\n"));
} else {
var appFilesCount = this.options.split;
var buffers = [];
for (var i = 0; i <= appFilesCount; i++)
buffers.push([]);
var chunks = this.fileCacheBuffer.concat(this.functionsBuffer).sort(function(chunkA, chunkB) {
return chunkA.length - chunkB.length;
});
// try to equally distribute the chunks. could be better but good enough for now.
var n = 0;
while (chunks.length) {
buffers[(n++ % appFilesCount) + 1].push(chunks.pop());
}
buffers[0].push("var baseURL = new CFURL(\".\", ObjectiveJ.pageURL);");
buffers[0].push(additions);
buffers[0].push("var appFilesCount = " + appFilesCount +";");
buffers[0].push("for (var i = 1; i <= appFilesCount; i++) {");
buffers[0].push(" var script = document.createElement(\"script\");");
buffers[0].push(" script.src = \"Application\"+i+\".js\";");
buffers[0].push(" script.charset = \"UTF-8\";");
buffers[0].push(" script.onload = function() { if (--appFilesCount === 0) ObjectiveJ.bootstrap(); };");
buffers[0].push(" document.getElementsByTagName(\"head\")[0].appendChild(script);");
buffers[0].push("}");
buffers.forEach(function(buffer) {
applicationJSs.push(buffer.join("\n"));
});
}
return applicationJSs;
}
ObjectiveJFlattener.prototype.serializeFunctions = function() {
var inlineFunctions = true;//this.options.inlineFunctions;
var outputFiles = {};
var _cachedExecutableFunctions = {};
this.require("objective-j").FileExecutable.allFileExecutables().forEach(function(executable) {
var path = executable.path();
if (inlineFunctions)
{
// stringify the function, replacing arguments
var functionString = executable._function.toString().replace(", require, exports, module, system, print, window", ""); // HACK
var relative = this.rootPath.relative(path).toString();
this.functionsBuffer.push("ObjectiveJ.StaticResource._cacheFunction(new CFURL("+JSON.stringify(relative)+", baseURL),\n"+functionString+");");
}
var bundle = this.context.global.CFBundle.bundleContainingURL(path);
if (bundle && bundle.infoDictionary())
{
var executablePath = bundle.executablePath(),
relativeToBundle = FILE.relative(FILE.join(bundle.path(), ""), path);
if (executablePath)
{
if (inlineFunctions)
{
// remove the code since we're inlining the functions
executable._code = "alert("+JSON.stringify(relativeToBundle)+");";
}
if (!outputFiles[executablePath])
{
outputFiles[executablePath] = [];
outputFiles[executablePath].push("@STATIC;1.0;");
}
var fileContents = executable.toMarkedString();
outputFiles[executablePath].push("p;" + relativeToBundle.length + ";" + relativeToBundle);
outputFiles[executablePath].push("t;" + fileContents.length + ";" + fileContents);
// stream.print("Adding \0green(" + this.rootPath.relative(path) + "\0) to \0cyan(" + this.rootPath.relative(executablePath) + "\0)");
}
}
else
CPLog.warn("No bundle (or info dictionary for) " + rootPath.relative(path));
}, this);
for (var executablePath in outputFiles)
{
var relative = this.rootPath.relative(executablePath).toString();
var contents = outputFiles[executablePath].join("");
this.filesToCache[relative] = contents;
}
}
ObjectiveJFlattener.prototype.serializeFileCache = function() {
for (var relative in this.filesToCache) {
var contents = this.filesToCache[relative];
print("caching: " + relative + " => " + (contents == null ? 404 : 200));
if (contents == null)
this.fileCacheBuffer.push("CFHTTPRequest._cacheRequest(new CFURL("+JSON.stringify(relative)+", baseURL), 404);");
else
this.fileCacheBuffer.push("CFHTTPRequest._cacheRequest(new CFURL("+JSON.stringify(relative)+", baseURL), 200, {}, "+JSON.stringify(contents)+");");
}
}
ObjectiveJFlattener.prototype.setupFileCache = function() {
var paths = {};
UTIL.update(paths, this.requestedURLs);
this.options.paths.forEach(function(relativePath) {
paths[this.rootPath.join(relativePath)] = true;
}, this);
Object.keys(paths).forEach(function(absolute) {
var relative = this.rootPath.relative(absolute).toString();
if (relative.indexOf("..") === 0)
{
print("skipping (parent of app root): " + absolute);
return;
}
if (FILE.isFile(absolute))
{
// if (this.options.maxCachedSize && FILE.size(absolute) > this.options.maxCachedSize)
// {
// print("skipping (larger than "+this.options.maxCachedSize+" bytes): " + absolute);
// return;
// }
var contents = FILE.read(absolute, { charset : "UTF-8" });
this.filesToCache[relative] = contents;
} else {
this.filesToCache[relative] = null;
}
}, this);
}
// "$1" is the matching indentation
var scriptTagsBefore =
'$1<script type = "text/javascript">\n'+
'$1 OBJJ_AUTO_BOOTSTRAP = false;\n'+
'$1</script>';
var scriptTagsAfter =
'$1<script type="text/javascript" src="Application.js" charset="UTF-8"></script>';
// enable CPLog:
// scriptTagsAfter = '$1<script type="text/javascript">\n$1 CPLogRegister(CPLogConsole);\n$1</script>\n' + scriptTagsAfter;
function rewriteMainHTML(indexHTMLPath) {
if (indexHTMLPath.isFile()) {
var indexHTML = indexHTMLPath.read({ charset : "UTF-8" });
// inline the Application.js if it's smallish
var applicationJSPath = indexHTMLPath.dirname().join("Application.js");
if (applicationJSPath.size() < 10*1024) {
// escape any dollar signs by replacing them with two
// then indent by splitting/joining on newlines
scriptTagsAfter =
'$1<script type="text/javascript">\n'+
'$1 ' + applicationJSPath.read({ charset : "UTF-8" }).replace(/\$/g, "$$$$").split("\n").join("\n$1 ")+'\n'+
'$1</script>';
}
// attempt to find Objective-J script tag and add ours
var newIndexHTML = indexHTML.replace(/([ \t]+)<script[^>]+Objective-J\.js[^>]+>(?:\s*<\/script>)?/,
scriptTagsBefore+'\n$&\n'+scriptTagsAfter);
if (newIndexHTML !== indexHTML) {
stream.print("\0green(Modified: "+indexHTMLPath+".\0)");
indexHTMLPath.write(newIndexHTML, { charset : "UTF-8" });
return;
}
} else {
stream.print("\0yellow(Warning: "+indexHTMLPath+" does not exist. Specify an alternate index HTML file with the --index option.\0)");
}
stream.print("\0yellow(Warning: Unable to automatically modify "+indexHTMLPath + ".\0)");
stream.print("\nAdd the following before the Objective-J script tag:");
stream.print(scriptTagsBefore.replace(/\$1/g, " "));
stream.print("\nAdd the following after the Objective-J script tag:");
stream.print(scriptTagsAfter.replace(/\$1/g, " "));
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env node
JAKE = require("@objj/jake");
ObjectiveJ = require("@objj/runtime");
CAPPUCCINO = require("@objj/cappuccino");
JAKE.application().run({ args : process.argv });
-9
View File
@@ -1,9 +0,0 @@
#!/usr/bin/env node
const path = require("path");
var commandName = path.basename(process.argv[1]);
var mainPath = path.resolve(__dirname, "..", "lib", commandName, "main.j");
var args = ["objj", mainPath].concat(process.argv.slice(2));
require("@objj/runtime").run(args);
-613
View File
@@ -1,613 +0,0 @@
#!/usr/bin/env objj
@import <Foundation/Foundation.j>
var fs = require("fs"),
acorn = require("objj-parser"),
walk = require("objj-parser/util/walk"),
stream = ObjectiveJ.term;
debugger;
function main(args)
{
debugger;
args.shift();
if (args.length < 1)
return printUsage();
parser(args);
}
function printUsage()
{
console.log("objj2objskeleton [FILE] [DESTINATION]");
console.log("Convert a objective-j file to an objective-c files skeleton (.h and .m)")
}
// Debug function to print some JS objects
function dump(obj)
{
console.log(JSON.stringify(obj));
}
function raise(pos, message)
{
var syntaxError = new SyntaxError(message);
syntaxError.line = pos.line;
throw syntaxError;
}
var errors = [],
xcc = walk.make(
{
ClassDeclarationStatement: function(node, st, c)
{
var className = node.classname.name,
superclassname = node.superclassname ? node.superclassname.name : "",
declaredOutletsName = [],
classInfo = {
"name": className,
"category": node.categoryname ? node.categoryname.name : "",
"superClass": superclassname,
"outlets": [],
"actions": [],
"actionNames": []
};
if (node.ivardeclarations)
{
for (var i = 0; i < node.ivardeclarations.length; ++i)
{
var ivarDecl = node.ivardeclarations[i],
ivarType = ivarDecl.ivartype ? ivarDecl.ivartype.name : null,
ivarName = ivarDecl.id.name,
ivarHasOutlet = ivarDecl.outlet ? "@outlet" : null;
if (ivarHasOutlet)
{
if (declaredOutletsName.indexOf(ivarName) !== -1)
raise(ivarDecl.loc.start, "Outlet '" + ivarName + "' declared more than once");
declaredOutletsName.push(ivarName);
classInfo.outlets.push({"type": ivarType, "name": ivarName});
}
}
}
st.push(classInfo)
for (var i = 0; i < node.body.length; ++i)
c(node.body[i], classInfo, "Statement");
},
MethodDeclarationStatement: function(node, st, c)
{
var selectors = node.selectors,
arguments = node.arguments,
//methodReturnType = [node.returntype ? node.returntype.name : "id"],
methodHasAction = node.action ? "IBAction" : null,
selector = selectors[0].name,
actionInfo = {"name": selector, "arguments":[]};
if (methodHasAction)
{
if (arguments.length == 1)
{
if (st.actionNames.indexOf(selector) !== -1)
raise(node.loc.start, "Action '" + selector + "' declared more than once");
st.actionNames.push(selector);
for (var i = 0; i < arguments.length; i++)
{
var argument = arguments[i],
argumentName = argument.identifier.name,
argumentType = argument.type ? argument.type.name : null;
actionInfo.arguments.push({"type": argumentType, "name": argumentName});
}
st.actions.push(actionInfo)
}
else
raise(node.loc.start, "Action methods must have exactly one parameter");
}
}
}
);
function compile(node, state, visitor)
{
function c(node, st, override)
{
visitor[override || node.type](node, st, c);
}
c(node, state);
};
function removeLastSlashIfNecessary(path)
{
if (path[path.length - 1] == "/")
return path.substring(0, path.length - 1);
return path;
}
/*
$1 Full project source path
$2 Destination
$-n name of the cocoa files
*/
function parser(args)
{
try
{
var sourcePath = args.shift(),
projectBasePath = removeLastSlashIfNecessary(args.shift()),
outputDirectory = projectBasePath,
baseFilename = [sourcePath lastPathComponent],
baseFilenameWithNoExtension = args.shift() == "-n" ? args.shift() : baseFilename.substring(0, baseFilename.length - 2),
outputHeaderURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilenameWithNoExtension + ".h"]),
outputImplementationURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilenameWithNoExtension + ".m"]),
source = fs.readFileSync(sourcePath, { encoding: "utf8" }),
tokens = acorn.parse(source, { locations:true, sourceFile:sourcePath }),
classesInformation = [],
ObjectiveCSource = "",
ObjectiveCHeader = "",
hasErrors = NO;
compile(tokens, classesInformation, xcc);
// dump(classesInformation)
ObjectiveCHeader +=
"#import <Cocoa/Cocoa.h>\n" +
'#import "xcc_general_include.h"\n';
ObjectiveCSource += "#import \"" + outputHeaderURL.lastPathComponent() + "\"\n";
// Traverse each found classes
classesInformation.forEach(function(aClass)
{
// add new class definition
if (aClass.superClass)
ObjectiveCHeader += [CPString stringWithFormat:@"\n@interface %@ : %@", aClass.name, NSCompatibleClassName(aClass.superClass)];
else
ObjectiveCHeader += [CPString stringWithFormat:@"\n@interface %@ (%@)", NSCompatibleClassName(aClass.name, NO), aClass.category];
// add each outlet in header
if (aClass.outlets.length > 0)
ObjectiveCHeader += "\n";
aClass.outlets.forEach(function(anOutlet)
{
ObjectiveCHeader += [CPString stringWithFormat:@"\n@property (assign) IBOutlet %@ %@;", NSCompatibleClassName(anOutlet.type, YES), anOutlet.name];
});
if (aClass.actions.length > 0)
ObjectiveCHeader += "\n";
// add each action in header
aClass.actions.forEach(function(anAction)
{
ObjectiveCHeader += [CPString stringWithFormat:@"\n- (IBAction)%@:(%@)%@;", anAction.name, anAction.arguments[0].type, anAction.arguments[0].name];
});
if (aClass.outlets.length > 0 || aClass.actions.length > 0)
ObjectiveCHeader += "\n";
ObjectiveCHeader += "\n@end\n";
// fill up the implementation file
ObjectiveCSource += "\n@implementation " + NSCompatibleClassName(aClass.name, NO) + "\n@end\n";
});
if (ObjectiveCSource.length)
fs.writeFileSync(outputImplementationURL.absoluteString(), ObjectiveCSource, 'utf8');
if (ObjectiveCHeader.length)
fs.writeFileSync(outputHeaderURL.absoluteString(), ObjectiveCHeader, 'utf8');
}
catch (e)
{
[errors addObject:@{
@"message": e.message,
@"sourcePath": sourcePath,
@"line": e.line
}];
hasErrors = YES;
}
if ([errors count])
{
var plist = [CPPropertyListSerialization dataFromPropertyList:errors format:CPPropertyListXMLFormat_v1_0];
stream.printError([plist rawString]);
// If there were category warnings, hasErrors is NO, so return a warning status
process.exit(hasErrors ? 1 : 2);
}
}
function NSCompatibleClassName(aClassName, asPointer)
{
if (aClassName === "var" || aClassName === "id")
return "id";
var prefix = aClassName.substr(0, 2),
asterisk = asPointer ? "*" : "";
if (prefix !== "CP")
return aClassName + asterisk;
var NSClassName = "NS" + aClassName.substr(2);
if (NSClasses[NSClassName])
return NSClassName + asterisk;
if (ReplacementClasses[aClassName])
return ReplacementClasses[aClassName] + asterisk;
return aClassName + asterisk;
}
var ReplacementClasses = {
"CPWebView": "WebView",
"CPRadio": "NSButtonCell",
"CPRadioGroup": "NSMatrix"
};
var NSClasses = {
"NSAffineTransform" : YES,
"NSAppleEventDescriptor" : YES,
"NSAppleEventManager" : YES,
"NSAppleScript" : YES,
"NSArchiver" : YES,
"NSArray" : YES,
"NSAssertionHandler" : YES,
"NSAttributedString" : YES,
"NSAutoreleasePool" : YES,
"NSBlockOperation" : YES,
"NSBundle" : YES,
"NSCache" : YES,
"NSCachedURLResponse" : YES,
"NSCalendar" : YES,
"NSCharacterSet" : YES,
"NSClassDescription" : YES,
"NSCloneCommand" : YES,
"NSCloseCommand" : YES,
"NSCoder" : YES,
"NSComparisonPredicate" : YES,
"NSCompoundPredicate" : YES,
"NSCondition" : YES,
"NSConditionLock" : YES,
"NSConnection" : YES,
"NSCountCommand" : YES,
"NSCountedSet" : YES,
"NSCreateCommand" : YES,
"NSData" : YES,
"NSDate" : YES,
"NSDateComponents" : YES,
"NSDateFormatter" : YES,
"NSDecimalNumber" : YES,
"NSDecimalNumberHandler" : YES,
"NSDeleteCommand" : YES,
"NSDeserializer" : YES,
"NSDictionary" : YES,
"NSDirectoryEnumerator" : YES,
"NSDistantObject" : YES,
"NSDistantObjectRequest" : YES,
"NSDistributedLock" : YES,
"NSDistributedNotificationCenter" : YES,
"NSEnumerator" : YES,
"NSError" : YES,
"NSException" : YES,
"NSExistsCommand" : YES,
"NSExpression" : YES,
"NSFileHandle" : YES,
"NSFileManager" : YES,
"NSFileWrapper" : YES,
"NSFormatter" : YES,
"NSGarbageCollector" : YES,
"NSGetCommand" : YES,
"NSHashTable" : YES,
"NSHost" : YES,
"NSHTTPCookie" : YES,
"NSHTTPCookieStorage" : YES,
"NSHTTPURLResponse" : YES,
"NSIndexPath" : YES,
"NSIndexSet" : YES,
"NSIndexSpecifier" : YES,
"NSInputStream" : YES,
"NSInvocation" : YES,
"NSInvocationOperation" : YES,
"NSKeyedArchiver" : YES,
"NSKeyedUnarchiver" : YES,
"NSLocale" : YES,
"NSLock" : YES,
"NSLogicalTest" : YES,
"NSMachBootstrapServer" : YES,
"NSMachPort" : YES,
"NSMapTable" : YES,
"NSMessagePort" : YES,
"NSMessagePortNameServer" : YES,
"NSMetadataItem" : YES,
"NSMetadataQuery" : YES,
"NSMetadataQueryAttributeValueTuple" : YES,
"NSMetadataQueryResultGroup" : YES,
"NSMethodSignature" : YES,
"NSMiddleSpecifier" : YES,
"NSMoveCommand" : YES,
"NSMutableArray" : YES,
"NSMutableAttributedString" : YES,
"NSMutableCharacterSet" : YES,
"NSMutableData" : YES,
"NSMutableDictionary" : YES,
"NSMutableIndexSet" : YES,
"NSMutableSet" : YES,
"NSMutableString" : YES,
"NSMutableURLRequest" : YES,
"NSNameSpecifier" : YES,
"NSNetService" : YES,
"NSNetServiceBrowser" : YES,
"NSNotification" : YES,
"NSNotificationCenter" : YES,
"NSNotificationQueue" : YES,
"NSNull" : YES,
"NSNumber" : YES,
"NSNumberFormatter" : YES,
"NSObject" : YES,
"NSOperation" : YES,
"NSOperationQueue" : YES,
"NSOrthography" : YES,
"NSOutputStream" : YES,
"NSPipe" : YES,
"NSPointerArray" : YES,
"NSPointerFunctions" : YES,
"NSPort" : YES,
"NSPortCoder" : YES,
"NSPortMessage" : YES,
"NSPortNameServer" : YES,
"NSPositionalSpecifier" : YES,
"NSPredicate" : YES,
"NSProcessInfo" : YES,
"NSPropertyListSerialization" : YES,
"NSPropertySpecifier" : YES,
"NSProtocolChecker" : YES,
"NSProxy" : YES,
"NSPurgeableData" : YES,
"NSQuitCommand" : YES,
"NSRandomSpecifier" : YES,
"NSRangeSpecifier" : YES,
"NSRecursiveLock" : YES,
"NSRelativeSpecifier" : YES,
"NSRunLoop" : YES,
"NSScanner" : YES,
"NSScriptClassDescription" : YES,
"NSScriptCoercionHandler" : YES,
"NSScriptCommand" : YES,
"NSScriptCommandDescription" : YES,
"NSScriptExecutionContext" : YES,
"NSScriptObjectSpecifier" : YES,
"NSScriptSuiteRegistry" : YES,
"NSScriptWhoseTest" : YES,
"NSSerializer" : YES,
"NSSet" : YES,
"NSSetCommand" : YES,
"NSSocketPort" : YES,
"NSSocketPortNameServer" : YES,
"NSSortDescriptor" : YES,
"NSSpecifierTest" : YES,
"NSSpellServer" : YES,
"NSStream" : YES,
"NSString" : YES,
"NSTask" : YES,
"NSTextCheckingResult" : YES,
"NSThread" : YES,
"NSTimer" : YES,
"NSTimeZone" : YES,
"NSUnarchiver" : YES,
"NSUndoManager" : YES,
"NSUniqueIDSpecifier" : YES,
"NSURL" : YES,
"NSURLAuthenticationChallenge" : YES,
"NSURLCache" : YES,
"NSURLConnection" : YES,
"NSURLCredential" : YES,
"NSURLCredentialStorage" : YES,
"NSURLDownload" : YES,
"NSURLHandle" : YES,
"NSURLProtectionSpace" : YES,
"NSURLProtocol" : YES,
"NSURLRequest" : YES,
"NSURLResponse" : YES,
"NSUserDefaults" : YES,
"NSValue" : YES,
"NSValueTransformer" : YES,
"NSWhoseSpecifier" : YES,
"NSXMLDocument" : YES,
"NSXMLDTD" : YES,
"NSXMLDTDNode" : YES,
"NSXMLElement" : YES,
"NSXMLNode" : YES,
"NSXMLParser" : YES,
"NSActionCell" : YES,
"NSAffineTransform Additions" : YES,
"NSAlert" : YES,
"NSAnimation" : YES,
"NSAnimationContext" : YES,
"NSAppleScript Additions" : YES,
"NSApplication" : YES,
"NSArrayController" : YES,
"NSATSTypesetter" : YES,
"NSAttributedString Application Kit Additions" : YES,
"NSBezierPath" : YES,
"NSBitmapImageRep" : YES,
"NSBox" : YES,
"NSBrowser" : YES,
"NSBrowserCell" : YES,
"NSBundle Additions" : YES,
"NSButton" : YES,
"NSButtonCell" : YES,
"NSCachedImageRep" : YES,
"NSCell" : YES,
"NSCIImageRep" : YES,
"NSClipView" : YES,
"NSCoder Application Kit Additions" : YES,
"NSCollectionView" : YES,
"NSCollectionViewItem" : YES,
"NSColor" : YES,
"NSColorList" : YES,
"NSColorPanel" : YES,
"NSColorPicker" : YES,
"NSColorSpace" : YES,
"NSColorWell" : YES,
"NSComboBox" : YES,
"NSComboBoxCell" : YES,
"NSControl" : YES,
"NSController" : YES,
"NSCursor" : YES,
"NSCustomImageRep" : YES,
"NSDatePicker" : YES,
"NSDatePickerCell" : YES,
"NSDictionaryController" : YES,
"NSDockTile" : YES,
"NSDocument" : YES,
"NSDocumentController" : YES,
"NSDrawer" : YES,
"NSEPSImageRep" : YES,
"NSEvent" : YES,
"NSFileWrapper" : YES,
"NSFont" : YES,
"NSFontDescriptor" : YES,
"NSFontManager" : YES,
"NSFontPanel" : YES,
"NSForm" : YES,
"NSFormCell" : YES,
"NSGlyphGenerator" : YES,
"NSGlyphInfo" : YES,
"NSGradient" : YES,
"NSGraphicsContext" : YES,
"NSHelpManager" : YES,
"NSImage" : YES,
"NSImageCell" : YES,
"NSImageRep" : YES,
"NSImageView" : YES,
"NSLayoutManager" : YES,
"NSLevelIndicator" : YES,
"NSLevelIndicatorCell" : YES,
"NSMatrix" : YES,
"NSMenu" : YES,
"NSMenuItem" : YES,
"NSMenuItemCell" : YES,
"NSMenuView" : YES,
"NSMutableAttributedString Additions" : YES,
"NSMutableParagraphStyle" : YES,
"NSNib" : YES,
"NSNibConnector" : YES,
"NSNibControlConnector" : YES,
"NSNibOutletConnector" : YES,
"NSObjectController" : YES,
"NSOpenGLContext" : YES,
"NSOpenGLLayer" : YES,
"NSOpenGLPixelBuffer" : YES,
"NSOpenGLPixelFormat" : YES,
"NSOpenGLView" : YES,
"NSOpenPanel" : YES,
"NSOutlineView" : YES,
"NSPageLayout" : YES,
"NSPanel" : YES,
"NSParagraphStyle" : YES,
"NSPasteboard" : YES,
"NSPasteboardItem" : YES,
"NSPathCell" : YES,
"NSPathComponentCell" : YES,
"NSPathControl" : YES,
"NSPDFImageRep" : YES,
"NSPersistentDocument" : YES,
"NSPICTImageRep" : YES,
"NSPopUpButton" : YES,
"NSPopUpButtonCell" : YES,
"NSPredicateEditor" : YES,
"NSPredicateEditorRowTemplate" : YES,
"NSPrinter" : YES,
"NSPrintInfo" : YES,
"NSPrintOperation" : YES,
"NSPrintPanel" : YES,
"NSProgressIndicator" : YES,
"NSResponder" : YES,
"NSRuleEditor" : YES,
"NSRulerMarker" : YES,
"NSRulerView" : YES,
"NSRunningApplication" : YES,
"NSSavePanel" : YES,
"NSScreen" : YES,
"NSScroller" : YES,
"NSScrollView" : YES,
"NSSearchField" : YES,
"NSSearchFieldCell" : YES,
"NSSecureTextField" : YES,
"NSSecureTextFieldCell" : YES,
"NSSegmentedCell" : YES,
"NSSegmentedControl" : YES,
"NSShadow" : YES,
"NSSlider" : YES,
"NSSliderCell" : YES,
"NSSound" : YES,
"NSSpeechRecognizer" : YES,
"NSSpeechSynthesizer" : YES,
"NSSpellChecker" : YES,
"NSSplitView" : YES,
"NSStatusBar" : YES,
"NSStatusItem" : YES,
"NSStepper" : YES,
"NSStepperCell" : YES,
"NSString Application Kit Additions" : YES,
"NSTableCellView" : YES,
"NSTableColumn" : YES,
"NSTableHeaderCell" : YES,
"NSTableHeaderView" : YES,
"NSTableView" : YES,
"NSTabView" : YES,
"NSTabViewItem" : YES,
"NSText" : YES,
"NSTextAttachment" : YES,
"NSTextAttachmentCell" : YES,
"NSTextBlock" : YES,
"NSTextContainer" : YES,
"NSTextField" : YES,
"NSTextFieldCell" : YES,
"NSTextInputContext" : YES,
"NSTextList" : YES,
"NSTextStorage" : YES,
"NSTextTab" : YES,
"NSTextTable" : YES,
"NSTextTableBlock" : YES,
"NSTextView" : YES,
"NSTokenField" : YES,
"NSTokenFieldCell" : YES,
"NSToolbar" : YES,
"NSToolbarItem" : YES,
"NSToolbarItemGroup" : YES,
"NSTouch" : YES,
"NSTrackingArea" : YES,
"NSTreeController" : YES,
"NSTreeNode" : YES,
"NSTypesetter" : YES,
"NSURL Additions" : YES,
"NSUserDefaultsController" : YES,
"NSView" : YES,
"NSViewAnimation" : YES,
"NSViewController" : YES,
"NSWindow" : YES,
"NSWindowController" : YES,
"NSWorkspace" : YES,
"NSPopover": YES,
"NSAppearance" : YES,
"NSVisualEffectView" : YES,
};
-59
View File
@@ -1,59 +0,0 @@
#!/usr/bin/env objj
require("narwhal").ensureEngine("rhino");
@import <Foundation/Foundation.j>
@import "../lib/cappuccino/objj-analysis-tools.j"
var FILE = require("file");
var stream = require("narwhal/term").stream
var parser = new (require("narwhal/args").Parser)();
parser.usage("INPUT_PROJECT");
parser.help("Checks a project for missing imports.");
parser.helpful();
function main(args)
{
var options = parser.parse(args);
if (options.args.length < 1) {
parser.printUsage(options);
return;
}
var rootPath = FILE.path(options.args[0]).join("").absolute();
var mainPath = rootPath.join("main.j");
var analyzer = new ObjectiveJRuntimeAnalyzer(rootPath);
analyzer.setIncludePaths([rootPath.join("Frameworks")]);
analyzer.setEnvironments(["Browser", "ObjJ"]);
analyzer.initializeGlobalRecorder();
analyzer.load(mainPath);
analyzer.finishLoading();
var mainExecutable = analyzer.executableForImport(mainPath);
var context = analyzer.traverseDependencies(mainExecutable, {
progressCallback : function(path) { stream.print("Analyzing \0purple(" + path + "\0)..."); }
});
for (var path in context.referencedFiles) {
var referencedFiles = context.referencedFiles[path];
for (var referenced in referencedFiles) {
if (!context.importedFiles[path] || !context.importedFiles[path][referenced]) {
stream.print("\0purple(" + rootPath.relative(path) + "\0) references but doesn't import \0purple(" + rootPath.relative(referenced) + "\0):")
Object.keys(referencedFiles[referenced]).forEach(function(token) {
stream.print(" \0cyan(" + token + "\0)");
});
}
}
}
}
-13
View File
@@ -1,13 +0,0 @@
#!/usr/bin/env node
ObjectiveJ = require("@objj/runtime");
//CAPPUCCINO = require("@objj/cappuccino");
var ojtest = require("@objj/ojtest");
// Inject path to the OJTest main Objective-J file into the arguments so it is the file to be executed.
var args = process.argv.slice(); // Copy
args.splice(1, 1, ojtest.pathToOJTest);
// Compile, load and run it
ObjectiveJ.run(args, {inlineMsgSendFunctions: true, sourceMap: true, sourceMapIncludeSource: true });
-411
View File
@@ -1,411 +0,0 @@
#!/usr/bin/env objj
require("narwhal").ensureEngine("rhino");
@import <Foundation/Foundation.j>
@import "../lib/cappuccino/objj-analysis-tools.j"
@import "../lib/cappuccino/cib-analysis-tools.j"
var FILE = require("file");
var OS = require("os");
var CACHEMANIFEST = require("objective-j/cache-manifest");
var stream = require("narwhal/term").stream;
var parser = new (require("narwhal/args").Parser)();
parser.usage("INPUT_PROJECT OUTPUT_PROJECT");
parser.help("Analyze and strip unused files from a Cappuccino project's .sj bundles.");
parser.option("-m", "--main", "main")
.def("main.j")
.set()
.help("The relative path (from INPUT_PROJECT) to the main file (default: 'main.j')");
parser.option("-F", "--framework", "frameworks")
.def(["Frameworks"])
.push()
.help("Add a frameworks directory, relative to INPUT_PROJECT (default: ['Frameworks'])");
parser.option("-f", "--force", "force")
.def(false)
.set(true)
.help("Force overwriting OUTPUT_PROJECT if it exists");
parser.option("--index", "index")
.def("index.html")
.set()
.help("The root HTML file to modify (default: index.html) (NOTE: currently only used by '--manifest' option)");
parser.option("-p", "--pngcrush", "png")
.def(false)
.set(true)
.help("Run pngcrush on all PNGs (pngcrush must be installed!)");
parser.option("--manifest", "manifest")
.set(true)
.help("Generate HTML5 cache manifest.");
parser.option("-v", "--verbose", "verbose")
.def(false)
.set(true)
.help("Verbose logging");
parser.helpful();
function main(args)
{
var options = parser.parse(args);
if (options.args.length < 2) {
parser.printUsage(options);
return;
}
CPLogRegister(CPLogPrint);
// HACK: ensure trailing slashes for "relative" to work correctly
var rootPath = FILE.path(options.args[0]).absolute().join("");
var outputPath = FILE.path(options.args[1]).absolute().join("");
if (outputPath.exists()) {
if (options.force) {
// FIXME: why doesn't this work?!
//outputPath.rmtree();
OS.system(["rm", "-rf", outputPath]);
} else {
stream.print("\0red(OUTPUT_PROJECT " + outputPath + " exists. Use -f to overwrite.\0)");
OS.exit(1);
}
}
press(rootPath, outputPath, options);
}
function press(rootPath, outputPath, options) {
stream.print("\0yellow("+Array(81).join("=")+"\0)");
stream.print("Application root: \0green(" + rootPath + "\0)");
stream.print("Output directory: \0green(" + outputPath + "\0)");
var outputFiles = {},
totalBytes = {executable:0, data:0, mhtml:0};
var environment = "Browser";
// analyze and gather files for each environment:
var returnedBytes = pressEnvironment(rootPath, outputFiles, environment, options);
for (var i in returnedBytes)
totalBytes[i] += returnedBytes[i];
// phase 4: copy everything and write out the new files
stream.print("\0red(PHASE 4:\0) copy to output \0green(" + rootPath + "\0) => \0green(" + outputPath + "\0)");
FILE.copyTree(rootPath, outputPath);
for (var path in outputFiles) {
var file = outputPath.join(rootPath.relative(path));
var parent = file.dirname();
if (!parent.exists()) {
CPLog.warn(parent + " doesn't exist, creating directories.");
parent.mkdirs();
}
if (typeof outputFiles[path] !== "string")
outputFiles[path] = outputFiles[path].join("");
stream.print((file.exists() ? "\0red(Overwriting:\0) " : "\0green(Writing:\0) ") + file);
FILE.write(file, outputFiles[path], { charset : "UTF-8" });
}
// phase 4a: update the info.plist to contain meta information about the app
stream.print("\0red(PHASE 4a:\0) Add application size data to Info.plist");
var outputInfoPlistPath = FILE.join(outputPath, "Info.plist"),
outputInfoPlistContents = FILE.read(outputInfoPlistPath, { charset:"UTF-8" }),
format = CFPropertyList.sniffedFormatOfString(outputInfoPlistContents),
outputInfoPlist = CFPropertyList.propertyListFromString(outputInfoPlistContents);
// read in the default theme name, and attempt to get its size
var themeName = outputInfoPlist.valueForKey("CPDefaultTheme") || "Aristo2",
themePath = nil;
if (themeName === "Aristo" || themeName === "Aristo2")
themePath = FILE.join(outputPath, options.frameworks, "AppKit", "Resources", themeName+".blend");
else
themePath = FILE.join(outputPath, "Resources", themeName + ".blend");
if (FILE.exists(themePath))
{
var themeEnvPath = FILE.join(themePath, environment + ".environment"),
themeExecutablePath = FILE.join(themeEnvPath, themeName+".blend.sj"),
themeDataPath = FILE.join(themeEnvPath, "dataURLs.txt"),
themeMHTMLPath = FILE.join(themeEnvPath, "MHTMLPaths.txt");
totalBytes.executable += FILE.read(themeExecutablePath, {charset : "UTF-8"}).length;
totalBytes.data += FILE.read(themeDataPath, {charset : "UTF-8"}).length;
totalBytes.mhtml += FILE.read(themeMHTMLPath, {charset : "UTF-8"}).length;
}
outputInfoPlist.setValueForKey("CPApplicationSize", [CPDictionary dictionaryWithJSObject:totalBytes]);
FILE.write(outputInfoPlistPath, CFPropertyList.stringFromPropertyList(outputInfoPlist, format), { charset:"UTF-8" });
// strip known unnecessary files
// outputPath.glob("**/Frameworks/Debug").forEach(function(debugFramework) {
// outputPath.join(debugFramework).rmtree();
// });
// outputPath.join("index-debug.html").remove();
if (options.png) {
pngcrushDirectory(outputPath);
}
if (options.manifest) {
CACHEMANIFEST.generateManifest(outputPath, {
index : outputPath.join(options.index)
});
}
}
function pressEnvironment(rootPath, outputFiles, environment, options) {
var mainPath = String(rootPath.join(options.main));
var frameworks = options.frameworks.map(function(framework) { return rootPath.join(framework); });
stream.print("\0yellow("+Array(81).join("=")+"\0)");
stream.print("Main file: \0green(" + mainPath + "\0)");
stream.print("Frameworks: \0green(" + frameworks + "\0)");
stream.print("Environment: \0green(" + environment + "\0)");
var analyzer = new ObjectiveJRuntimeAnalyzer(rootPath);
var _OBJJ = analyzer.require("objective-j");
analyzer.setIncludePaths(frameworks);
analyzer.setEnvironments([environment, "ObjJ"]);
// build list of cibs to inspect for dependent classes
// FIXME: what's the best way to determine which cibs to look in?
var cibs = FILE.glob(rootPath.join("**", "*.cib")).filter(function(path) { return !(/Frameworks/).test(path); });
// phase 1: get global defines
stream.print("\0red(PHASE 1:\0) Loading application...");
analyzer.initializeGlobalRecorder();
analyzer.load(mainPath);
analyzer.finishLoading();
// coalesce the results
var dependencies = analyzer.mapGlobalsToFiles();
// log identifer => files defining
stream.print("Global defines:");
Object.keys(dependencies).sort().forEach(function(identifier) {
stream.print("\0blue(" + identifier + "\0) => \0cyan(" + dependencies[identifier].map(rootPath.relative.bind(rootPath)) + "\0)");
});
// phase 2: walk the dependency tree (both imports and references) to determine exactly which files need to be included
stream.print("\0red(PHASE 2:\0) Traverse dependency graph...");
var requiredFiles = {};
requiredFiles[mainPath] = true;
var context = {
ignoreFrameworkImports : true, // ignores "XXX/XXX.j" imports
importCallback: function(importing, imported) { requiredFiles[imported] = true; },
referenceCallback: function(referencing, referenced) { requiredFiles[referenced] = true; },
progressCallback: function(relPath) { stream.print("Processing \0cyan("+relPath+"\0)"); },
ignoreFrameworkImportsCallback : function(relPath) { stream.print("\0yellow(Ignoring imports in "+relPath+"\0)"); }
}
mainExecutable = analyzer.executableForImport(mainPath);
// check the code
analyzer.traverseDependencies(mainExecutable, context);
// check the cibs
var globalsToFiles = analyzer.mapGlobalsToFiles();
cibs.forEach(function(cibPath) {
var cibClasses = findCibClassDependencies(cibPath);
stream.print("Cib: \0green("+rootPath.relative(cibPath)+"\0) => \0cyan("+cibClasses+"\0)");
var referencedFiles = {};
markFilesReferencedByTokens(cibClasses, globalsToFiles, referencedFiles);
analyzer.checkReferenced(context, null, referencedFiles);
});
var included = 0, total = 0;
var includedBytes = 0, totalBytes = 0, dataBytes = 0, mhtmlBytes = 0;
_OBJJ.FileExecutable.allFileExecutables().forEach(function(aFileExecutable) {
var path = aFileExecutable.path();
// mark all ".keytheme"s as required
if (/\.keyedtheme$/.test(path))
requiredFiles[path] = true;
if (requiredFiles[path]) {
stream.print("Included: \0green(" + rootPath.relative(path) + "\0)");
included++;
includedBytes += aFileExecutable.code().length
}
else {
stream.print("Excluded: \0red(" + rootPath.relative(path) + "\0)");
}
total++;
totalBytes += aFileExecutable.code().length
}, this);
stream.print(_OBJJ.sprintf(
"Saved \0green(%f%%\0) (\0blue(%s\0)); Total required files: \0magenta(%d\0) (\0blue(%s\0)) of \0magenta(%d\0) (\0blue(%s\0));",
Math.round(((includedBytes - totalBytes) / totalBytes) * -100),
bytesToString(totalBytes - includedBytes),
included, bytesToString(includedBytes),
total, bytesToString(totalBytes)
));
// phase 3b: rebuild .sj files with correct imports, copy .j files
stream.print("\0red(PHASE 3b:\0) Rebuild .sj files");
var processedBundles = {};
for (var path in requiredFiles)
{
var executable = analyzer.executableForImport(path),
bundle = analyzer.context.global.CFBundle.bundleContainingURL(executable.path()),
relativePath = FILE.relative(FILE.join(bundle.path(), ""), executable.path());
if (executable.path() !== path)
CPLog.warn("Sanity check failed (file path): " + executable.path() + " vs. " + path);
if (bundle && bundle.infoDictionary())
{
var executablePath = bundle.executablePath();
if (executablePath)
{
if (context.ignoredImports[path])
{
stream.print("Stripping extra imports from \0blue(" + path + "\0)");
var code = executable.code();
var dependencies = executable.fileDependencies(),
fileExecutableSearcher = executable.fileExecutableSearcher();
for (var i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
var dependencyExecutable = nil;
fileExecutableSearcher(dependency.URL(), dependency.isLocal(), function(aFileExecutable)
{
dependencyExecutable = aFileExecutable;
});
var dependencyPath = dependencyExecutable.path();
if (!requiredFiles[dependencyPath]) {
stream.print(" -> \0red(" + dependencyPath + "\0)");
// build up a regex to match objj_executeFile with optional whitespace
var regex = new RegExp([
RegExp.escape("objj_executeFile"),
RegExp.escape("("),
"[\"']"+RegExp.escape(dependency.path())+"[\"']",
RegExp.escape(","),
dependency.isLocal() ? "(true|YES)" : "(false|NO)",
RegExp.escape(")")
].join("\\s*"), "g");
// replace instances of "objj_executeFile()" with "(undefined)"
newCode = code.replace(regex, "/* $& */ (undefined)");
if (newCode === code)
stream.print("\0yellow(Warning:\0) Unable to strip import for \0cyan(" + dependency.path() + "\0) (" + dependency.isLocal() + ")");
code = newCode;
// remove from dependencies list (decrement i since we're removing an item)
dependencies.splice(i--, 1);
}
}
if (code !== executable.code())
executable.setCode(code);
}
if (!outputFiles[executablePath])
{
outputFiles[executablePath] = [];
outputFiles[executablePath].push("@STATIC;1.0;");
}
var fileContents = executable.toMarkedString();
outputFiles[executablePath].push("p;" + relativePath.length + ";" + relativePath);
outputFiles[executablePath].push("t;" + fileContents.length + ";" + fileContents);
stream.print("Adding \0green(" + rootPath.relative(path) + "\0) to \0cyan(" + rootPath.relative(executablePath) + "\0)");
}
else
{
stream.print("Passing .j through: \0green(" + rootPath.relative(path) + "\0)");
}
if (!processedBundles[bundle.path()] && bundle.hasSpritedImages())
{
processedBundles[bundle.path()] = bundle;
stream.print("Sizing bundle's sprited resources \0green(" + bundle.path() + "\0)");
var bundlePath = FILE.join(bundle.path(), environment+".environment"),
bundleDataPath = FILE.join(bundlePath, "dataURLs.txt"),
bundleMHTMLPath = FILE.join(bundlePath, "MHTMLPaths.txt");
dataBytes += FILE.read(bundleDataPath, {charset : "UTF-8"}).length;
mhtmlBytes += FILE.read(bundleMHTMLPath, {charset : "UTF-8"}).length;
}
}
else
CPLog.warn("No bundle (or info dictionary for) " + rootPath.relative(path));
}
return {executable:includedBytes, data:dataBytes, mhtml:mhtmlBytes};
}
function pngcrushDirectory(directory)
{
var directoryPath = FILE.path(directory),
pngs = directoryPath.glob("**/*.png");
system.stderr.print("Running pngcrush on " + pngs.length + " pngs:");
pngs.forEach(function(dstPath)
{
var tmpPath = FILE.path(dstPath+".tmp");
try
{
var p = OS.popen(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ dstPath, tmpPath]);
if (p.wait())
{
CPLog.warn("pngcrush failed. Ensure it's installed and on your PATH.");
}
else
{
FILE.move(tmpPath, dstPath);
system.stderr.write(".").flush();
}
}
finally
{
p.stdin.close();
p.stdout.close();
p.stderr.close();
}
});
system.stderr.print("");
}
function bytesToString(bytes) {
var n = 0;
while (bytes > 1024) {
bytes /= 1024;
n++;
}
return Math.round(bytes*100)/100 + " " + ["", "K", "M"][n] + "B";
}