Ant-like build system?

482 views
Skip to first unread message

deitch

unread,
Jan 25, 2012, 4:02:04 AM1/25/12
to nodejs
Are there any active projects for an ant-like build.xml system using
native node?

I have a number of projects that require some sort of "when complete,
run a build process". In my early years, it was all make/nmake/gmake,
then ant/build.xml, which is much cleaner.

But with node, and building JS projects, why should I depend on Java
to build them? Much nicer to have some build.json that runs off node
to do my build. node can do everything ant can, or close to it.

I have some stuff hacked together, but if there is some standard, love
to hear about it before I spend more time.

Anything out there?

Honigbaum

unread,
Jan 25, 2012, 6:13:12 AM1/25/12
to nod...@googlegroups.com
hi deitch

maybe cake and cakefiles are what you looking for?
http://jashkenas.github.com/coffee-script/documentation/docs/cake.html

Torben

Jeff Barczewski

unread,
Jan 25, 2012, 10:36:45 AM1/25/12
to nod...@googlegroups.com
Jake is the project you should look at. 

It isn't XML but that is a good thing. js is a dynamic language so just define your rules in it.

Zachary Scott

unread,
Jan 25, 2012, 11:00:23 AM1/25/12
to nod...@googlegroups.com
There's also cake[1] for CoffeeScript users, which is a quaint build
system[2] that's used for building and testing CS itself.

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

Avi Deitcher

unread,
Jan 25, 2012, 11:33:23 AM1/25/12
to nod...@googlegroups.com
jake doesn't look too bad. It assumes you write your tasks in JS. I still prefer having descriptive rather than programmatic for common tasks. 

I agree with you, I don't like using XML, would much prefer json, but still descriptive. 98% of the tasks one does in builds are repetitive, can easily be descriptive.

I will see if I can use jake, or something else. Would be nice if there were 

--
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



--
Avi Deitcher
a...@deitcher.net

Honigbaum

unread,
Jan 25, 2012, 6:08:30 AM1/25/12
to nodejs
Hi deitch,

maybe cake/cakefiles are what you looking for?
http://jashkenas.github.com/coffee-script/documentation/docs/cake.html

Torben

mde

unread,
Jan 25, 2012, 4:02:44 PM1/25/12
to nod...@googlegroups.com
Avi,

I'm the author of Jake. Let me know how it works for you, or if you have any questions or problems. It's great to get feedback.

Ryan Schmidt

unread,
Jan 26, 2012, 1:18:56 AM1/26/12
to nod...@googlegroups.com

On Jan 25, 2012, at 10:00, Zachary Scott wrote:
> On Wed, Jan 25, 2012 at 10:36 AM, Jeff Barczewski wrote:
>> Jake is the project you should look at. [snip]
>
> There's also cake[1] for CoffeeScript users, [snip]

I also am starting to use Jake. Note that you can write your Jakefiles in CoffeeScript if you prefer that over JavaScript.


deitch

unread,
Jan 26, 2012, 4:43:07 AM1/26/12
to nodejs
That definitely has more appeal, to be as broad as possible.

deitch

unread,
Jan 26, 2012, 4:54:39 AM1/26/12
to nodejs
Hey Matthew,

I like the idea behind it, seems similar to Ant/make, but using more
of a JS-ey (can I used that word?) structure.

I will read through it, then see if I can replace one of my build
processes with jake. Hopefully time in the next week.

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).

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:
- minification
- jslint
- 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.
- zip - don't know if this is hard or easy
- delete - to delete (i.e. clean) a file or directory

3) Perhaps the best would be a plugin structure (if it doesn't have
it, I didn't see in the README, but maybe I missed it). That way I
could easily build one of those as a plugin, and submit it, as opposed
to going to the source itself. I think you will find it grows very
quickly if you give people the ability to extend it.

Overall, Matthew, really cool, save me from building it. Wish I had
seen this a while back, might have nominated you for the #jsconf
speakers list. :-)

Ryan Schmidt

unread,
Jan 26, 2012, 6:20:08 AM1/26/12
to nod...@googlegroups.com

On Jan 26, 2012, at 03:54, deitch wrote:

> 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});

deitch

unread,
Jan 26, 2012, 7:17:10 AM1/26/12
to nodejs
>
> > 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.

No question it works. That was just a long-term maintainability and
cleanliness suggestion.

>
> > 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.

True, but modular is nice, and writing DRY is even better. There is a
reason some tasks became standard in make and ant.

>
> > - 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');
>
> });

Perfect, love to see it as a standard task.

> > - 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.

