jakejs / jake

JavaScript build tool, similar to Make or Rake. Built to work with Node.js.
http://jakejs.com
Apache License 2.0
1.97k stars 190 forks source link

invoke runs prereqs but not the invoked task #241

Closed toebu closed 10 years ago

toebu commented 10 years ago

With this jake file:

task('foo', [], function() {
    console.log(this.name);
});
task('bar', [], function() {
    console.log(this.name);
});
task('baz', [], function() {
    console.log(this.name);
});

task('deps', ['foo', 'bar', 'baz'], function() {
    console.log(this.name);
});

desc('This is the default task.');
task('default', [], function() {
    console.log(this.name);
    jake.Task['deps'].invoke();
});

When I run the default task I would expect the following output:

default
foo
bar
baz
deps

What I actually get is:

default
foo
bar
baz

All prereqs ran, but the task itself doesn't.

Am I doing something wrong or is this a bug?

toebu commented 10 years ago

Oh yeah, I'm using jake 0.7.7 and tried it on Windows as well as on Ubuntu.

mde commented 10 years ago

Yes, this looks like a bona fide bug. I'm taking a look now.

mde commented 10 years ago

Oh, duh, sorry. Didn't look at this clearly. When you use invoke, you have to manage the asynchrony yourself:

https://github.com/mde/jake#managing-asynchrony-without-prereqs-eg-when-using-invoke

It's not necessarily intuitive, but this means that the containing task needs to be async. What's happening in your example is that "default" is completing immediately after kicking off "deps," because its work is done, and that throws the execution queue off by one.

If you make the "default" task async, and use the evented API, it works as expected:

task('default', {async: true}, function () {
  var self = this
    , t = jake.Task.deps;
  t.on('complete', function () {
    console.log(self.name);
    complete();
  }); 
  t.invoke();
});
toebu commented 10 years ago

Thanks for the quick reply! This is really not intuitive at all, I define all my tasks as sync (or at least that's what it says is the default) and just because I call them with invoke that makes them async?

I would just like to have a simple setup where I define a few tasks and then combine them in other tasks. Using the evented API seems like an awful lot of code for this rather simple use case.

mde commented 10 years ago

Asynchrony can be hard. Calling tasks with invoke doesn't necessarily make them async, but because Jake has to handle both sync and async tasks, the mechanism has to be async by default. If you use Jake's runner and pass an array of prereq task names, Jake will handle the sync/async for you (using whatever flag you use when you define each task), but if you drop to the lower-level API, you have to deal with the async default.