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