PageRenderTime 29ms CodeModel.GetById 23ms app.highlight 2ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 23@import "Configuration.j"
 24
 25var OS = require("os"),
 26    SYSTEM = require("system"),
 27    FILE = require("file"),
 28    OBJJ = require("objective-j"),
 29
 30    stream = require("narwhal/term").stream,
 31    parser = new (require("narwhal/args").Parser)();
 32
 33parser.usage("DESTINATION_DIRECTORY");
 34
 35parser.help("Generate a Cappuccino project or Frameworks directory");
 36
 37parser.option("-t", "--template", "template")
 38    .set()
 39    .def("Application")
 40    .help("Selects a project template to use (default: Application).");
 41
 42parser.option("-f", "--frameworks", "justFrameworks")
 43    .set(true)
 44    .help("Only generate or update Frameworks directory.");
 45
 46parser.option("-F", "--framework", "framework", "frameworks")
 47    .def([])
 48    .push()
 49    .help("Additional framework to copy/symlink (default: Objective-J, Foundation, AppKit)");
 50
 51parser.option("-T", "--theme", "theme", "themes")
 52    .def([])
 53    .push()
 54    .help("Additional Theme to copy/symlink into Resource (default: nothing)");
 55
 56parser.option("--no-frameworks", "noFrameworks")
 57    .set(true)
 58    .help("Don't copy any default frameworks (can be overridden with -F)");
 59
 60parser.option("--symlink", "symlink")
 61    .set(true)
 62    .help("Creates a symlink to each framework instead of copying.");
 63
 64parser.option("--build", "useCappBuild")
 65    .set(true)
 66    .help("Uses frameworks in the $CAPP_BUILD.");
 67
 68parser.option("-l")
 69    .action(function(o) { o.symlink = o.useCappBuild = true; })
 70    .help("Enables both the --symlink and --build options.");
 71
 72parser.option("--force", "force")
 73    .set(true)
 74    .help("Overwrite update existing frameworks.");
 75
 76parser.option("--noconfig", "noconfig")
 77    .set(true)
 78    .help("Use the default configuration, ignore your configuration.");
 79
 80parser.option("--list-templates", "listTemplates")
 81    .set(true)
 82    .help("Lists available templates.");
 83
 84parser.option("--list-frameworks", "listFrameworks")
 85    .set(true)
 86    .help("Lists available frameworks.");
 87
 88parser.helpful();
 89
 90// FIXME: better way to do this:
 91var CAPP_HOME = require("narwhal/packages").catalog["cappuccino"].directory,
 92    templatesDirectory = FILE.join(CAPP_HOME, "lib", "capp", "Resources", "Templates");
 93
 94function gen(/*va_args*/)
 95{
 96    var args = ["capp gen"].concat(Array.prototype.slice.call(arguments)),
 97        options = parser.parse(args, null, null, true);
 98
 99    if (options.args.length > 1)
100    {
101        parser.printUsage(options);
102        OS.exit(1);
103    }
104
105    if (options.listTemplates)
106    {
107        listTemplates();
108        return;
109    }
110
111    if (options.listFrameworks)
112    {
113        listFrameworks();
114        return;
115    }
116
117    var destination = options.args[0];
118
119    if (!destination)
120    {
121        if (options.justFrameworks)
122            destination = ".";
123
124        else
125        {
126            parser.printUsage(options);
127            OS.exit(1);
128        }
129    }
130
131    var sourceTemplate = null;
132
133    if (FILE.isAbsolute(options.template))
134        sourceTemplate = options.template;
135    else
136        sourceTemplate = FILE.join(templatesDirectory, options.template);
137
138    if (!FILE.isDirectory(sourceTemplate))
139    {
140        stream.print(colorize("Error: ", "red") + "The template " + logPath(sourceTemplate) + " cannot be found. Available templates are:");
141        listTemplates();
142        OS.exit(1);
143    }
144
145    var configFile = FILE.join(sourceTemplate, "template.config"),
146        config = {};
147
148    if (FILE.isFile(configFile))
149        config = JSON.parse(FILE.read(configFile, { charset:"UTF-8" }));
150
151    var destinationProject = destination,
152        configuration = options.noconfig ? [Configuration defaultConfiguration] : [Configuration userConfiguration],
153        frameworks = options.frameworks,
154        themes = options.themes;
155
156    if (!options.noFrameworks)
157        frameworks.push("Objective-J", "Foundation", "AppKit");
158
159    if (options.justFrameworks)
160    {
161        createFrameworksInFile(frameworks, destinationProject, options.symlink, options.useCappBuild, options.force);
162        createThemesInFile(themes, destinationProject, options.symlink, options.force);
163    }
164
165    else if (!FILE.exists(destinationProject))
166    {
167        // FIXME???
168        FILE.copyTree(sourceTemplate, destinationProject);
169
170        var files = FILE.glob(FILE.join(destinationProject, "**", "*")),
171            count = files.length,
172            name = FILE.basename(destinationProject),
173            orgIdentifier = [configuration valueForKey:@"organization.identifier"] || "";
174
175        [configuration setTemporaryValue:name forKey:@"project.name"];
176        [configuration setTemporaryValue:orgIdentifier + '.' +  toIdentifier(name) forKey:@"project.identifier"];
177        [configuration setTemporaryValue:toIdentifier(name) forKey:@"project.nameasidentifier"];
178
179        for (var index = 0; index < count; ++index)
180        {
181            var path = files[index];
182
183            if (FILE.isDirectory(path))
184                continue;
185
186            if (FILE.basename(path) === ".DS_Store")
187                continue;
188
189            // Don't do this for images.
190            if ([".png", ".jpg", ".jpeg", ".gif", ".tif", ".tiff"].indexOf(FILE.extension(path).toLowerCase()) !== -1)
191                continue;
192
193            try
194            {
195                var contents = FILE.read(path, { charset : "UTF-8" }),
196                    key = null,
197                    keyEnumerator = [configuration keyEnumerator];
198
199                while ((key = [keyEnumerator nextObject]) !== nil)
200                    contents = contents.replace(new RegExp("__" + RegExp.escape(key) + "__", 'g'), [configuration valueForKey:key]);
201
202                FILE.write(path, contents, { charset: "UTF-8"});
203            }
204            catch (anException)
205            {
206                warn("An error occurred (" + anException.toString() + ") while applying the " + (options.noconfig ? "default" : "user") + " configuration to: " + logPath(path));
207            }
208        }
209
210        var frameworkDestination = destinationProject;
211
212        if (config.FrameworksPath)
213            frameworkDestination = FILE.join(frameworkDestination, config.FrameworksPath);
214
215        createFrameworksInFile(frameworks, frameworkDestination, options.symlink, options.useCappBuild);
216
217        var themeDestination = destinationProject;
218
219        if (themes.length)
220            createThemesInFile(themes, themeDestination, options.symlink);
221    }
222
223    else
224    {
225        fail("The directory " + FILE.absolute(destinationProject) + " already exists.");
226    }
227
228    executePostInstallScript(destinationProject);
229}
230
231function createFrameworksInFile(/*Array*/ frameworks, /*String*/ aFile, /*Boolean*/ symlink, /*Boolean*/ build, /*Boolean*/ force)
232{
233    var destination = FILE.path(FILE.absolute(aFile));
234
235    if (!destination.isDirectory())
236        fail("Cannot create Frameworks. The directory does not exist: " + destination);
237
238    var destinationFrameworks = destination.join("Frameworks"),
239        destinationDebugFrameworks = destination.join("Frameworks", "Debug");
240
241    stream.print("Creating Frameworks directory in " + logPath(destinationFrameworks) + "...");
242
243    //destinationFrameworks.mkdirs(); // redundant
244    destinationDebugFrameworks.mkdirs();
245
246    if (build)
247    {
248        if (!(SYSTEM.env["CAPP_BUILD"]))
249            fail("$CAPP_BUILD must be defined to use the --build or -l option.");
250
251        var builtFrameworks = FILE.path(SYSTEM.env["CAPP_BUILD"]),
252            sourceFrameworks = builtFrameworks.join("Release"),
253            sourceDebugFrameworks = builtFrameworks.join("Debug");
254
255        frameworks.forEach(function(framework)
256        {
257            installFramework(sourceFrameworks.join(framework), destinationFrameworks.join(framework), force, symlink);
258            installFramework(sourceDebugFrameworks.join(framework), destinationDebugFrameworks.join(framework), force, symlink);
259        });
260    }
261    else
262    {
263        // Frameworks. Search frameworks paths
264        frameworks.forEach(function(framework)
265        {
266            // Need a special case for Objective-J
267            if (framework === "Objective-J")
268            {
269                // Objective-J. Take from OBJJ_HOME.
270                var objjHome = FILE.path(OBJJ.OBJJ_HOME),
271                    objjPath = objjHome.join("Frameworks", "Objective-J"),
272                    objjDebugPath = objjHome.join("Frameworks", "Debug", "Objective-J");
273
274                installFramework(objjPath, destinationFrameworks.join("Objective-J"), force, symlink);
275                installFramework(objjDebugPath, destinationDebugFrameworks.join("Objective-J"), force, symlink);
276
277                return;
278            }
279
280            var found = false;
281
282            for (var i = 0; i < OBJJ.objj_frameworks.length; i++)
283            {
284                var sourceFramework = FILE.path(OBJJ.objj_frameworks[i]).join(framework);
285
286                if (FILE.isDirectory(sourceFramework))
287                {
288                    installFramework(sourceFramework, destinationFrameworks.join(framework), force, symlink);
289                    found = true;
290                    break;
291                }
292            }
293
294            if (!found)
295                warn("Couldn't find the framework: " + logPath(framework));
296
297            for (var i = 0, found = false; i < OBJJ.objj_debug_frameworks.length; i++)
298            {
299                var sourceDebugFramework = FILE.path(OBJJ.objj_debug_frameworks[i]).join(framework);
300
301                if (FILE.isDirectory(sourceDebugFramework))
302                {
303                    installFramework(sourceDebugFramework, destinationDebugFrameworks.join(framework), force, symlink);
304                    found = true;
305                    break;
306                }
307            }
308
309            if (!found)
310                warn("Couldn't find the debug framework: " + logPath(framework));
311        });
312    }
313}
314
315function installFramework(source, dest, force, symlink)
316{
317    if (dest.exists())
318    {
319        if (force)
320            dest.rmtree();
321
322        else
323        {
324            warn(logPath(dest) + " already exists. Use --force to overwrite.");
325            return;
326        }
327    }
328
329    if (source.exists())
330    {
331        stream.print((symlink ? "Symlinking " : "Copying ") + logPath(source) + " ==> " + logPath(dest));
332
333        if (symlink)
334            FILE.symlink(source, dest);
335        else
336            FILE.copyTree(source, dest);
337    }
338    else
339        warn("Cannot find: " + logPath(source));
340}
341
342function createThemesInFile(/*Array*/ themes, /*String*/ aFile, /*Boolean*/ symlink, /*Boolean*/ force)
343{
344    var destination = FILE.path(FILE.absolute(aFile));
345
346    if (!destination.isDirectory())
347        fail("Cannot create Themes. The directory does not exist: " + destination);
348
349    var destinationThemes = destination.join("Resources");
350
351    stream.print("Creating Themes in " + logPath(destinationThemes) + "...");
352
353    if (!(SYSTEM.env["CAPP_BUILD"]))
354        fail("$CAPP_BUILD must be defined to use the --theme or -T option.");
355
356    var themesBuild = FILE.join(SYSTEM.env["CAPP_BUILD"], "Release"),
357        sources = [];
358
359    themes.forEach(function(theme)
360    {
361        var themeFolder = theme + ".blend",
362            path = FILE.join(themesBuild, themeFolder);
363
364        if (!FILE.isDirectory(path))
365            fail("Cannot find theme " + themeFolder + " in " + themesBuild);
366
367        sources.push([FILE.path(path), themeFolder])
368    });
369
370    sources.forEach(function(source)
371    {
372        installTheme(source[0], FILE.path(destinationThemes).join(source[1]), force, symlink);
373    });
374}
375
376function installTheme(source, dest, force, symlink)
377{
378    if (dest.exists())
379    {
380        if (force)
381            dest.rmtree();
382
383        else
384        {
385            warn(logPath(dest) + " already exists. Use --force to overwrite.");
386            return;
387        }
388    }
389
390    if (source.exists())
391    {
392        stream.print((symlink ? "Symlinking " : "Copying ") + logPath(source) + " ==> " + logPath(dest));
393
394        if (symlink)
395            FILE.symlink(source, dest);
396        else
397            FILE.copyTree(source, dest);
398    }
399    else
400        warn("Cannot find: " + logPath(source));
401}
402
403function toIdentifier(/*String*/ aString)
404{
405    var identifier = "",
406        count = aString.length,
407        capitalize = NO,
408        firstRegex = new RegExp("^[a-zA-Z_$]"),
409        regex = new RegExp("^[a-zA-Z_$0-9]");
410
411    for (var index = 0; index < count; ++index)
412    {
413        var character = aString.charAt(index);
414
415        if ((index === 0) && firstRegex.test(character) || regex.test(character))
416        {
417            if (capitalize)
418                identifier += character.toUpperCase();
419            else
420                identifier += character;
421
422            capitalize = NO;
423        }
424        else
425            capitalize = YES;
426    }
427
428    return identifier;
429}
430
431function listTemplates()
432{
433    FILE.list(templatesDirectory).forEach(function(templateName)
434    {
435        stream.print(templateName);
436    });
437}
438
439function listFrameworks()
440{
441    stream.print("Frameworks:");
442
443    OBJJ.objj_frameworks.forEach(function(frameworksDirectory)
444    {
445        stream.print("  " + frameworksDirectory);
446
447        FILE.list(frameworksDirectory).forEach(function(templateName)
448        {
449            stream.print("    + " + templateName);
450        });
451    });
452
453    stream.print("Frameworks (Debug):");
454
455    OBJJ.objj_debug_frameworks.forEach(function(frameworksDirectory)
456    {
457        stream.print("  " + frameworksDirectory);
458
459        FILE.list(frameworksDirectory).forEach(function(frameworkName)
460        {
461            stream.print("    + " + frameworkName);
462        });
463    });
464}
465
466function executePostInstallScript(/*String*/ destinationProject)
467{
468    var path = FILE.join(destinationProject, "postinstall");
469
470    if (FILE.exists(path))
471    {
472        stream.print(colorize("Executing postinstall script...", "cyan"));
473
474        OS.system(["/bin/sh", path, destinationProject]);  // Use sh in case it isn't marked executable
475        FILE.remove(path);
476    }
477}
478
479function colorize(message, color)
480{
481    return "\0" + color + "(" + message + "\0)";
482}
483
484function logPath(path)
485{
486    return colorize(path, "cyan");
487}
488
489function warn(message)
490{
491    stream.print(colorize("Warning: ", "yellow") + message);
492}
493
494function fail(message)
495{
496    stream.print(colorize(message, "red"));
497    OS.exit(1);
498}