Yep. And since jslint itself is written in JS (Crockford's version,
anyways), should be fairly easy.

deitch

unread,
Jan 26, 2012, 7:54:24 AM1/26/12
to nodejs
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?

deitch

unread,
Jan 26, 2012, 8:12:55 AM1/26/12
to nodejs
Let me give an example. If I want to have a task that concats multiple
files in order, but only does so if at least one of the dependencies
is newer than the target, in ant I do something like:

<concat destfile="${target}">
<fileset file="${src}/file1"/>
<fileset file="${src}/file2"/>
<filelist dir="${src}" files="${srcfiles}"/>
<fileset file="${src}/filelast"/>
</concat>

It would be nice (actually much nicer than ant) if I could do the same
thing in jake:

concat(target,[srcfile1,srcfile2,...,filelast]);

And it would *both* check the timestamps and do the concatenation. For
now, I need to do something a little messy:

desc("standard concatenation task");
task("concat",function(target,src){
var stream;
src = src || [];
if (target) {
// open the file for writing
stream = fs.createWriteStream(target,{flags:'w'});
src.forEach(function(file){
// write each file in order to the stream
stream.write(fs.readFileSync(file));
});
stream.end();
stream.destroy();
}
});


// combine the files
var files = [file1, file2,...,filelast], target = somefile, concat =
jake.Task["concat"];
file(target,files,function(){
// concat here
concat.invoke.apply(concat,target,files);
});

Matthew, give us the ability to do plugins and we will contribute...

Ryan Schmidt

unread,
Jan 26, 2012, 5:58:13 PM1/26/12
to nod...@googlegroups.com

On Jan 26, 2012, at 06:54, deitch wrote:

> 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 )

deitch

unread,
Jan 27, 2012, 6:02:16 AM1/27/12
to nodejs
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.


On Jan 26, 3:12 pm, deitch <a...@deitcher.net> wrote:
> Let me give an example. If I want to have a task that concats multiple
> files in order, but only does so if at least one of the dependencies
> is newer than the target, inantI do something like:
>
>                 <concat destfile="${target}">
>                         <fileset file="${src}/file1"/>
>                         <fileset file="${src}/file2"/>
>                         <filelist dir="${src}" files="${srcfiles}"/>
>                         <fileset file="${src}/filelast"/>
>                 </concat>
>
> It would be nice (actually much nicer thanant) if I could do the same
> > > I like the idea behind it, seems similar toAnt/make, but using more
> > > of a JS-ey (can I used that word?) structure.
>
> > > I will read through it, then see if I can replace one of my build
> > > processes with jake. Hopefully time in the next week.
>
> > > 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).
>
> > > 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:
> > > - minification
> > > - jslint
> > > - replace - this is a workhorse ofAnt. This way, you have some string

Ryan Schmidt

unread,
Jan 28, 2012, 8:48:32 AM1/28/12
to nod...@googlegroups.com

On Jan 27, 2012, at 05:02, deitch wrote:

> 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?

Avi Deitcher

unread,
Jan 28, 2012, 12:02:37 PM1/28/12
to nod...@googlegroups.com
How about if I post part of an old build.xml? Maybe I am going about this with a wrong paradigm, but here is what I am doing in one case.

<target name="core" depends="init">
<!-- combine the files into build/ and minify -->
<concat destfile="${build}/${expfile}">
<fileset file="${src}/00license.js"/>
<fileset file="${core}/000pre.js"/>
<filelist dir="${core}" files="${srcfiles}"/>
<fileset file="${core}/000post.js"/>
</concat>
<jsmin destdir="${build}" srcfile="${build}/${expfile}" destfile="tmp-minfile.js">
</jsmin>
<!-- Make sure minified file includes the license -->
<concat destfile="${build}/${minfile}">
<fileset file="${src}/00license.js"/>
<fileset file="${build}/tmp-minfile.js"/>
</concat>
<!-- eliminate the temporary minfile -->
<delete file="${build}/tmp-minfile.js"/>

<!-- put the version number in the files -->
<replace file="${build}/${minfile}" token="@@version@@" value="${version}"/>
<replace file="${build}/${expfile}" token="@@version@@" value="${version}"/>

<!-- Include the built file in the test directory -->
<copy todir="${test}" verbose="true">
   <fileset dir="${build}" includes="${minfile},${expfile}"/>
</copy>
<!-- Include the built file in the samples directory -->
<copy todir="${samples}" verbose="true">
   <fileset dir="${build}" includes="${minfile},${expfile}"/>
</copy>


</target>

To summarize, I do the following in sequence:

