/Tools/capp/Generate.j

http://github.com/cacaodev/cappuccino · Unknown · 498 lines · 393 code · 105 blank · 0 comment · 0 complexity · 55ca7c99979f05496504c503f25ae92b MD5 · raw file

  1. /*
  2. * Generate.j
  3. * capp
  4. *
  5. * Created by Francisco Tolmasky.
  6. * Copyright 2009, 280 North, Inc.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. @import "Configuration.j"
  23. var OS = require("os"),
  24. SYSTEM = require("system"),
  25. FILE = require("file"),
  26. OBJJ = require("objective-j"),
  27. stream = require("narwhal/term").stream,
  28. parser = new (require("narwhal/args").Parser)();
  29. parser.usage("DESTINATION_DIRECTORY");
  30. parser.help("Generate a Cappuccino project or Frameworks directory");
  31. parser.option("-t", "--template", "template")
  32. .set()
  33. .def("Application")
  34. .help("Selects a project template to use (default: Application).");
  35. parser.option("-f", "--frameworks", "justFrameworks")
  36. .set(true)
  37. .help("Only generate or update Frameworks directory.");
  38. parser.option("-F", "--framework", "framework", "frameworks")
  39. .def([])
  40. .push()
  41. .help("Additional framework to copy/symlink (default: Objective-J, Foundation, AppKit)");
  42. parser.option("-T", "--theme", "theme", "themes")
  43. .def([])
  44. .push()
  45. .help("Additional Theme to copy/symlink into Resource (default: nothing)");
  46. parser.option("--no-frameworks", "noFrameworks")
  47. .set(true)
  48. .help("Don't copy any default frameworks (can be overridden with -F)");
  49. parser.option("--symlink", "symlink")
  50. .set(true)
  51. .help("Creates a symlink to each framework instead of copying.");
  52. parser.option("--build", "useCappBuild")
  53. .set(true)
  54. .help("Uses frameworks in the $CAPP_BUILD.");
  55. parser.option("-l")
  56. .action(function(o) { o.symlink = o.useCappBuild = true; })
  57. .help("Enables both the --symlink and --build options.");
  58. parser.option("--force", "force")
  59. .set(true)
  60. .help("Overwrite update existing frameworks.");
  61. parser.option("--noconfig", "noconfig")
  62. .set(true)
  63. .help("Use the default configuration, ignore your configuration.");
  64. parser.option("--list-templates", "listTemplates")
  65. .set(true)
  66. .help("Lists available templates.");
  67. parser.option("--list-frameworks", "listFrameworks")
  68. .set(true)
  69. .help("Lists available frameworks.");
  70. parser.helpful();
  71. // FIXME: better way to do this:
  72. var CAPP_HOME = require("narwhal/packages").catalog["cappuccino"].directory,
  73. templatesDirectory = FILE.join(CAPP_HOME, "lib", "capp", "Resources", "Templates");
  74. function gen(/*va_args*/)
  75. {
  76. var args = ["capp gen"].concat(Array.prototype.slice.call(arguments)),
  77. options = parser.parse(args, null, null, true);
  78. if (options.args.length > 1)
  79. {
  80. parser.printUsage(options);
  81. OS.exit(1);
  82. }
  83. if (options.listTemplates)
  84. {
  85. listTemplates();
  86. return;
  87. }
  88. if (options.listFrameworks)
  89. {
  90. listFrameworks();
  91. return;
  92. }
  93. var destination = options.args[0];
  94. if (!destination)
  95. {
  96. if (options.justFrameworks)
  97. destination = ".";
  98. else
  99. {
  100. parser.printUsage(options);
  101. OS.exit(1);
  102. }
  103. }
  104. var sourceTemplate = null;
  105. if (FILE.isAbsolute(options.template))
  106. sourceTemplate = options.template;
  107. else
  108. sourceTemplate = FILE.join(templatesDirectory, options.template);
  109. if (!FILE.isDirectory(sourceTemplate))
  110. {
  111. stream.print(colorize("Error: ", "red") + "The template " + logPath(sourceTemplate) + " cannot be found. Available templates are:");
  112. listTemplates();
  113. OS.exit(1);
  114. }
  115. var configFile = FILE.join(sourceTemplate, "template.config"),
  116. config = {};
  117. if (FILE.isFile(configFile))
  118. config = JSON.parse(FILE.read(configFile, { charset:"UTF-8" }));
  119. var destinationProject = destination,
  120. configuration = options.noconfig ? [Configuration defaultConfiguration] : [Configuration userConfiguration],
  121. frameworks = options.frameworks,
  122. themes = options.themes;
  123. if (!options.noFrameworks)
  124. frameworks.push("Objective-J", "Foundation", "AppKit");
  125. if (options.justFrameworks)
  126. {
  127. createFrameworksInFile(frameworks, destinationProject, options.symlink, options.useCappBuild, options.force);
  128. createThemesInFile(themes, destinationProject, options.symlink, options.force);
  129. }
  130. else if (!FILE.exists(destinationProject))
  131. {
  132. // FIXME???
  133. FILE.copyTree(sourceTemplate, destinationProject);
  134. var files = FILE.glob(FILE.join(destinationProject, "**", "*")),
  135. count = files.length,
  136. name = FILE.basename(destinationProject),
  137. orgIdentifier = [configuration valueForKey:@"organization.identifier"] || "";
  138. [configuration setTemporaryValue:name forKey:@"project.name"];
  139. [configuration setTemporaryValue:orgIdentifier + '.' + toIdentifier(name) forKey:@"project.identifier"];
  140. [configuration setTemporaryValue:toIdentifier(name) forKey:@"project.nameasidentifier"];
  141. for (var index = 0; index < count; ++index)
  142. {
  143. var path = files[index];
  144. if (FILE.isDirectory(path))
  145. continue;
  146. if (FILE.basename(path) === ".DS_Store")
  147. continue;
  148. // Don't do this for images.
  149. if ([".png", ".jpg", ".jpeg", ".gif", ".tif", ".tiff"].indexOf(FILE.extension(path).toLowerCase()) !== -1)
  150. continue;
  151. try
  152. {
  153. var contents = FILE.read(path, { charset : "UTF-8" }),
  154. key = null,
  155. keyEnumerator = [configuration keyEnumerator];
  156. while ((key = [keyEnumerator nextObject]) !== nil)
  157. contents = contents.replace(new RegExp("__" + RegExp.escape(key) + "__", 'g'), [configuration valueForKey:key]);
  158. FILE.write(path, contents, { charset: "UTF-8"});
  159. }
  160. catch (anException)
  161. {
  162. warn("An error occurred (" + anException.toString() + ") while applying the " + (options.noconfig ? "default" : "user") + " configuration to: " + logPath(path));
  163. }
  164. }
  165. var frameworkDestination = destinationProject;
  166. if (config.FrameworksPath)
  167. frameworkDestination = FILE.join(frameworkDestination, config.FrameworksPath);
  168. createFrameworksInFile(frameworks, frameworkDestination, options.symlink, options.useCappBuild);
  169. var themeDestination = destinationProject;
  170. if (themes.length)
  171. createThemesInFile(themes, themeDestination, options.symlink);
  172. }
  173. else
  174. {
  175. fail("The directory " + FILE.absolute(destinationProject) + " already exists.");
  176. }
  177. executePostInstallScript(destinationProject);
  178. }
  179. function createFrameworksInFile(/*Array*/ frameworks, /*String*/ aFile, /*Boolean*/ symlink, /*Boolean*/ build, /*Boolean*/ force)
  180. {
  181. var destination = FILE.path(FILE.absolute(aFile));
  182. if (!destination.isDirectory())
  183. fail("Cannot create Frameworks. The directory does not exist: " + destination);
  184. var destinationFrameworks = destination.join("Frameworks"),
  185. destinationDebugFrameworks = destination.join("Frameworks", "Debug");
  186. stream.print("Creating Frameworks directory in " + logPath(destinationFrameworks) + "...");
  187. //destinationFrameworks.mkdirs(); // redundant
  188. destinationDebugFrameworks.mkdirs();
  189. if (build)
  190. {
  191. if (!(SYSTEM.env["CAPP_BUILD"]))
  192. fail("$CAPP_BUILD must be defined to use the --build or -l option.");
  193. var builtFrameworks = FILE.path(SYSTEM.env["CAPP_BUILD"]),
  194. sourceFrameworks = builtFrameworks.join("Release"),
  195. sourceDebugFrameworks = builtFrameworks.join("Debug");
  196. frameworks.forEach(function(framework)
  197. {
  198. installFramework(sourceFrameworks.join(framework), destinationFrameworks.join(framework), force, symlink);
  199. installFramework(sourceDebugFrameworks.join(framework), destinationDebugFrameworks.join(framework), force, symlink);
  200. });
  201. }
  202. else
  203. {
  204. // Frameworks. Search frameworks paths
  205. frameworks.forEach(function(framework)
  206. {
  207. // Need a special case for Objective-J
  208. if (framework === "Objective-J")
  209. {
  210. // Objective-J. Take from OBJJ_HOME.
  211. var objjHome = FILE.path(OBJJ.OBJJ_HOME),
  212. objjPath = objjHome.join("Frameworks", "Objective-J"),
  213. objjDebugPath = objjHome.join("Frameworks", "Debug", "Objective-J");
  214. installFramework(objjPath, destinationFrameworks.join("Objective-J"), force, symlink);
  215. installFramework(objjDebugPath, destinationDebugFrameworks.join("Objective-J"), force, symlink);
  216. return;
  217. }
  218. var found = false;
  219. for (var i = 0; i < OBJJ.objj_frameworks.length; i++)
  220. {
  221. var sourceFramework = FILE.path(OBJJ.objj_frameworks[i]).join(framework);
  222. if (FILE.isDirectory(sourceFramework))
  223. {
  224. installFramework(sourceFramework, destinationFrameworks.join(framework), force, symlink);
  225. found = true;
  226. break;
  227. }
  228. }
  229. if (!found)
  230. warn("Couldn't find the framework: " + logPath(framework));
  231. for (var i = 0, found = false; i < OBJJ.objj_debug_frameworks.length; i++)
  232. {
  233. var sourceDebugFramework = FILE.path(OBJJ.objj_debug_frameworks[i]).join(framework);
  234. if (FILE.isDirectory(sourceDebugFramework))
  235. {
  236. installFramework(sourceDebugFramework, destinationDebugFrameworks.join(framework), force, symlink);
  237. found = true;
  238. break;
  239. }
  240. }
  241. if (!found)
  242. warn("Couldn't find the debug framework: " + logPath(framework));
  243. });
  244. }
  245. }
  246. function installFramework(source, dest, force, symlink)
  247. {
  248. if (dest.exists())
  249. {
  250. if (force)
  251. dest.rmtree();
  252. else
  253. {
  254. warn(logPath(dest) + " already exists. Use --force to overwrite.");
  255. return;
  256. }
  257. }
  258. if (source.exists())
  259. {
  260. stream.print((symlink ? "Symlinking " : "Copying ") + logPath(source) + " ==> " + logPath(dest));
  261. if (symlink)
  262. FILE.symlink(source, dest);
  263. else
  264. FILE.copyTree(source, dest);
  265. }
  266. else
  267. warn("Cannot find: " + logPath(source));
  268. }
  269. function createThemesInFile(/*Array*/ themes, /*String*/ aFile, /*Boolean*/ symlink, /*Boolean*/ force)
  270. {
  271. var destination = FILE.path(FILE.absolute(aFile));
  272. if (!destination.isDirectory())
  273. fail("Cannot create Themes. The directory does not exist: " + destination);
  274. var destinationThemes = destination.join("Resources");
  275. stream.print("Creating Themes in " + logPath(destinationThemes) + "...");
  276. if (!(SYSTEM.env["CAPP_BUILD"]))
  277. fail("$CAPP_BUILD must be defined to use the --theme or -T option.");
  278. var themesBuild = FILE.join(SYSTEM.env["CAPP_BUILD"], "Release"),
  279. sources = [];
  280. themes.forEach(function(theme)
  281. {
  282. var themeFolder = theme + ".blend",
  283. path = FILE.join(themesBuild, themeFolder);
  284. if (!FILE.isDirectory(path))
  285. fail("Cannot find theme " + themeFolder + " in " + themesBuild);
  286. sources.push([FILE.path(path), themeFolder])
  287. });
  288. sources.forEach(function(source)
  289. {
  290. installTheme(source[0], FILE.path(destinationThemes).join(source[1]), force, symlink);
  291. });
  292. }
  293. function installTheme(source, dest, force, symlink)
  294. {
  295. if (dest.exists())
  296. {
  297. if (force)
  298. dest.rmtree();
  299. else
  300. {
  301. warn(logPath(dest) + " already exists. Use --force to overwrite.");
  302. return;
  303. }
  304. }
  305. if (source.exists())
  306. {
  307. stream.print((symlink ? "Symlinking " : "Copying ") + logPath(source) + " ==> " + logPath(dest));
  308. if (symlink)
  309. FILE.symlink(source, dest);
  310. else
  311. FILE.copyTree(source, dest);
  312. }
  313. else
  314. warn("Cannot find: " + logPath(source));
  315. }
  316. function toIdentifier(/*String*/ aString)
  317. {
  318. var identifier = "",
  319. count = aString.length,
  320. capitalize = NO,
  321. firstRegex = new RegExp("^[a-zA-Z_$]"),
  322. regex = new RegExp("^[a-zA-Z_$0-9]");
  323. for (var index = 0; index < count; ++index)
  324. {
  325. var character = aString.charAt(index);
  326. if ((index === 0) && firstRegex.test(character) || regex.test(character))
  327. {
  328. if (capitalize)
  329. identifier += character.toUpperCase();
  330. else
  331. identifier += character;
  332. capitalize = NO;
  333. }
  334. else
  335. capitalize = YES;
  336. }
  337. return identifier;
  338. }
  339. function listTemplates()
  340. {
  341. FILE.list(templatesDirectory).forEach(function(templateName)
  342. {
  343. stream.print(templateName);
  344. });
  345. }
  346. function listFrameworks()
  347. {
  348. stream.print("Frameworks:");
  349. OBJJ.objj_frameworks.forEach(function(frameworksDirectory)
  350. {
  351. stream.print(" " + frameworksDirectory);
  352. FILE.list(frameworksDirectory).forEach(function(templateName)
  353. {
  354. stream.print(" + " + templateName);
  355. });
  356. });
  357. stream.print("Frameworks (Debug):");
  358. OBJJ.objj_debug_frameworks.forEach(function(frameworksDirectory)
  359. {
  360. stream.print(" " + frameworksDirectory);
  361. FILE.list(frameworksDirectory).forEach(function(frameworkName)
  362. {
  363. stream.print(" + " + frameworkName);
  364. });
  365. });
  366. }
  367. function executePostInstallScript(/*String*/ destinationProject)
  368. {
  369. var path = FILE.join(destinationProject, "postinstall");
  370. if (FILE.exists(path))
  371. {
  372. stream.print(colorize("Executing postinstall script...", "cyan"));
  373. OS.system(["/bin/sh", path, destinationProject]); // Use sh in case it isn't marked executable
  374. FILE.remove(path);
  375. }
  376. }
  377. function colorize(message, color)
  378. {
  379. return "\0" + color + "(" + message + "\0)";
  380. }
  381. function logPath(path)
  382. {
  383. return colorize(path, "cyan");
  384. }
  385. function warn(message)
  386. {
  387. stream.print(colorize("Warning: ", "yellow") + message);
  388. }
  389. function fail(message)
  390. {
  391. stream.print(colorize(message, "red"));
  392. OS.exit(1);
  393. }