var FILE = require("file"), OS = require("os"), ObjectiveJ = require("objective-j"); require("objective-j/rhino/regexp-rhino-patch"); ObjectiveJ.Preprocessor.Flags.Preprocess = 1 << 10; ObjectiveJ.Preprocessor.Flags.Compress = 1 << 11; ObjectiveJ.Preprocessor.Flags.CheckSyntax = 1 << 12; 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 compressor = require(compressors[name].id); var result = compressor.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) { var shouldObjjPreprocess = objjcFlags & ObjectiveJ.Preprocessor.Flags.Preprocess, shouldCheckSyntax = objjcFlags & ObjectiveJ.Preprocessor.Flags.CheckSyntax, shouldCompress = objjcFlags & ObjectiveJ.Preprocessor.Flags.Compress, fileContents = ""; if (OS.popen("which gcc").stdout.read().length === 0) fileContents = FILE.read(aFilePath, { charset:"UTF-8" }); else { // GCC preprocess the file. 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; } if (!shouldObjjPreprocess) return fileContents; // Preprocess contents into fragments. // FIXME: should calculate relative path, etc. try { var executable = ObjectiveJ.preprocess(fileContents, FILE.basename(aFilePath), objjcFlags); } catch (anException) {print(anException); var lines = fileContents.split("\n"), PAD = 3, lineNumber = anException.lineNumber || anException.line, errorInfo = "Syntax error in " + aFilePath + " on preprocessed line number " + lineNumber + "\n\n" + "\t" + lines.slice(Math.max(0, lineNumber - 1 - PAD), lineNumber + PAD).join("\n\t"); print(errorInfo); throw errorInfo; } if (shouldCompress) { var 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 executable.toMarkedString(); } function resolveFlags(args) { var filePaths = [], outputFilePaths = [], index = 0, count = args.length, gccFlags = [], objjcFlags = ObjectiveJ.Preprocessor.Flags.Preprocess | ObjectiveJ.Preprocessor.Flags.CheckSyntax; 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("-E") === 0) objjcFlags &= ~ObjectiveJ.Preprocessor.Flags.Preprocess; else if (argument.indexOf("-S") === 0) objjcFlags &= ~ObjectiveJ.Preprocessor.Flags.CheckSyntax; else if (argument.indexOf("-g") === 0) objjcFlags |= ObjectiveJ.Preprocessor.Flags.IncludeDebugSymbols; else if (argument.indexOf("-O") === 0) objjcFlags |= ObjectiveJ.Preprocessor.Flags.Compress; 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) { // TODO: args parser args.shift(); var resolved = resolveFlags(args), outputFilePaths = resolved.outputFilePaths, objjcFlags = resolved.objjcFlags, gccFlags = resolved.gccFlags; resolved.filePaths.forEach(function(filePath, index) { print("Statically Compiling " + filePath); FILE.write(outputFilePaths[index], compileWithResolvedFlags(filePath, objjcFlags, gccFlags), { charset: "UTF-8" }); }); } if (require.main == module.id) exports.main(system.args);