Bug 32041

Summary: want to remember a value set within a task next time the task is called
Product: Ant Reporter: Ben Temko <benjamin>
Component: CoreAssignee: Ant Notifications List <notifications>
Status: NEW ---    
Severity: enhancement    
Priority: P3    
Version: 1.6.2   
Target Milestone: ---   
Hardware: All   
OS: All   

Description Ben Temko 2004-11-03 15:45:50 UTC
We have a highly distributed project encompassing many jars containing many
components all of which run on many machines across the network.  Remote deploys
are problematic given this architecture.  We currently have a base
"remote-deploy" task which prompts the user via "input" for a remote machine and
directory and uses the "scp" task to deploy there.

I'd really love to be able to remember the value the user enters here so that
the next time the "remote-deploy" task is called it can display the last value
the user gave as a default, and use that value if they just hit "return".  This
way they can tailor their builds to ask them for their first remote-deploy host,
use that until the needed files are deployed there, then give the next
remote-deploy host, then use that until the needed files are there, etc.  None
of these hosts are known until deploy-time, so it cannot be done via properties
in the build file.

Right now my only option is to pass in a default value every time I call the
"remote-deploy" task, and then prompt the user for a value if they want to use a
different value than the default.  It would be fantastic to remember the value
they enter in and use THAT as the default next time I call the task.  

In general, a "property" like entity/task which is truly mutable across the span
of the project (and not just within a target, as in the ant-contrib "variable"
task) so that it can be dynamically altered to suit the conditions of the
progression of the build based on the changing requirements as given by the user
would be phenomenal.  Right now, if a property is only referred to within a
particular Task, its scope is that Task (and any Task it antcalls), and no more
- one you're done with that Task, the property is removed, and any state-ful
information it might contain is gone with it.  

I've been studying the Ant codebase, and I don't think it's enormously difficult
(unless I'm missing something crucial about Ant's architecture (and yes, I know
"properties are non-mutable" is law, but I can't find a clear explanation of the
necessity of this fact on the site)) : a few tweaks to PropertyHelper to add a
new Hashtable to contain the mutable variables, a small tweak to add Hooks for
them, and a tweak to the replaceProperties function so that these variables are
treated as properties are for ${} substitution, and a new class to describe them.  

That, or allow us to subclass PropertyHelper to add these enhancements ourselves
(if we can subclass ProjectHelper, why not PropertyHelper?), looks like it would
do the trick...

Another possibility would be to have static properties in Tasks, so that they
are treated as static variables in a java method would be.  I'll have to
investigate that as well - might be easier than making changes to PropertyHelper...
Comment 1 Alexey Solofnenko 2004-11-03 16:51:16 UTC
It is very easy to write a new task using <scriptdef> to store a property into a file. There is also <loadfile> and <property file=""> tasks for loading properties.
Comment 2 Steve Loughran 2004-11-03 17:26:53 UTC
We dont do mutability because it turns out to work very well for overriding
things in bigger and more complex build process. I'd be leery about adding any
kind of mutability, especially as you probably want persistence of the
information. Save the input to a file, instead.
Comment 3 Ben Temko 2004-11-03 18:55:05 UTC
I must admit to a very deep dissatisfaction to touching the filesystem outside
of the actual compilation process.  I thought of this solution early on but
discarded it as impractical for various reasons (also, it doesn't "feel" right).  

One of those reasons is that we have a common build base file which defines many
tasks.  If multiple users are building various projects (especially if multiple
users are working on several branches of the same project) it can be a pain to
manage the files to which I am trying to maintain user session data.  Sure, you
can always write to the user's home directory, but if I have builds going in
parallel this gets messy as well.  It makes much more sense to me to have a
thread-safe session like object where I can store data generated throughout the
build process, and refer to this data as needed during my large, anastomotic
builds.  Ant does everything else so well, and is so enormously useful, this
seems like a next-logical-step, especially under the context of
user-input-driven builds with the "input" task - that's a pandora's box you
can't close back.

Comment 4 Alexey Solofnenko 2004-11-03 19:27:52 UTC
Why not use build.xml.properties file for storing persistent data - it is 
for the build. There is also an API to store user settings in registry (or 
user home on Unix), you can use it. 
Comment 5 Matt Benson 2004-11-03 19:32:30 UTC
Are you talking about within the life of a single (capital-P) Project?  If so 
you could simply use ant-contrib's <var> task for that.  If you mean within a 
JVM that could reference multiple Project instances, you could still write a 
custom task to set a system property (I think), which should then be copied 
into later-instantiated Project properties collections.  Otherwise I am at a 
loss to understand what you expect of Ant.
Comment 6 Ben Temko 2004-11-03 20:52:36 UTC
Let me try to show you what I mean.  Here's a simplified example:

<property name="default_rhost" value=""/>
  <target name="remote_deploy">
    <input message="Enter remote server [${default_rhost}]: "
    <property name="default_rhost" value="${rd_rhost}">
    <scp task here using user-supplied hostname in "rd_host">


  <import file="basebuild.xml"/>

  <target name="remote_deploy" description="scp files">
    <antcall target="basebuild.remote_deploy">
        <params to specify first set of files fo here>
    <antcall target="basebuild.remote_deploy">
        <params to specify next set of files go here>
    <ant call and so on>

OK, from the top : 

I have basebuild, which is included in projectbuild.  The basebuild file defines
the remote_deploy task which projectbuild uses to deploy several different sets
of files remotely, possible to one or more servers, possibly not, it is not
known until I'm ready for deployment.

1) default_rhost gets set to
2) antcall to basebuild.remote_deploy occurs, the user is asked for input
3) user gives
4) I set the "default_rhost" variable to the user supplied value, which works
    as long as this Target is active.
