mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 21:17:03 +00:00
A Cappuccino project has always needed to have a flat file structure to be able to compile from source in the web browser. A new dictionary can be added to the project's Info.plist with the key 'CPFileTranslationDictionary'. It should contain all the path translations in the project to allow the Browser to find out where in the file structure the source files are. Check in the AppKit framework for an example. Also, a include list can be added in the Info.plist for the key 'CPCompileIncludeFileArray'. The file in the list will be included by a '#include' statement for each file that is compiled in this project A last new feature is to add macros in the 'OBJJ_COMPILER_FLAGS' global variable. It is usually declared in the index.html file. It is done with the format '-Dmacroname[=macrodefinition]'. An updated Info.plist file in the AppKit framework will allow the AppKit to compile from the source in the web browser. The following things is needed to get this to work: 1. Replace the AppKit folder in your project's Frameworks folder with a copy of the AppKit folder from the Cappuccino project. 2. Add a copy of the built Aristo2.blend folder to the AppKit's Resource folder. 3. Add "-DPLATFORM_DOM", "-DPLATFORM_BROWSER" and optionally "-DDEBUG" to the global variable 'OBJJ_COMPILER_FLAGS' in the project's index.html file. The app load time will increase (about 3 seconds on a 2018 MacBook Pro) as the AppKit framework is compiled from source. The Foundation framework is not yet tested but it might be working by adding info about the file structure in the Info.plist file.
318 lines
11 KiB
JavaScript
318 lines
11 KiB
JavaScript
|
|
var FILE = require("file"),
|
|
OS = require("os"),
|
|
ObjectiveJ = require("objective-j"),
|
|
JAKE = require("jake");
|
|
|
|
require("objective-j/rhino/regexp-rhino-patch");
|
|
|
|
var compressors = {
|
|
ss : { id : "minify/shrinksafe" }
|
|
//,yui : { id : "minify/yuicompressor" }
|
|
// ,cc : { id : "minify/closure-compiler" }
|
|
};
|
|
|
|
var compressorStats = {};
|
|
function compressor(code)
|
|
{
|
|
var winner, winnerName;
|
|
compressorStats['original'] = (compressorStats['original'] || 0) + code.length;
|
|
for (var name in compressors)
|
|
{
|
|
var aCompressor = require(compressors[name].id),
|
|
result = aCompressor.compress(code, { charset : "UTF-8", useServer : true });
|
|
compressorStats[name] = (compressorStats[name] || 0) + result.length;
|
|
if (!winner || result < winner.length) {
|
|
winner = result;
|
|
winnerName = name;
|
|
}
|
|
}
|
|
// print("winner="+winnerName+" compressorStats="+JSON.stringify(compressorStats));
|
|
return winner;
|
|
}
|
|
|
|
function compileWithResolvedFlags(aFilePath, objjcFlags, gccFlags, asPlainJavascript)
|
|
{
|
|
var shouldObjjPreprocess = true,
|
|
shouldCompress = objjcFlags.compress,
|
|
fileContents = "",
|
|
executable,
|
|
code;
|
|
|
|
if (!shouldObjjPreprocess)
|
|
{
|
|
try
|
|
{
|
|
var p = OS.popen("which gcc");
|
|
|
|
if (p.stdout.read().length === 0)
|
|
{
|
|
fileContents = FILE.read(aFilePath, { charset:"UTF-8" });
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
var gcc = OS.popen("gcc -E -x c -P " + (gccFlags ? gccFlags.join(" ") : "") + " " + OS.enquote(aFilePath), { charset:"UTF-8" }),
|
|
chunk = "";
|
|
|
|
while (chunk = gcc.stdout.read())
|
|
fileContents += chunk;
|
|
}
|
|
finally
|
|
{
|
|
gcc.stdin.close();
|
|
gcc.stdout.close();
|
|
gcc.stderr.close();
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
p.stdin.close();
|
|
p.stdout.close();
|
|
p.stderr.close();
|
|
}
|
|
|
|
return fileContents;
|
|
}
|
|
|
|
// Preprocess contents into fragments.
|
|
// FIXME: should calculate relative path, etc.
|
|
try
|
|
{
|
|
var sources = new JAKE.FileList("**/*.j"),
|
|
translateFilenameToPath = {},
|
|
otherwayTranslateFilenameToPath = {};
|
|
|
|
// Create a filename to filename path dictionary. (For example: CPArray.j -> CPArray/CPArray.j)
|
|
sources.forEach(function(/*String*/ aFilename)
|
|
{
|
|
translateFilenameToPath[FILE.basename(aFilename)] = aFilename;
|
|
otherwayTranslateFilenameToPath[aFilename] = FILE.basename(aFilename);
|
|
}, this);
|
|
|
|
var translatedFilename = translateFilenameToPath[aFilePath] ? translateFilenameToPath[aFilePath] : aFilePath,
|
|
otherwayTranslatedFilename = otherwayTranslateFilenameToPath[aFilePath] ? otherwayTranslateFilenameToPath[aFilePath] : aFilePath,
|
|
theTranslatedFilename = otherwayTranslatedFilename ? otherwayTranslatedFilename : translatedFilename,
|
|
absolutePath = FILE.absolute(theTranslatedFilename),
|
|
basePath = absolutePath.substring(0, absolutePath.length - theTranslatedFilename.length);
|
|
|
|
// This ugly fix to make Cappuccino compile with rhino can be removed when the compiler is not loading classes into the runtime
|
|
var rhinoUglyFix = false;
|
|
if (system.engine === "rhino")
|
|
{
|
|
if (typeof document == "undefined") {
|
|
document = { createElement: function(x) { return { innerText: "", style: {}}}};
|
|
rhinoUglyFix = true;
|
|
}
|
|
if (typeof navigator == "undefined")
|
|
{
|
|
navigator = { "userAgent": "fakenavigator" };
|
|
rhinoUglyFix = true;
|
|
}
|
|
}
|
|
|
|
ObjectiveJ.StaticResource.setCurrentCompilerFlags(objjcFlags);
|
|
ObjectiveJ.make_narwhal_factory(absolutePath, basePath, translateFilenameToPath)(require, {}, module, system, print);
|
|
|
|
executable = new ObjectiveJ.FileExecutable(FILE.basename(aFilePath));
|
|
|
|
if (rhinoUglyFix)
|
|
delete document;
|
|
}
|
|
catch (anException)
|
|
{
|
|
print(anException);
|
|
return;
|
|
}
|
|
|
|
if (shouldCompress)
|
|
{
|
|
code = executable.code();
|
|
code = compressor("function(){" + code + "}");
|
|
// more robust function wrapper stripping
|
|
code = code.replace(/^\s*function\s*\(\s*\)\s*{|}\s*;?\s*$/g, "");
|
|
executable.setCode(code);
|
|
}
|
|
|
|
return asPlainJavascript ? executable.code() : executable.toMarkedString();
|
|
}
|
|
|
|
function resolveFlags(args)
|
|
{
|
|
var filePaths = [],
|
|
outputFilePaths = [],
|
|
|
|
index = 0,
|
|
count = args.length,
|
|
|
|
gccFlags = [],
|
|
objjcFlags = {};
|
|
|
|
for (; index < count; ++index)
|
|
{
|
|
var argument = args[index];
|
|
|
|
if (argument === "-o")
|
|
{
|
|
if (++index < count)
|
|
outputFilePaths.push(args[index]);
|
|
}
|
|
|
|
else if (argument.indexOf("-D") === 0)
|
|
gccFlags.push(argument);
|
|
|
|
else if (argument.indexOf("-U") === 0)
|
|
gccFlags.push(argument);
|
|
|
|
else if (argument === "--include")
|
|
{
|
|
if (++index < count)
|
|
{
|
|
gccFlags.push(argument);
|
|
gccFlags.push(args[index]);
|
|
}
|
|
}
|
|
|
|
else if (argument.indexOf("-T") === 0)
|
|
{
|
|
objjcFlags.includeIvarTypeSignatures = false;
|
|
objjcFlags.includeMethodArgumentTypeSignatures = false;
|
|
}
|
|
else if (argument.indexOf("-g") === 0)
|
|
objjcFlags.includeMethodFunctionNames = true;
|
|
|
|
else if (argument.indexOf("-O") === 0) {
|
|
objjcFlags.compress = true;
|
|
// FIXME: currently we are sending in '-O2' when we want InlineMsgSend. Here we only check if we it is '-O...'
|
|
if (argument.length > 2)
|
|
objjcFlags.inlineMsgSendFunctions = true;
|
|
}
|
|
|
|
else if (argument.indexOf("-G") === 0)
|
|
objjcFlags.generate = true;
|
|
|
|
else if (argument.indexOf("--inline-msg-send") === 0)
|
|
{
|
|
// This option is if you only want to inline message send functions and not compress
|
|
objjcFlags.inlineMsgSendFunctions = true;
|
|
}
|
|
else
|
|
filePaths.push(argument);
|
|
}
|
|
|
|
return { filePaths:filePaths, outputFilePaths:outputFilePaths, objjcFlags:objjcFlags, gccFlags:gccFlags };
|
|
}
|
|
|
|
exports.compile = function(aFilePath, flags)
|
|
{
|
|
if (flags.split)
|
|
flags = flags.split(/\s+/);
|
|
|
|
var resolvedFlags = resolveFlags(flags);
|
|
|
|
return compileWithResolvedFlags(aFilePath, resolvedFlags.objjcFlags, resolvedFlags.gccFlags);
|
|
};
|
|
|
|
exports.main = function(args)
|
|
{
|
|
var shouldPrintOutput = false,
|
|
asPlainJavascript = false,
|
|
objjcFlags = {};
|
|
|
|
var argv = args.slice(1);
|
|
|
|
while(argv.length)
|
|
{
|
|
if (argv[0] === '--')
|
|
{
|
|
argv.shift();
|
|
break;
|
|
}
|
|
|
|
if (argv[0] === "-p" || argv[0] === "--print")
|
|
{
|
|
shouldPrintOutput = YES;
|
|
argv.shift();
|
|
continue;
|
|
}
|
|
|
|
if (argv[0] === "--unmarked")
|
|
{
|
|
asPlainJavascript = true;
|
|
argv.shift();
|
|
continue;
|
|
}
|
|
|
|
if (argv[0] === "-T" || argv[0] === "--includeTypeSignatures")
|
|
{
|
|
objjcFlags.includeIvarTypeSignatures = true;
|
|
objjcFlags.includeMethodArgumentTypeSignatures = true;
|
|
argv.shift();
|
|
continue;
|
|
}
|
|
|
|
if (argv[0] === "--help" || argv[0].substr(0, 1) == '-')
|
|
{
|
|
print("Usage (objjc 2.0): " + args[0] + " [options] [--] file...");
|
|
print(" -p, --print print the output directly to stdout");
|
|
print(" --unmarked don't tag the output with @STATIC header");
|
|
print("");
|
|
print(" -T, --dont-include-type-signatures include type signatures in the compiled output");
|
|
print(" -g, --include-debug-symbols include debug symbols in the compiled output");
|
|
print(" -T, --include-type-signatures include type signatures in the compiled output");
|
|
print(" -O, --compress compress the compiled output");
|
|
print(" -O2, --inline-msg-send inline objj_msgSend function in the compiled output");
|
|
print(" -Wno-unused-but-set-variable turn off warning when a local variable is never read");
|
|
print(" -Wno-shadow-ivar turn off warning when a local variable is shadowing an instance variable for the class");
|
|
print(" -Wno-create-global-inside-function-or-method turn off warning when creating a global variable inside a function or method");
|
|
print(" -Wno-unknown-class-or-global. turn off warning when a class or global variable is not known");
|
|
print(" -Wno-unknown-ivar-type turn off warning when the type for an instance variable is not known");
|
|
print(" To turn on a warning flag remove the 'no-' prefix. Example: -Wunused-but-set-variable");
|
|
print("");
|
|
print(" --help print this help");
|
|
return;
|
|
}
|
|
|
|
// Current argument doesn't begin with - so it's not an argument but the
|
|
// first filename.
|
|
// TODO Full GNU getopt parsing which doesn't stop on the first non-argument.
|
|
break;
|
|
}
|
|
|
|
var resolved = resolveFlags(argv),
|
|
outputFilePaths = resolved.outputFilePaths,
|
|
gccFlags = resolved.gccFlags,
|
|
resolvedObjjcFlags = resolved.objjcFlags;
|
|
|
|
// Merge resolved keys into objjcFlags
|
|
for (var key in resolvedObjjcFlags)
|
|
{
|
|
if (resolvedObjjcFlags.hasOwnProperty(key))
|
|
{
|
|
if (resolvedObjjcFlags[key])
|
|
objjcFlags[key] = resolvedObjjcFlags[key];
|
|
}
|
|
}
|
|
|
|
resolved.filePaths.forEach(function(filePath, index)
|
|
{
|
|
if (!shouldPrintOutput)
|
|
print("Statically Compiling " + filePath);
|
|
var output = compileWithResolvedFlags(filePath, objjcFlags, gccFlags, asPlainJavascript);
|
|
|
|
// Only write the output if the compilation didn't fail.
|
|
if (output !== undefined)
|
|
{
|
|
if (shouldPrintOutput)
|
|
print(output);
|
|
else
|
|
FILE.write(outputFilePaths[index], output, { charset: "UTF-8" });
|
|
}
|
|
});
|
|
};
|
|
|
|
if (require.main == module.id)
|
|
exports.main(system.args);
|