1) concat - take a bunch of files and build a single output file *if* any of the dependencies has changed
2) minify the concated output
3) concat - add the license file to the minified file
4) replace - add versions
5) copy - copy the minified and non-minified versions to the test/ and samples/ directories

I guess what is bothering me is twofold:
a) I need to do a lot of repetition for concat() and minify() because they are not readily available, along with the file list, because file() *just* creates the dependency, not the actual concat(), even though it is the same file list
b) I need to do a lot of callbacks within callbacks to do the above, which is messy (and a reason promises were created).

I am frustrated because I love the idea of jake, just want to round it out a bit. Would even help contribute if Matthew opens it up.

FYI, I took the above (just one task in an ant build.xml) as a first conversion to jake, as it is a purely js project, and uses Java only to do the build.
Thoughts?



deitch

unread,
Jan 28, 2012, 12:19:09 PM1/28/12
to nodejs
Oy, sorry about the poor formatting. I do not know how to make Google
Groups format xml correctly.

deitch

unread,
Jan 28, 2012, 2:42:29 PM1/28/12
to nodejs
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?

On Jan 28, 7:02 pm, Avi Deitcher <a...@deitcher.net> wrote:

deitch

unread,
Jan 28, 2012, 2:42:47 PM1/28/12
to nodejs
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?


Ryan Schmidt

unread,
Jan 29, 2012, 2:23:33 PM1/29/12
to nod...@googlegroups.com

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.


Ryan Schmidt

unread,
Jan 29, 2012, 2:29:14 PM1/29/12
to nod...@googlegroups.com

On Jan 28, 2012, at 13:42, deitch wrote:

> 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.

Alexey Petrushin

unread,
Jan 29, 2012, 2:49:02 PM1/29/12
to nod...@googlegroups.com
One reason for Ant special DSL is because Java is a compiled language with very limited DSL capabilities.
So, Ant tries to compensate it with it's own custom XML-DSL.

But I doubt for usage of Ant-like system in languages like Ruby or CoffeeScript (and almost in JavaScript).

Both rake (ruby) and cake (coffee) are just superior simply, small and clear compared to Ant XML.
I believe trying to use XML-ant-clone in such environment is a wrong way.
Message has been deleted
Message has been deleted

Avi Deitcher

unread,
Jan 30, 2012, 9:15:15 AM1/30/12
to nod...@googlegroups.com
Ah, that explains it.

--
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



--
Avi Deitcher
a...@deitcher.net

deitch

unread,
Jan 30, 2012, 9:21:59 AM1/30/12
to nodejs
Alexey, you are right, that *one* of the reasons for the DSL in ant is
because Java is a compiled language, so a parsed DSL makes things
easier. Same for Makefile, etc.

But there are two other strong reasons:
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.

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.

There are two layers here: tasks to be done and groupings (aka
targets). Targets are high-level tasks that I can see from the command-
line, and have dependencies. Tasks are small bits of work that are
organized together. This isn't namespacing, it is grouping together
for a higher level of the stack.

On Jan 29, 2:49 pm, Alexey Petrushin <alexey.petrus...@gmail.com>
wrote:

deitch

unread,
Jan 30, 2012, 9:24:27 AM1/30/12
to nodejs
BTW, if my paradigm is off here, and there is some better paradigm
that is different than the Ant/Make world, by all means open to it.

Alexey Petrushin

unread,
Jan 30, 2012, 12:45:37 PM1/30/12
to nod...@googlegroups.com
> Modularity: even in a scripted language, if I concatenate a file on 
a regular basis
File operations isn't the problem of the build system, it's a problem of FS API design. 
Take look for example this API for working with file system http://alexeypetrushin.github.com/vfs (Ruby, not Node, but I believe it's totally possible create almost identical API for Node) 

    concat("output.js",["file1.js","file2.js","fi e3.js"]); 