5) first set of files is deployed to
6) basebuild.remote_deploy Target ends, *** "default_rhost" resets to
   "" *** 
7) second antcall to remote_deploy occurs, the user is asked for input, but the
   "default_rhost" variable doesn't remember what the user put in last time, 
   even though I attempted to set its value within the Target.

I've tried this in several different ways and its always the same.

Changing the value of a property in the scope of a single Target execution works
fine with ant-contrib's Variable, but once the Target has completed, it ** does
not remember ** the last value it had.  If I call that Target again and attempt
to access either an ant-contrib Variable or a Property which only has scope
within that Target (i.e. it has no <property> task outside of the Target to
which it is assigned a value), it is empty.  There is no memory of its last value.

Why is this useful?  In our case, because we want to be able to have the user
re-use a given value several times in a row, rather then potentially mis-typing
an ip address and having files get deployed to the wrong place.  The user gives
a value once (or, in our case, picks a value from a list), and then can re-use
it until he needs to give a different value.  In the interim, *** between Target
calls*, and remembers the last thing the user gave as input.

I hope this is clearer?

Comment 7 Alexey Solofnenko 2004-11-03 21:32:26 UTC
Please avoid using <antcall>&Co tasks. Please use <macrodef> to define 
<remote_deploy> task and specify different parameters to it. 
Comment 8 Matt Benson 2004-11-03 21:59:21 UTC
To elaborate on Alexey's response, the reason your <var> is "scoped" is due to 
the fact that you are using <antcall>.  If you use <macrodef> (which is a 
really great task) you may really be glad you did.
Comment 9 Ben Temko 2004-11-03 22:04:17 UTC
It looks like when using <macrodef> I lose the ability to inherit "depends"
Targets, since <macrodef> is only defining Tasks, not Targets?  I want my
project's Target to have its own list of dependancies in it which are checked
before my project's Target is run, and then to have the <rdeploy> Target I'm
executing via <antcall> have ITS own set of dependancies, which are project
independant, checked before it runs.  

I suppose I could simply add the dependancy Targets, in the proper order, to the
list in <sequential> to happen before my actual desired action takes place to
simulate it, but that's mixing my modes - I would have to convert every
dependancy to a <macrodef>'d Task in this case?

Out of curiosity, why not use <antcall>?  Is it being phased out?
Comment 10 Matt Benson 2004-11-03 22:09:20 UTC
<ant>, <subant> and <antcall> are to be used when you do NOT want to keep the 
results of the target invocations.  This is because they clone the Project 
instance instead of acting upon the current Project.  If you need an <antcall> 
that will return properties your simplest option is to look at some of the 
alternatives ant-contrib has to offer such as <antfetch> and <antcallback>.
Comment 11 Ben Temko 2004-11-03 22:40:07 UTC
I also now remember why I had to use <antcall> - if I want to call the Target
(or even the Task, <macrodef>'d or otherwise defined) multiple times, and still
get user input, I have to use <antcall>, otherwise, once the property is set,
it's set, and that's the end of it, no more user input, as the "addproperty"
portion of the Task sort of kills the fun.  I'm tinkering with using various
combinations of antcontrib's <var> and the <macrodef> Task to do what I need,
but I'm not optimistic yet... I hope I'm wrong!
Comment 12 Matt Benson 2004-11-03 22:50:00 UTC
You can use <var unset="true" /> while you're using ant-contrib stuff...