1: http://coffeescript.org/#cake
2: http://coffeescript.org/documentation/docs/cake.html
> --
> Job Board: http://jobs.nodejs.org/
> Posting guidelines:
> https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> You received this message because you are subscribed to the Google
> Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com
> To unsubscribe from this group, send email to
> nodejs+un...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/nodejs?hl=en?hl=en
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en
I also am starting to use Jake. Note that you can write your Jakefiles in CoffeeScript if you prefer that over JavaScript.
> Some immediate thoughts:
> 1) chaining. Rather than
>
> desc('description');
> task(some more);
>
> why not chain them together? This way you are dependent on the order,
> but if you can do
> desc('some desc').task(...);
>
> or
> task(...).desc('some desc');
>
> it gets much cleaner (and your code to build this is probably
> lighter).
As a user, this really doesn't matter to me. :) It works fine the way it is.
> 2) I like that you have the predefined file() and directory() tasks,
> and those packaging and npm publish tasks are spot on. I would add the
> following, which should be pretty easy:
More specific comments below, but my general comment is that, like most Node modules, I think Jake is trying to have a narrow focus, and do just the bare essentials needed. There are other existing modules and techniques to handle what you're asking for, and you should use those instead of expecting Jake to grow larger to encompass all those tasks. Importantly, there are usually several ways of accomplishing a given task, or even several different modules available; Jake shouldn't be put into the position of having to guess which technique you prefer, nor of offering all of them so that you can choose. A Jakefile is just JavaScript (or CoffeeScript) code, so you can code up whatever you want it to do. And we're all here because we love (Java|Coffee)Script and program efficiently in it, so writing a couple lines of code for any of the below tasks shouldn't be a big deal.
> - minification
minification is one of the examples given in the Jake readme, though writing the code was left as an exercise for the reader.
For minification I am using UglifyJS:
https://github.com/mishoo/UglifyJS#readme
My minification task looks like this:
var fs = require('fs');
var uglify = require('uglify-js');
desc('builds the minified library');
file('library.min.js', ['library.js'], function() {
var code = fs.readFileSync('library.js', 'utf8');
var uglify_options = {
strict_semicolons: true,
gen_options: {ascii_only: true},
};
var minified_code = uglify(code, uglify_options);
fs.writeFileSync('library.min.js', minified_code, 'utf8');
});
> - jslint
Good idea, you could write a "test" or "check" task. I'm sure there are available linting programs or modules you could use. For now, minification is sufficient for me; if I write a syntax error, the minifier ought to catch it and complain.
> - replace - this is a workhorse of Ant. This way, you have some string
> inside your files (e.g. @@version@@) and then keep your version in one
> place. Run replace on your combined file and the version is
> automatically correct.
Yes, I want to do this in my Jakefile. I was planning to write a few-line function to do this. I had an example of this kind of Jake task that I was going to follow, but I seem to have misplaced it so I can't show it to you.
> - zip - don't know if this is hard or easy
The PackageTask can already create .tar.gz and .tar.bz2 files, so adding .zip creation seems reasonable. It looks like this was already planned -- PackageTask has a zipCommand variable, but never uses it.
> - delete - to delete (i.e. clean) a file or directory
Deleting a file is just:
http://nodejs.org/docs/latest/api/fs.html#fs.unlinkSync
Deleting an empty directory is just:
http://nodejs.org/docs/latest/api/fs.html#fs.rmdirSync
Deleting a directory and all its contents is:
https://github.com/isaacs/rimraf#readme
My clean task currently looks like this:
var rimraf = require('rimraf');
desc('removes the build directory and everything in it');
task('clean', function() {
rimraf('build', {gently: true}, complete);
}, {async: true});
> I think I misunderstood something. The filetask doesn't actually
> concat the files, does it? It just checks the timestamps on the list
> of file dependencies and the target (if it exists), and determines
> whether or not to execute the task? It doesn't pass the list of file
> dependencies to the callback either?
I believe that's correct.
Here's how one of my concat tasks looks like:
desc('builds the concatenated path library');
file('build/path.js', ['build', 'src/Point.js', 'src/Bezier.js', 'src/Path.js', 'src/Polygon.js', 'src/Rect.js', 'src/Ellipse.js'], function() {
var code = [];
['src/Point.js', 'src/Bezier.js', 'src/Path.js', 'src/Polygon.js', 'src/Rect.js', 'src/Ellipse.js'].forEach(function(file) {
code.push(fs.readFileSync(file, 'utf8'));
});
fs.writeFileSync('build/path.js', code.join("\n"), 'utf8');
});
(from http://code.google.com/p/canviz/source/browse/path/trunk/Jakefile )
> How do I do things synchronously inside a task? E.g. I want to concat
> a file as discussed above, then copy a file, then copy some other. The
> first only runs if the files are updated, but the letters might run no
> matter what.
>
> Promises might be nice here.
I don't think you need promises or anything fancy. Just define your tasks, with the correct dependencies. If you want a task to run only when a file has been modified, define it as a "file". If you want a task to run every time no matter what, define it as a "task". If this isn't working for you, can you post relevant parts of your Jakefile?
I'm not familiar with ant, but glancing at the XML, and reading your description, it sounds like a script -- a sequential set of steps. That's not what a Jakefile is; that's not what a Makefile is.* A Jakefile, or a Makefile, is a collection of tasks, each task doing one small thing. When you invoke jake, or make, you tell it what task(s) to run. Jake or make figures out what other tasks need to run first, based on the dependencies you've declared in your tasks. With make, some tasks might even run in parallel, if the tasks have no dependencies on one another and you request multiple jobs; don't know what jake does.
The point is, within your sequential set of steps above, you've described several tasks. The task to concat a few files into another one and do some replacements. (That's a file task.) The task to minify that concatenated file into a new minified file, inserting a license header. (That's a second file task.) The task to copy some files around. (That might be another file task.)
There's no need for "a lot of repetition". A Jakefile is JavaScript, and standard DRY principles apply. If you find yourself doing the same thing over and over, abstract that code into a function.
Here's the Jakefile for a project I'm working on; perhaps it helps to see a complete working example:
http://code.google.com/p/canviz/source/browse/path/trunk/Jakefile
I am still new to jake myself so I might not yet know the best way to do everything, but this is working for me so far. And I've barely needed to think about callbacks at all.
Today I wrote a fun preprocessing function in the Jakefile, to allow me to put C-like "#include" directives at the tops of my JavaScript fragments to indicate what other fragments they require. Then I wrote a wrapper fragment that just #include's all the others, and that's what my concatenated script is built from. Now I no longer have to manually keep track of what order my fragments need to be listed in in the concatenated file. Also, this preprocessor is where I'll add string replacements shortly.
* If what you want is a sequential set of steps, then you don't need jake or make; just write a script.
> Hmm, even the basic throw me off. Is there something wrong with:
>
> task("init",function(){
> directory("./a");
> directory("./b");
> directory("./c");
> directory("./d");
> });
>
> It doesn't create the dirs?
directory('a') doesn't create a directory. It defines a task that, when run, creates the directory -- a task that you would declare as a dependency for other tasks you define that require that directory to exist.
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en
> 1) Modularity: even in a scripted language, if I concatenate a file on
> a regular basis, then I really want a single function to check the
> dependency and do the concat. Using Ryan's example from earlier,
> almost everyone in a build file does some form of concat; it is a very
> common activity. So the following is a lot cleaner:
>
> concat("output.js",["file1.js","file2.js","file3.js"]);
>
> than
>
> file("output.js",["file1.js","file2.js","file3.js"],function() {
> // do some concat here and I need to list the files all over again
> });
>
> That is why I listed the very common tasks people do: concat, minify,
> etc.
On the one hand, you could probably easily define "concat" and "minify" functions that do this (they would themselves call the "file" function and supply their own generic callbacks).
On the other hand, in the code I showed you, I want to do more than just concat; I also want to implement my "#include" preprocessor directive, and I also want to do placeholder substitutions, like you showed in your ant task. For minification, I need to specify some options to the minifier, which might be different from the options you want or need, and I also want to prepend the minified code with a license header, which you mentioned wanting to do too. For these reasons, I don't think it's so bad if you have to write the code yourself in your Jakefile, because then you know it does what you want it to.
> 2) Organization: There is a distinction between tasks and groups (or
> targets, in make/ant lingo). When I run make "some target", or better
> yet jake -T, I want to see just those targets that make sense at the
> highest level. For example, I am never going to create a directory by
> running "jake build/", might as well just do "mkdir build/". But if I
> need five directories to be built as part of my initialization, then I
> will have a *target* that creates five directories, each creation of
> which is a *task* to be done.
I do agree that I would like "jake -T" to list only tasks that a user would want to run, and not every little intermediate task that I had to define. I hadn't yet gotten around to looking up whether there was a way to maybe declare a task as hidden, or whether this is a missing feature so far.
> Isn't it better to just eliminate small tasks and write a high-level and meaningful tasks, using plain code for expressing low-level logic, and don't overcomplicate it with introducing lots of smaller tasks and complex dependencies?
> Ant needs such approach because You can't use Java code, so it invent all these smaller tasks and its orchestration and dependencies between them to build larger tasks. But with dynamic languages it's easier to just use language. And define only major tasks dependencies.
I guess it depends how you're used to thinking about it. I spend a lot of time reading Makefiles from other compiled software packages. Makefiles will define a rule for each file to be built (or possibly a wildcard rule that builds any number of similar files, for example a rule to build any *.c file), plus a few meta rules for common actions (like "all", "test", "install", "clean"). So that's how I'm approaching Jakefiles as well.
Certainly, as I said before, if you prefer, you can just write a build script of sequential steps; you don't need Jake or any other build system to do that. But using a build system like Jake, you can define tasks and dependencies among them. Certainly it is a common need in a build system to create a directory where the built files will go. All the various tasks that build files need this directory to exist. Jake handles this nicely: define a directory task that will create the build directory, and make every other task depend on it. Except that now the task that creates the build directory appears in Jake's output of the list of available tasks, even though it's not useful for the user to run this separately.
/overtObviousness
--
Did you consider:
JavaScript, Ruby: Compile at run-time. Dependencies resolved at run-time.
CoffeeScript: Compiled. Dependencies resolved at run-time.
Java: Compiled. Dependencies resolved at compile time by the compiler
(and once again at run-time by the jvm).
C: Compiled. Dependencies resolved at compile time external to the compiler.
This is why Make is special for C. It resolves dependencies and compiles
C based on a dependency tree.
Javac does the same for Java. It can be told to only compile classes
that are out of date. Therefore, Ant does not manage a dependency tree.
Rake, Cake, Ant have tasks and dependencies, but the dependencies are
other tasks.
Ant has conditionals you can attach to a task to determine if the task
to run, but that does not amount to dependency management, any more that
stat'ing files in a bash script is dependency management.
--
Alan Gutierrez - @bigeasy
TaskA (dependencies as other tasks or files) ->
Behaviour1
behaviour2
Custom code 3
Custom code 4
behaviour5
Where "behaviour" = predefined standard activity like concat, create
directory, minify, etc
And can be extended.
Ha! iPad just auto corrected "minify" to "Munich"!
Make is necessary for C because C does not resolve its own dependencies.
Java does, so Ant has a conditional attached to a task, but to make that
file dependency requires more XML.
What you're beginning to design is a Make. If the utility would manage a
dependency tree by stating files, then Make is its peer.
The others, Cake, Ant, Rake, the tasks are just methods that you can
name from the command line. No dependency management. (Again, an Ant
user might take issue with me for saying that.)
I'm putting forward not a nomenclature, but the requirements that drove
those systems. If you do end up doing dependency management, that might
be, in itself, a useful algorithm.
--
Alan Gutierrez - @bigeasy
As I don't post here often, I want to clarify that I'm not being combative, nor clever, only terse.
Make is necessary for C because C does not resolve its own dependencies. Java does, so Ant has a conditional attached to a task, but to make that file dependency requires more XML.
What you're beginning to design is a Make. If the utility would manage a dependency tree by stating files, then Make is its peer.
The others, Cake, Ant, Rake, the tasks are just methods that you can name from the command line. No dependency management. (Again, an Ant user might take issue with me for saying that.)
I'm putting forward not a nomenclature, but the requirements that drove those systems. If you do end up doing dependency management, that might be, in itself, a useful algorithm.
--
Alan Gutierrez - @bigeasy
On 1/31/12 5:16 AM, Avi Deitcher wrote:
Whatever philosophical approach we choose and nomenclature we adopt,
and as nice as cake and especially jake are, there still is no system
that has a way to group together a number of internal (to the makefile
equivalent) tasks under external (command-line callable) tasks, and
predefined behaviours, so I can do (pseudo code):
TaskA (dependencies as other tasks or files) ->
Behaviour1
behaviour2
Custom code 3
Custom code 4
behaviour5
Where "behaviour" = predefined standard activity like concat, create
directory, minify, etc
And can be extended.
Ha! iPad just auto corrected "minify" to "Munich"!
On Jan 31, 2012, at 7:42 AM, Alan Gutierrez<alan@prettyrobots.com> wrote:
Ant has tasks and dependencies, just like Cake and Rake.
Did you consider:
JavaScript, Ruby: Compile at run-time. Dependencies resolved at run-time.
CoffeeScript: Compiled. Dependencies resolved at run-time.
Java: Compiled. Dependencies resolved at compile time by the compiler (and once again at run-time by the jvm).
C: Compiled. Dependencies resolved at compile time external to the compiler.
This is why Make is special for C. It resolves dependencies and compiles C based on a dependency tree.
Javac does the same for Java. It can be told to only compile classes that are out of date. Therefore, Ant does not manage a dependency tree.
Rake, Cake, Ant have tasks and dependencies, but the dependencies are other tasks.
Ant has conditionals you can attach to a task to determine if the task to run, but that does not amount to dependency management, any more that stat'ing files in a bash script is dependency management.
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
http://ant.apache.org/manual/Tasks/concat.html
But, the point I'm driving at is that, yes, within a task, that task can
stat files, and act on the timestamps. Javac does this. Some of the
tasks do this. It's the same as if, when you wrote a Cakefile task, you
called fs.stat a bunch of times, and only acted on dirty files.
If you want to call that "management," then we're arguing over
semantics, but you should realize that its no great shakes. Ant will not
short circuit the execution of concat task. It will execute it. The
concat task will stat files, then maybe do something. But, Ant is not
doing the dependency management, the concat task is.
This is not a quibble. Make builds a dependency tree. If a target is out
of date it is built. If a target is not out of date, the code for the
target is not executed. The target code does not check to see if gcc
needs to run, Make does.
That each task is a dependency manager makes for a lot of redundancy in
the Ant code base. Plus, a lot of inconsistent semantics between Ant
tasks. (Which may have smoothed a bit since I last looked at it.) Each
task might stat a handful of files, but it doesn't cascade.
If Make detects that file A is out of date, it knows it has to run C, F,
H, L, and X. Ant runs A, then B, then C, then D, all the way to Z.
Because Make knows what needs to run and in what order, Make can build
in parallel, if the dependency tree permits it. (Yes, Ant does parallel
too, but in the same explicit way that it does dirty checks.)
You could build a helper library for your Cake, Jake, Rake files, with a
function like:
dirty(source, dest, callback)
Where source and dest are globs, and the callback is called with a
boolean, yes or no. Then you could call this from your tasks and have
Ant like dependency management, but it wouldn't feel special. It would
feel like you're just stating files. Ant buries this redundancy by
virtue of XML. You can't really see the redundancy. Ant files are
verbose in any case.
I imagine that if you were to use Ant as a model, you'd find yourself,
at the end of the day, with nothing more than Cake with a file stat
convenience function.
--
Alan Gutierrez - @bigeasy
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
Thanks,Jon--