in VFS (from the link above) this will be

    ['file1.js;, 'file2.js', 'file3.js'].map{|n| n.to_file.read}.join.write_to "output.js"

It's universal FS API, You can use it anywhere and don't bother to remember special build commands, usable only inside build file.

> Organization ... There are two layers here: tasks to be done and groupings (aka 
targets)
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.

Ryan Schmidt

unread,
Jan 30, 2012, 12:55:31 PM1/30/12
to nod...@googlegroups.com
On Jan 30, 2012, at 08:21, deitch wrote:

> 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.

Ryan Schmidt

unread,
Jan 30, 2012, 1:03:39 PM1/30/12
to nod...@googlegroups.com

On Jan 30, 2012, at 11:45, Alexey Petrushin wrote:

> 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.


Alexey Petrushin

unread,
Jan 30, 2012, 2:11:58 PM1/30/12
to nod...@googlegroups.com
Interesting naming thing - take a closer look to the naming: "Build System", "Tasks and Dependencies"

Ant, Jake - is more like "Build System"
Rake, Cake - is more like "Tasks and Dependencies"

"Tasks and Dependencies" -  is unique and important concept having a value on its own. No mather is it Java, Ruby or JavaScript.

Build System in additon to the "Task and Dependencies" also provides additional tools for working with common build commands.

So the question is - do You need additional tools? And it seems that it's a question of personal preferences :).

Tim Caswell

unread,
Jan 30, 2012, 2:16:23 PM1/30/12
to nod...@googlegroups.com
Considering that JavaScript doesn't have to be compiled before it can
be run changes things a lot compared to Java.

/overtObviousness

Avi Deitcher

unread,
Jan 30, 2012, 4:15:18 PM1/30/12
to nod...@googlegroups.com
Yes, I think this really sums it up. I want a build system, one that has targets, lots of common tasks predefined, and an ability to call them. you want something different, like tasks and dependencies.

Jake seems much more T&D right now than Build Sys. Can it be extended to support T&D both? That is where I would like to take it. Matthew, you want to weigh in?
--

Alan Gutierrez

unread,
Jan 31, 2012, 12:41:54 AM1/31/12
to nod...@googlegroups.com
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.

--
Alan Gutierrez - @bigeasy

Avi Deitcher

unread,
Jan 31, 2012, 5:16:18 AM1/31/12
to nod...@googlegroups.com
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"!

Alan Gutierrez

unread,
Jan 31, 2012, 6:21:59 AM1/31/12
to nod...@googlegroups.com
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

Avi Deitcher

unread,
Jan 31, 2012, 6:36:55 AM1/31/12
to nod...@googlegroups.com
A- you were not being combative, clever or contrary, but clarifying (it must be letter C day on my kids' Sesame Street)
B- If it is important, then by all means, be any of the above. :-)

I disagree with you about Ant - it does do dependency management, not only targets to other targets, but also some of the tasks can have explicit file dependencies (e.g. concat) - but ok with the rest.

I want:
1) tasks that can be called at any time, even from within targets, like Ant has
2) many more predefined tasks (my list earlier) 
3) targets as large groupings of tasks and functions that are callable from the command-line

I want the best of jake meets ant.

Didn't want to duplicate efforts, but looks like I need to either build it or extend jake or cake. Was hoping jake was it. I will see if it is easier to extend jake or just build it. 

On Tue, Jan 31, 2012 at 1:21 PM, Alan Gutierrez <al...@prettyrobots.com> wrote:
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

Alan Gutierrez

unread,
Jan 31, 2012, 7:22:04 AM1/31/12
to nod...@googlegroups.com
Makes me sad to think of Ant.

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

deitch

unread,
Jan 31, 2012, 4:38:56 PM1/31/12
to nodejs
@Alan, why does it make you sad to think of Ant?

You are right, Ant itself does not do any of the tasks; Ant just
builds a dependency tree of targets to other targets. But Ant tasks
(written as separate or bundled plugins) do all of the stuff like
concatenating a file, or minifying, or compiling, or running an exec,
etc.

So using your nomenclature, I am missing not the dependency tree, for,
indeed, jake (and cake) has that, using only task(). It has all I need
for dependency tree.
I am missing the bundled tasks that I can run. I want to have
directory() and file() not as a target, but as a task that can be run
inside the target, as well as some other tasks that are basic
repetitive, like minify(), concat(), etc.

For the last comment, if I were to use Ant as a model, I would find
myself with jake/cake, with many convenience functions.

Jonathan Bomgardner

unread,
Jan 31, 2012, 5:26:14 PM1/31/12
to nod...@googlegroups.com
Just wanted to chime in and point you at the following:
 
 
It's pretty rough but was patterned after Ant.... I use it a lot myself and it was specially built for use by the JxLib ui project (http://jxlib.org). I believe it has most of what you're looking for and is easily extendable with more tasks...
 
Thanks,
Jon B.

Avi Deitcher

