PageRenderTime 48ms CodeModel.GetById 3ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/common.jake

http://github.com/cacaodev/cappuccino
Unknown | 670 lines | 538 code | 132 blank | 0 comment | 0 complexity | db41352d671870d60ddbcc583505e946 MD5 | raw file
  1/*
  2 * command.jake
  3 * toolchain
  4 *
  5 * Copyright 2012 The Cappuccino Foundation
  6 *
  7 * This library is free software; you can redistribute it and/or
  8 * modify it under the terms of the GNU Lesser General Public
  9 * License as published by the Free Software Foundation; either
 10 * version 2.1 of the License, or (at your option) any later version.
 11 *
 12 * This library is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15 * Lesser General Public License for more details.
 16 *
 17 * You should have received a copy of the GNU Lesser General Public
 18 * License along with this library; if not, write to the Free Software
 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 20 */
 21
 22var SYSTEM = require("system"),
 23    FILE = require("file"),
 24    OS = require("os"),
 25    UTIL = require("narwhal/util"),
 26    stream = require("narwhal/term").stream,
 27
 28    requiresSudo = false;
 29
 30SYSTEM.args.slice(1).forEach(function(arg)
 31{
 32    if (arg === "sudo-install")
 33        requiresSudo = true;
 34});
 35
 36function ensurePackageUpToDate(packageName, requiredVersion, options)
 37{
 38    return;
 39
 40    options = options || {};
 41
 42    var packageInfo = require("narwhal/packages").catalog[packageName];
 43    if (!packageInfo)
 44    {
 45        if (options.optional)
 46            return;
 47
 48        print("You are missing package \"" + packageName + "\", version " + requiredVersion + " or later. Please install using \"tusk install "+ packageName +"\" and re-run jake");
 49        OS.exit(1);
 50    }
 51
 52    var version = packageInfo.version;
 53    if (typeof version === "string")
 54        version = version.split(".");
 55
 56    if (typeof requiredVersion === "string")
 57        requiredVersion = requiredVersion.split(".");
 58
 59    if (version && UTIL.compare(version, requiredVersion) !== -1)
 60        return;
 61
 62    print("Your copy of " + packageName + " is out of date (" + (version || ["0"]).join(".") + " installed, " + requiredVersion.join(".") + " required).");
 63
 64    if (!options.noupdate)
 65    {
 66        print("Update? Existing package will be overwritten. yes or no:");
 67
 68        if (!SYSTEM.env["CAPP_AUTO_UPGRADE"] && system.stdin.readLine() !== "yes\n")
 69        {
 70            print("Jake aborted.");
 71            OS.exit(1);
 72        }
 73
 74        if (requiresSudo)
 75        {
 76            if (OS.system(["sudo", "tusk", "install", "--force", packageName]))
 77            {
 78                // Attempt a hackish work-around for sudo compiled with the --with-secure-path option
 79                if (OS.system("sudo bash -c 'source " + getShellConfigFile() + "; tusk install --force "+ packageName))
 80                    OS.exit(1); //rake abort if ($? != 0)
 81            }
 82        }
 83        else
 84            OS.system(["tusk", "install", "--force", packageName]);
 85    }
 86
 87    if (options.after)
 88    {
 89        options.after(packageInfo.directory);
 90    }
 91
 92    if (options.message)
 93    {
 94        print(options.message);
 95        OS.exit(1);
 96    }
 97}
 98
 99// This is disabled because tusk causes a lot of problems, and no packages will
