Jon Cox wrote:
Thanks for all the great feedback on skip proposal:
http://www.nabble.com/Some-ideas-on-path-handling-and-our-command-line-td21768085.html
Lately, I've been working another notion: specifying targets via groovy
regex on the command line. This could be nice in cases where you
don't want to make a bunch of stupid ephemeral targets for ad-hoc
target collections (this can be heplful during testing). The regex
idea isn't very complicated but I wanted to play with a few different
possibilities before asking anybody to look at it in detail.
For that reason, instead of talking about target regexes,
I'd like to raise an issue that occurred to me along the way:
should targets on the command line be treated as "build goals"
or as a series "actions" for Gradle to preform?
Concretely, should the following commands be equivalent or not?
% gradle cow
% gradle cow cow cow
Gnu Make says: Yes; the second command just redundantly states a goal.
Ant says: No; the second command means "execute 'cow' three times".
Currently, Gradle does things Ant's way.
I believe Gradle should handle it Make's way.
I changed this behaviour in the trunk a few days ago, so that Gradle
behaves in the Make way.
At first I thought this subtle distinction really didn't matter all
that much, but now I do: I think "build goal" semantics (like Make)
are preferable to "build action" semantics (like Ant). Besides being
more predictable when it comes to specification via regex, Make's "goal"
semantics for targets lead to greater symmetry in terms of Gradle's
DAG-oriented design, and scale better in terms of human understanding
in cases where the build is complex & tasks aren't idempotent.
I absolutely agree.
I'd be very interested in hearing what others think about this issue.
Cheers,
-Jon
--------------------------------------------------------------------
Gory Details:
--------------------------------------------------------------------
Suppose
task moo dependsOn cow
task egg dependsOn cow
task cow does not depend on anything
If we say:
% gradle cow cow cow
Currently, in Gradle that means:
run the cow task
then run the cow task again
then run the cow task yet again
I believe this should mean "run cow once",
and be entirely equivalent to:
% gradle cow
You could argue either way about the issuance of a warning.
Here are the things I think are worth considering:
[1] Historical precedent: A mixed bag.
Suppose you had the following Makefile:
#----------------------------
.PHONY=(cow)
cow:
echo you said cow
#----------------------------
Then suppose you issued the command:
% make cow cow cow
Gnu make would run cow once.
However, suppose you have the following build.xml:
<!-- ~~~~~~~~~~~~~~~~~~~~~~~ -->
<project default="all">
<target name="cow">
<echo>cow</echo>
</target>
</project>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~ -->
Then suppose you issued the command:
% ant cow cow cow
Ant would run cow three times.
Therefore, I think there isn't a particularly
strong historical president argument either way.
Two commonly-used programs do it differently.
[2] Consistency of metaphor
When many tasks in a graph depend on cow,
cow only gets run once. We don't run cow
more than that because our goal was to build
it, and once we've done that, we're done.
Thus, it seems strange to say that we accept
this reasoning when it comes to the DAG, but
not when it comes to our command-line specification
of what's to be built. This argument seems to lean
in favor of the GNU-make's "do it once" semantics.
Put differently, GNU Make treats the command line as
indicating what your goals are, not like a crippled
scripting language. Ant treats the command line
as a series of call sites (i.e.: cow() cow() cow()).
Build systems are about goals, so the GNU make metaphor
seems to be upheld better by "do it once".
[3] Symmetry of operations
Consider what the following command SHOULD mean:
% gradle moo -cow -cow -cow
The user has indicated "they don't want cow".
Pressing the elevator button over and over
should not matter. Ok already, I won't do cow!
Therefore, it's currently (and correctly)
equivalent to:
% gradle moo -cow
Let's see what gradle does under harsher conditions.
Suppose we've got this build.gradle file:
createTask("moo", dependsOn: "fun") {}
createTask("cow", dependsOn: "fun") {}
createTask("fun") {}
Look at what we get right now:
% gradle -Dskip.fun moo fun
:fun SKIPPED
:moo
:fun SKIPPED
So far so good.
We said we didn't want any 'fun' and so our wish was granted.
Here's an easy one:
% gradle moo
:fun
:moo
Fine, moo dependsOn fun so this is exactly what I'd expect.
But now look at this:
% gradle moo fun
:fun
:moo
:fun
Wait.
I'm all for doubling my pleasure, but should I *really* be
having this much fun? The 'moo' task already implies the
'fun' task, but here I am running 'fun' twice, probably by
accident. If 'fun' was a deeply-nested dependency of the
moo task, it's very easy for a person typing on the command
line to not realize that. I think you could even say that
it's critical for scalability (on the dimension of complexity)
that the user should NOT have to know that somewhere deep
in the guts of the build that 'moo' eventually calls 'fun'.
Hence, explicitly saying 'fun' on the command line should
be harmlessly idempotent. The task should only run once.
Conclusion:
While different from how Ant works, the GNU Make-ish
"do it once" semantics seems preferable because it's:
o Easier for humans to predict the behavior of complex builds
(e.g.: in the case of deep 'fun' task dependencies)
o More symmetric with how everybody agrees negated tasks work
o more congruent with the goal-oriented nature of a build system
(i.e.: command lines should specify goals, not procedures).
Just as the DAG spares me from running fun multiple times when more
than one thing depends on it, and just like I don't need to keep
saying "-Dskip.fun" for each attempt to re-invoke 'fun', I don't
think gradle should treat redundant targets in the command line
as task invocations.
In short, I believe that:
% gradle cow cow cow
should mean:
% gradle cow
The fact that it's not working that way now seems like a bug,
despite Ant's historical president. Because there are fairly
compelling reasons to work GNU Make's way, it's not just a
matter of taste. I believe GNU got this one right.
What do you think?
-Jon
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email