unread,
Jan 31, 2012, 5:32:07 PM1/31/12
to nod...@googlegroups.com
Looks interesting, love to check it out (I really don't relish building one myself). Is there any documentation? 
is blank.

--
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

Jonathan Bomgardner

unread,
Jan 31, 2012, 5:53:48 PM1/31/12
to nod...@googlegroups.com
ah, the readme is blank indeed.... I'll get some stuff written up on it as soon as I can. I've been so busy coding that I forgot to write it up. I'll bump that a bit closer to the top of the to do list and will post here once it's done.
 
In the meantime, you can see how the targets get written and actually run it by looking at the jxlib project (https://github.com/JxLib/JxLib). The targets are all kept in the "builder" directory and you can read a quick "how-to" on getting it to build at http://blog.solagratiadesigns.com/jxlib-a-brief-introduction-to-3-1-1/ 
 
Any feedback once the documentation is completed would be appreciated.
 
Thanks,
 
Jon

deitch

unread,
Jan 31, 2012, 9:07:56 PM1/31/12
to nodejs
The build.xml is the build target file? Is it that closely ant-
modeled?

On Jan 31, 5:53 pm, Jonathan Bomgardner <jo...@comcast.net> wrote:
> ah, the readme is blank indeed.... I'll get some stuff written up on it as
> soon as I can. I've been so busy coding that I forgot to write it up. I'll
> bump that a bit closer to the top of the to do list and will post here once
> it's done.
>
> In the meantime, you can see how the targets get written and actually run
> it by looking at the jxlib project (https://github.com/JxLib/JxLib). The
> targets are all kept in the "builder" directory and you can read a quick
> "how-to" on getting it to build athttp://blog.solagratiadesigns.com/jxlib-a-brief-introduction-to-3-1-1/

Jonathan Bomgardner

unread,
Jan 31, 2012, 9:33:51 PM1/31/12
to nod...@googlegroups.com
sorry, no... the build.xml is a leftover from the previous ant-based build system. The current system is based on the build.sh file that is used to kick off the node-based build process.

Thanks,
Jon

Avi Deitcher

unread,
Jan 31, 2012, 9:41:11 PM1/31/12
to nod...@googlegroups.com
Ah, OK. I thought maybe you had replicated it exactly.

Looks like there is some builder/config.js, and builder/tasks/ subdir dependencies?


Thanks,
Jon

--

Jonathan Bomgardner

unread,
Feb 1, 2012, 11:47:23 AM2/1/12
to nod...@googlegroups.com
right, the config.js gives you a place to store paths to different locations and non-node dependencies and the targets subdirectory holds the actual targets that the builder runs. The targets can have interdependencies and there is even a way to run targets with dynamic parameters.

I'm working on docs but it may be a while before they're done due to some family stuff that I need to take care of.... I'll let you know when those are done but feel free to continue to ask questions and I'll answer them as/when I can.

Thanks,
Jon

Juraj Vitko

unread,
Feb 2, 2012, 5:58:24 AM2/2/12
to nodejs
I'm also looking for a new build tool (Ant is what I currently use,
and it's so 2001).

Then I remembered that the proper way to go about this is to first
check the Node's wiki Modules page on Github, section "build and
deployment": https://github.com/joyent/node/wiki/modules#wiki-build-and-deployment

Also, there's the node-waf tool (installed with Node), used AFAIK to
build native libs for Node, but should be pretty usable for anything.
You can see the grammar here: http://docs.waf.googlecode.com/git/apidocs_16/index.html
 (I'm just about to check this out more closely, so I might be wrong.)

J.

On Jan 25, 11:02 am, deitch <a...@deitcher.net> wrote:
> Are there any active projects for an ant-like build.xml system using
> native node?
>
> I have a number of projects that require some sort of "when complete,
> run a build process". In my early years, it was all make/nmake/gmake,
> then ant/build.xml, which is much cleaner.
>
> But with node, and building JS projects, why should I depend on Java
> to build them? Much nicer to have some build.json that runs off node
> to do my build. node can do everything ant can, or close to it.
>
> I have some stuff hacked together, but if there is some standard, love
> to hear about it before I spend more time.
>
> Anything out there?

deitch

unread,
Feb 3, 2012, 6:59:51 AM2/3/12
to nodejs
Not enamoured of waf; seems too complex/terse a language, feels like
reading Erlang while doing JS. I see why you need it to build node,
which is, after all, built in C. But to build external projects after
node is in place?

I really like jake's syntax, very much. It just doesn't have (a)
enough built-in functions; (b) good separation between functions and
tasks/targets.

Jonathan Bomgardner

unread,
Feb 5, 2012, 1:53:23 AM2/5/12
to nod...@googlegroups.com
Basic documentation is now available for node-builder via the readme file on github. I'll flesh this out as I have more time but this should get you started:


Thanks,
Jon
Reply all
Reply to author
Forward
0 new messages