100// never be updated anyway
101// // UPDATE THESE TO PICK UP CORRESPONDING CHANGES IN DEPENDENCIES
102// ensurePackageUpToDate("jake",           "0.3");
103// ensurePackageUpToDate("browserjs",      "0.1.1");
104// ensurePackageUpToDate("shrinksafe",     "0.2");
105// ensurePackageUpToDate("narwhal",        "0.3.1", {
106//     noupdate : true,
107//     message : "Update Narwhal by re-running bootstrap.sh, or pulling the latest from git (see: http://github.com/280north/narwhal)."
108// });
109// ensurePackageUpToDate("narwhal-jsc",    "0.3", {
110//     optional : true,
111//     after : function(dir) {
112//         if (OS.system("cd " + OS.enquote(dir) + " && make webkit")) {
113//             print("Problem building narwhal-jsc.");
114//             OS.exit(1);
115//         }
116//     }
117// });
118
119var JAKE = require("jake");
120
121// Set up development environment variables.
122
123// record the initial SYSTEM.env so we know which need to be serialized later
124var envInitial = Object.freeze(UTIL.copy(SYSTEM.env));
125
126SYSTEM.env["BUILD_PATH"] = FILE.absolute(
127    SYSTEM.env["BUILD_PATH"] ||
128    SYSTEM.env["CAPP_BUILD"] || // Global Cappuccino build directory.
129    SYSTEM.env["STEAM_BUILD"] || // Maintain backwards compatibility with steam.
130    FILE.join(FILE.dirname(module.path), "Build") // Just build here.
131);
132
133if (!SYSTEM.env["CAPP_BUILD"] && SYSTEM.env["STEAM_BUILD"])
134    system.stderr.print("STEAM_BUILD environment variable is deprecated; Please use CAPP_BUILD instead.");
135
136if (!SYSTEM.env["CONFIG"])
137    SYSTEM.env["CONFIG"] = "Release";
138
139global.ENV  = SYSTEM.env;
140global.ARGV = SYSTEM.args;
141global.FILE = FILE;
142global.OS   = OS;
143
144global.task = JAKE.task;
145global.directory = JAKE.directory;
146global.file = JAKE.file;
147global.filedir = JAKE.filedir;
148global.FileList = JAKE.FileList;
149
150global.$CONFIGURATION                   = SYSTEM.env['CONFIG'];
151global.$BUILD_DIR                       = SYSTEM.env['BUILD_PATH'];
152global.$BUILD_CONFIGURATION_DIR         = FILE.join($BUILD_DIR, $CONFIGURATION);
153
154global.$BUILD_CJS_OBJECTIVE_J           = FILE.join($BUILD_CONFIGURATION_DIR, "CommonJS", "objective-j");
155
156global.$BUILD_CJS_CAPPUCCINO            = FILE.join($BUILD_CONFIGURATION_DIR, "CommonJS", "cappuccino");
157global.$BUILD_CJS_CAPPUCCINO_BIN        = FILE.join($BUILD_CJS_CAPPUCCINO, "bin");
158global.$BUILD_CJS_CAPPUCCINO_LIB        = FILE.join($BUILD_CJS_CAPPUCCINO, "lib");
159global.$BUILD_CJS_CAPPUCCINO_FRAMEWORKS = FILE.join($BUILD_CJS_CAPPUCCINO, "Frameworks");
160
161global.CLEAN = require("jake/clean").CLEAN;
162global.CLOBBER = require("jake/clean").CLOBBER;
163global.CLEAN.include(FILE.join(global.$BUILD_DIR, "*.build"));
164global.CLOBBER.include(global.$BUILD_DIR);
165
166global.$HOME_DIR        = FILE.absolute(FILE.dirname(module.path));
167global.$LICENSE_FILE    = FILE.absolute(FILE.join(FILE.dirname(module.path), 'LICENSE'));
168
169global.FIXME_fileDependency = function(destinationPath, sourcePath)
170{
171    file(destinationPath, [sourcePath], function()
172    {
173        FILE.touch(destinationPath);
174    });
175};
176
177// logic to determine which packages should be loaded but are not.
178// used in serializedENV()
179function additionalPackages()
180{
181    var builtObjectiveJPackage = FILE.path($BUILD_CONFIGURATION_DIR).join("CommonJS", "objective-j", "");
182    var builtCappuccinoPackage = FILE.path($BUILD_CONFIGURATION_DIR).join("CommonJS", "cappuccino", "");
183
184    var packages = [];
185
186    // load built objective-j if exists, otherwise unbuilt
187    if (builtObjectiveJPackage.join("package.json").exists()) {
188        if (!packageInCatalog(builtObjectiveJPackage))
189            packages.push(builtObjectiveJPackage);
190    }
191
192    // load built cappuccino if it exists
193    if (builtCappuccinoPackage.join("package.json").exists()) {
194        if (!packageInCatalog(builtCappuccinoPackage))
195            packages.push(builtCappuccinoPackage);
196    }
197
198    return packages;
199}
200
201// checks to see if a path is in the package catalog
202function packageInCatalog(path)
203{
204    var catalog = require("narwhal/packages").catalog;
205    for (var name in catalog)
206        if (String(catalog[name].directory) === String(path))
207            return true;
208    return false;
209}
210
211serializedENV = function()
212{
213    var envNew = {};
214
215    // add changed keys to the new ENV
216    Object.keys(SYSTEM.env).forEach(function(key)
217    {
218        if (SYSTEM.env[key] !== envInitial[key])
219            envNew[key] = SYSTEM.env[key];
220    });
221
222    // pseudo-HACK: add NARWHALOPT with packages we should ensure are loaded
223    var packages = additionalPackages();
224
225    if (packages.length)
226    {
227        envNew["NARWHALOPT"] = packages.map(function(p) { return "-p " + OS.enquote(p); }).join(" ");
228        envNew["PATH"] = packages.map(function(p) { return FILE.join(p, "bin"); }).concat(SYSTEM.env["PATH"]).join(":");
229    }
230
231    return Object.keys(envNew).map(function(key)
232    {
233        return key + "=" + OS.enquote(envNew[key]);
234    }).join(" ");
235};
236
237function getShellConfigFile()
238{
239    var homeDir = SYSTEM.env["HOME"] + "/";
240    // use order outlined by http://hayne.net/MacDev/Notes/unixFAQ.html#shellStartup
241    var possibilities = [homeDir + ".bash_profile",
242                         homeDir + ".bash_login",
243                         homeDir + ".profile",
244                         homeDir + ".bashrc"];
245
246    for (var i = 0; i < possibilities.length; i++)
247    {
248        if (FILE.exists(possibilities[i]))
249            return possibilities[i];
250    }
251}
252
253function reforkWithPackages()
254{
255    if (additionalPackages().length > 0)
256    {
257        var cmd = serializedENV() + " " + system.args.map(OS.enquote).join(" ");
258        //print("REFORKING: " + cmd);
259        OS.exit(OS.system(cmd));
260    }
261}
262
263reforkWithPackages();
264
265function handleSetupEnvironmentError(e)
266{
267    if (String(e).indexOf("require error") == -1)
268    {
269        print("setupEnvironment warning: " + e);
270        //throw e;
271    }
272}
273
274function setupEnvironment()
275{
276    try
277    {
278        require("objective-j").OBJJ_INCLUDE_PATHS.push(FILE.join($BUILD_CONFIGURATION_DIR, "CommonJS", "cappuccino", "Frameworks"));
279    }
280    catch (e)
281    {
282        handleSetupEnvironmentError(e);
283    }
284}
285
286setupEnvironment();
287
288global.rm_rf = function(/*String*/ aFilename)
289{
290    try { FILE.rmtree(aFilename); }
291    catch (anException) { }
292};
293
294global.cp_r = function(/*String*/ from, /*String*/ to)
295{
296    if (FILE.exists(to))
297        rm_rf(to);
298
299    if (FILE.isDirectory(from))
300        FILE.copyTree(from, to);
301    else
302    {
303        try
304        {
305            FILE.copy(from, to);
306        }
307        catch (e)
308        {
309            print(e + FILE.exists(from) + " " + FILE.exists(FILE.dirname(to)));
310        }
311    }
312};
313
314global.cp = function(/*String*/ from, /*String*/ to)
315{
316    FILE.copy(from, to);
317//    FILE.chmod(to, FILE.mod(from));
318};
319
320global.mv = function(/*String*/ from, /*String*/ to)
321{
322    FILE.move(from, to);
323};
324
325global.subjake = function(/*Array<String>*/ directories, /*String*/ aTaskName)
326{
327    if (!Array.isArray(directories))
328        directories = [directories];
329
330    directories.forEach(function(/*String*/ aDirectory)
331    {
332        if (FILE.isDirectory(aDirectory) && FILE.isFile(FILE.join(aDirectory, "Jakefile")))
333        {
334            var cmd = "cd " + OS.enquote(aDirectory) + " && " + serializedENV() + " " + OS.enquote(SYSTEM.args[0]) + " " + OS.enquote(aTaskName),
335                returnCode = OS.system(cmd);
336
337            if (returnCode)
338                OS.exit(returnCode);
339        }
340        else
341            print("warning: subjake missing: " + aDirectory + " (this is not necessarily an error, " + aDirectory + " may be optional)");
342    });
343};
344
345global.executableExists = function(/*String*/ executableName)
346{
347    var paths = SYSTEM.env["PATH"].split(':');
348    for (var i = 0; i < paths.length; i++) {
349        var path = FILE.join(paths[i], executableName);
350        if (FILE.exists(path))
351            return path;
352    }
353    return null;
354};
355
356$OBJJ_TEMPLATE_EXECUTABLE = FILE.join($HOME_DIR, "Objective-J", "CommonJS", "objj-executable");
357
358global.make_objj_executable = function(aPath)
359{
360    cp($OBJJ_TEMPLATE_EXECUTABLE, aPath);
361    FILE.chmod(aPath, 0755);
362};
363
364global.symlink_executable = function(source)
365{
366    relative = FILE.relative($ENVIRONMENT_NARWHAL_BIN_DIR, source);
367    destination = FILE.join($ENVIRONMENT_NARWHAL_BIN_DIR, FILE.basename(source));
368    FILE.symlink(relative, destination);
369};
370
371global.getCappuccinoVersion = function()
372{
373    var versionFile = FILE.path(module.path).dirname().join("version.json");
374    return JSON.parse(versionFile.read({ charset : "UTF-8" })).version;
375};
376
377global.setPackageMetadata = function(packagePath)
378{
379    var pkg = JSON.parse(FILE.read(packagePath, { charset : "UTF-8" }));
380
381    var p = OS.popen(["git", "rev-parse", "--verify", "HEAD"]);
382    if (p.wait() === 0) {
383        var sha = p.stdout.read().split("\n")[0];
384        if (sha.length === 40)
385            pkg["cappuccino-revision"] = sha;
386    }
387
388    pkg["cappuccino-timestamp"] = new Date().getTime();
389    pkg["version"] = getCappuccinoVersion();
390
391    stream.print("    Version:   \0purple(" + pkg["version"] + "\0)");
392    stream.print("    Revision:  \0purple(" + pkg["cappuccino-revision"] + "\0)");
393    stream.print("    Timestamp: \0purple(" + pkg["cappuccino-timestamp"] + "\0)");
394
395    FILE.write(packagePath, JSON.stringify(pkg, null, 4), { charset : "UTF-8" });
396};
397
398global.subtasks = function(subprojects, taskNames)
399{
400    taskNames.forEach(function(aTaskName)
401    {
402        var subtaskName = aTaskName + "_subprojects";
403
404        task (aTaskName, [subtaskName]);
405
406        task (subtaskName, function()
407        {
408            subjake(subprojects, aTaskName);
409        });
410    });
411};
412
413global.installSymlink = function(sourcePath)
414{
415    if (!FILE.isDirectory(sourcePath))
416        return;
417
418    var packageName = FILE.basename(sourcePath),
419        targetPath = FILE.join(SYSTEM.prefix, "packages", packageName);
420
421    if (FILE.isDirectory(targetPath))
422        FILE.rmtree(targetPath);
423    else if (FILE.linkExists(targetPath))
424        FILE.remove(targetPath);
425
426    stream.print("Symlinking \0cyan(" + targetPath + "\0) ==> \0cyan(" + sourcePath + "\0)");
427    FILE.symlink(sourcePath, targetPath);
428
429    var binPath = FILE.Path(FILE.join(targetPath, "bin"));
430
431    if (binPath.isDirectory())
432    {
433        var narwhalBin = FILE.Path(FILE.join(SYSTEM.prefix, "bin"));
434
435        binPath.list().forEach(function (name)
436        {
437            var binary = binPath.join(name);
438            binary.chmod(0755);
439
440            var target = narwhalBin.join(name),
441                relative = FILE.relative(target, binary);
442
443            if (target.linkExists())
444                target.remove();
445
446            FILE.symlink(relative, target);
447        });
448    }
449};
450
451global.installCopy = function(sourcePath, useSudo)
452{
453    if (!FILE.isDirectory(sourcePath))
454        return;
455
456    var packageName = FILE.basename(sourcePath),
457        targetPath = FILE.join(SYSTEM.prefix, "packages", packageName);
458
459    if (FILE.isDirectory(targetPath))
460        FILE.rmtree(targetPath);
461    else if (FILE.linkExists(targetPath))
462        FILE.remove(targetPath);
463
464    stream.print("Copying \0cyan(" + sourcePath + "\0) ==> \0cyan(" + targetPath + "\0)");
465
466    // hacky way to do a sudo copy.
467    if (useSudo)
468        OS.system(["sudo", "cp", "-r", sourcePath, targetPath]);
469    else
470        FILE.copyTree(sourcePath, targetPath);
471
472    var binPath = FILE.Path(FILE.join(targetPath, "bin"));
473
474    if (binPath.isDirectory())
475    {
476        binPath.list().forEach(function (name)
477        {
478            var binary = binPath.join(name);
479            binary.chmod(0755);
480        });
481    }
482};
483
484global.spawnJake = function(/*String*/ aTaskName)
485{
486    if (OS.system(serializedENV() + " " + SYSTEM.args[0] + " " + aTaskName))
487        OS.exit(1);//rake abort if ($? != 0)
488};
489
490var normalizeCommand = function(/*Array or String*/ command)
491{
492    if (Array.isArray(command))
493        return command.map(function (arg)
494        {
495            return OS.enquote(arg);
496        }).join(" ");
497    else
498        return command;
499};
500
501global.sudo = function(/*Array or String*/ command)
502{
503    // First try without sudo
504    command = normalizeCommand(command);
505
506    var returnCode = OS.system(command + " >/dev/null 2>&1");
507
508    if (returnCode)
509    {
510        // if this is set, then disable the use of sudo.
511        // This is very usefull for CI scripts and stuff like that
512        if (SYSTEM.env["CAPP_NOSUDO"] == 1)
513            return returnCode;
514
515        return OS.system("sudo -p '\nEnter your admin password: ' " + command);
516    }
517
518
519    return 0;
520};
521
522global.exec = function(/*Array or String*/ command, quiet)
523{
524    command = normalizeCommand(command) + (quiet === true ? " >/dev/null 2>&1" : "");
525    return OS.system(command);
526};
527
528global.copyManPage = function(/*String*/ name, /*int*/ section)
529{
530    var manDir = "/usr/local/share/man/man" + section,
531        pageFile = name + "." + section,
532        manPagePath = FILE.join(manDir, pageFile);
533
534    if (!FILE.exists(manPagePath) || FILE.mtime(pageFile) > FILE.mtime(manPagePath))
535    {
536        if (!FILE.isDirectory(manDir))
537        {
538            if (sudo(["mkdir", "-p", "-m", "0755", manDir]))
539                stream.print("\0red(Unable to create the man directory.\0)");
540        }
541
542        if (sudo(["cp", "-f", pageFile, manDir]))
543            stream.print("\0red(Unable to copy the man file.\0)");
544    }
545};
546
547global.xcodebuildCanListSDKs = function()
548{
549    return global.exec("xcodebuild -showsdks", true) === 0;
550};
551
552global.xcodebuildHasTenPointFiveSDK = function()
553{
554    if (xcodebuildCanListSDKs())
555        return global.exec("xcodebuild -showsdks | grep 'macosx10.5'", true) === 0;
556
557    return FILE.exists(FILE.join("/", "Developer", "SDKs", "MacOSX10.5.sdk"));
558};
559
560global.colorize = function(/* String */ message, /* String */ color)
561{
562    var matches = color.match(/(bold(?: |\+))?(.+)/);
563
564    if (!matches)
565        return;
566
567    message = "\0" + matches[2] + "(" + message + "\0)";
568
569    if (matches[1])
570        message = "\0bold(" + message + "\0)";
571
572    return message;
573};
574
575global.colorPrint = function(/* String */ message, /* String */ color)
576{
577    stream.print(colorize(message, color));
578};
579
580var minUlimit = 1024;
581
582global.checkUlimit = function()
583{
584    var ulimitPath = executableExists("ulimit");
585
586    if (!ulimitPath)
587        return;
588
589    var p = OS.popen([ulimitPath, "-n"]);
590
591    if (p.wait() === 0)
592    {
593        var limit = p.stdout.read().split("\n")[0];
594
595        if (Number(limit) < minUlimit)
596        {
597            stream.print("\0red(\0bold(ERROR:\0)\0) Cappuccino may need to open more files than this terminal session currently allows (" + limit + "). Add the following line to your login configuration file (.bash_profile, .bashrc, etc.), start a new terminal session, then try again:\n");
598            stream.print("ulimit -n " + minUlimit);
599            OS.exit(1);
600        }
601    }
602}
603
604
605// built in tasks
606
607task ("build");
608task ("default", "build");
609
610task ("release", function()
611{
612    SYSTEM.env["CONFIG"] = "Release";
613    spawnJake("build");
614});
615
616task ("debug", function()
617{
618    SYSTEM.env["CONFIG"] = "Debug";
619    spawnJake("build");
620});
621
622task ("all", ["debug", "release"]);
623
624task ("sudo-install-symlinks", function()
625{
626    sudo("jake install-symlinks");
627});
628
629task ("sudo-install-debug-symlinks", function()
630{
631    sudo("jake install-debug-symlinks");
632});
633
634task ("clean-debug", function()
635{
636    SYSTEM.env['CONFIG'] = 'Debug';
637    spawnJake("clean");
638});
639
640task ("cleandebug", ["clean-debug"]);
641
642task ("clean-release", function()
643{
644    SYSTEM.env["CONFIG"] = "Release";
645    spawnJake("clean");
646});
647
648task ("cleanrelease", ["clean-release"]);
649
650task ("clean-all", ["clean-debug", "clean-release"]);
651task ("cleanall", ["clean-all"]);
652
653task ("clobber-debug", function()
654{
655    SYSTEM.env["CONFIG"] = "Debug";
656    spawnJake("clobber");
657});
658
659task ("clobberdebug", ["clobber-debug"]);
660
661task ("clobber-release", function()
662{
663    SYSTEM.env["CONFIG"] = "Release";
664    spawnJake("clobber");
665});
666
667task ("clobberrelease", ['clobber-release']);
668
669task ("clobber-all", ["clobber-debug", "clobber-release"]);
670task ("clobberall", ["clobber-all"]);