I can create a local property in Ant within a "block" scope.
But how can I now reference the global property within the same block?
<property name="myprop" value="global"/>
...
<sequential>
<local name="myprop"/>
<property name="myprop" value="local"/>
<echo message="my local prop is ${myprop}"/> //<-- this works fine
<echo message="my global prop is ????"/> //<-- HOW?
</sequential/>
No ant-contrib please.
I'd also like to keep this in Ant, not resorting to JS.
The only thing I could think of was "copying" the global property under a different name to be used in this block
<local name="myglobprop"/>
<property name"myglobprop" value="${myprop}"/>
<local name="myprop"/>
<property name="myprop" value="local"/>
<echo message="my global prop is ${myglobprop}"/>
But that is rather unsightly and seems redundant. I am really just looking for an out-of-scope property reference method.
Edit - use case
My attempt to coerce Ant to do delayed expansion:
Let's say I have a property whose value is a combination of several other properties.
<property name="mycmdline" value="${cmd}=${type}"/>
If ${cmd} and ${type} are known before the above property, all is great. However in my case, these values are not defined (no property set). These values only become known at later stage inside a build macrodef.
I have another simple macrodef that will perform delayed expansion:
<property name="mycmdline" value="${cmd}=${type}"/>
...
<macrodef name="property-expand">
<attribute name="name"/>
<attribute name="value"/>
<sequential>
<fail if="#{name}" message="Property #{name} is already set"/>
<property name="#{name}" value="#{value}"/>
</sequential>
</macrodef>
Finally my build macrodef would contain the following snippet:
<local name="cmd"/>
<local name="type"/>
<local name="mycmdline"/>
<property name="cmd" value="#{cmd}"/>
<property name="type" value="#{type}"/>
<property-expand name="mycmdline" value="${mycmdline}"/>
The last line is obviously wrong. I am creating a (now local) property called mycmdline with expanded value of global (now out of scope) ${mycmdline}. The macrodef for delayed expansion works great on it's own, but the problem is that the global ${mycmdline} is out of scope and cannot be referenced.
What I really wanted is:
<property name="mycmdline" value="${cmd}=${type}"/>
...
<!-- In macrodef now -->
<local name="mycmdline"/>
<property-expand name="mycmdline" value="GLOBAL:${mycmdline}"/>
Instead, what I have to do is:
<property name="unexpanded_mycmdline" value="${cmd}=${type}"/>
...
<!-- In macrodef now -->
<local name="mycmdline"/>
<property-expand name="mycmdline" value="${unexpanded_mycmdline}"/>
Why?
It may not look like a lot of difference, but it's about readability. unexpanded_mycmdline and mycmdline are now two different names, when trying to follow the way the value of property gets used through a script, it now makes a disconnected jump from one property name to another (not matter how similar the names may look). The whole unexpanded_ prefix looks out of place and doesn't fit with the rest of the naming conventions, unless I name all my global variables with some prefix, which doesn't make sense either.
Related
Can the token itself be parsed from other values from within the property file?
Is it possible to evaluate the token key, without hardcoding the token? Can the token itself be parsed from other values from within the property file?
For example, if the properties file has the following tokens (test.properties):
module_no = 01
module_code = bb
title_01_aa = ABC
title_02_aa = DEF
title_03_aa = GHI
title_01_bb = JKL
title_02_bb = MNO
title_03_bb = PQR
Contents of build.xml
<?xml version="1.0" encoding="utf-8"?>
<project default="repl">
<property file="test.properties" />
<target name="repl">
<replace file="test.txt" token="module_title" value="title_${module_no}_${module_code}" />
</target>
</project>
Sample content with text:
Welcome to module_title.
The replace task will result in:
Welcome to title_01_bb.
How to achieve this instead?
Welcome to JKL.
This might be very basic, but please do guide me in the right direction. Thank you.
Nested property expansion does not work by default in Ant as described in the documentation:
Nesting of Braces
In its default configuration Ant will not try to balance braces in property expansions, it will only consume the text up to the first closing brace when creating a property name. I.e. when expanding something like ${a${b}} it will be translated into two parts:
the expansion of property a${b - likely nothing useful.
the literal text } resulting from the second closing brace
This means you can't use easily expand properties whose names are given by properties, but there are some workarounds for older versions of Ant. With Ant 1.8.0 and the the props Antlib you can configure Ant to use the NestedPropertyExpander defined there if you need such a feature.
If you check the workarounds link, one solution is to use a macrodef to copy the property:
<property file="test.properties" />
<target name="repl">
<gettitleprop name="titleprop" moduleno="${module_no}" modulecode="${module_code}" />
<replace file="test.txt" token="module_title" value="${titleprop}" />
</target>
<macrodef name="gettitleprop">
<attribute name="name"/>
<attribute name="moduleno"/>
<attribute name="modulecode"/>
<sequential>
<property name="#{name}" value="${title_#{moduleno}_#{modulecode}}"/>
</sequential>
</macrodef>
I'm trying to read a property after updating it using propertyfile task. Something like
<property file="test.properties" />
<echo>before :: ${modules}</echo>
<propertyfile file="test.properties" >
<entry key="modules" type="string" operation="+" value="foo" />
</propertyfile>
<property file="${status.path}/test.properties" />
<echo>after :: ${modules}</echo>.
It doesn't seem to load the second time. But the property file is updated.
You can invoke a new ant runtime with the antcall task that ignores the properties of your main target runtime - just make sure to include inheritAll="false"
<target name="main">
<property file="test.properties"/>
<echo>before :: ${modules}</echo>
<propertyfile file="test.properties">
<entry key="modules" type="string" operation="+" value="foo" />
</propertyfile>
<antcall target="second-runtime" inheritAll="false"/>
</target>
<target name="second-runtime">
<property file="${status.path}/test.properties" />
<echo>after :: ${modules}</echo>
</target>
antcall refrence
As sudocode already mentioned, in Core Ant properties are immutable - for good reasons.
With the unset task from Antelope Ant Tasks you're able to unset all properties set in a file with a one liner :
<unset file="test.properties"/>
afterwards
<propertyfile file="test.properties" >
<entry key="modules" type="string" operation="+" value="foo" />
</propertyfile>
will work.
Hint : the task works only for normal properties, not for xmlproperties.
But there's a simple workararound, simply use <echoproperties prefix="..." destfile="foo.properties"/> and afterwards <unset file="foo.properties"/>
If you don't want to use Antelope for that specific task only, you may write a macrodef or own task with similar features.
For this case, when whole properties file is loaded twice, I suggest using different prefixes for the first and second load. First load with aprefix attribute equal to first. Access the properties with this prefix, that is property foo will be accessible as first.foo. Then save the properties file and load again, but this time without a prefix. You will get modified properties in suitable place.
Without using the prefix the second load will do nothing, as ant prevents properties from overriding. Others pointed that already.
Ant properties are immutable - once set, they are fixed. So reloading the properties file will not refresh the value of a property already set.
this macro allow you to change the property value after fixed one
<macrodef name="set" >
<attribute name="name"/>
<attribute name="value"/>
<sequential>
<script language="javascript">
<![CDATA[
project.setProperty("#{name}", "#{value}");
]]>
</script>
</sequential>
</macrodef>
you can create a new properties file and save the property in the new file.
Provide the reference of the file in the next line.
Done :)
I am trying to move a file (specified by a pattern) to a given location in an Ant macrodef:
<macrodef name="extract">
<attribute name="package"/>
<sequential>
<!-- the path will contain the unique file in extracted regardless of the name -->
<path id="source_refid">
<dirset dir="${dep}/lib/#{package}/extracted/">
<include name="#{package}-*"/>
</dirset>
</path>
<!-- this is not working: properties are immutable -->
<property name="source_name" refid="source_refid"/>
<move
file="${source_name}"
tofile="${dep}/#{package}/"
overwrite="true"
/>
</sequential>
</macrodef>
This will work just once as ${source_name} is immutable.
An option would be to use the variable task but I didn't find a way to assign a refid to a var.
Is there a way to have something similar to local variable in a macrodef? Or (XY problem) is there a better way to solve my problem?
Since Ant 1.8 you can use the local task for this. For example:
<local name="source_name"/>
<property name="source_name" refid="source_refid"/>
Your example is just the sort of thing local is for!
I have a set of nested Ant build files, and I need to control which properties are inherited by each "sub" task. I'm trying to define these as propertysets (to keep the code manageable) but these are not inherited by subtasks, unlike properties.
The example below demonstrates the problem, foo.* get copied into the middle project but not to the bottom project. If I define each property to be inherited explicitly, like bar.*, they get inherited by the bottom project too.
Is there any way to get a group of properties to inherit all the way down, in the same way individual properties do? Without rewriting the sub-processes, is there something else I could try?
[top.xml]
<?xml version="1.0"?>
<project name="test-top">
<property name="foo.1" value="1"/>
<property name="foo.2" value="2"/>
<property name="bar.1" value="1"/>
<property name="bar.2" value="2"/>
<ant antfile="middle.xml" inheritall="false">
<propertyset>
<propertyref prefix="foo."/>
</propertyset>
<property name="bar.1" value="${bar.1}"/>
<property name="bar.2" value="${bar.2}"/>
</ant>
</project>
[middle.xml]
<?xml version="1.0"?>
<project name="test-middle">
<echo>foo ${foo.1} ${foo.2}</echo>
<echo>bar ${bar.1} ${bar.2}</echo>
<ant antfile="bottom.xml" inheritall="false"/>
</project>
[bottom.xml]
<?xml version="1.0"?>
<project name="test-bottom">
<echo>foo ${foo.1} ${foo.2}</echo>
<echo>bar ${bar.1} ${bar.2}</echo>
</project>
[OUTPUT OF ant -f top.xml]
[echo] foo 1 2
[echo] bar 1 2
[echo] foo ${foo.1} ${foo.2}
[echo] bar 1 2
I think Alexander's solution is close. How about this though, doesn't need any change in middle.xml or bottom.xml.
The idea is to use the echoproperties task to 'unroll' the propertyset to individual properties, then to use that in the ant task call.
Before calling middle.xml, write the property set out using something like this:
<echoproperties destfile="myproperties.txt">
<propertyset>
<propertyref prefix="foo."/>
<propertyref prefix="bar."/>
</propertyset>
</echoproperties>
Then make the call to middle.xml:
<ant antfile="middle.xml" inheritall="false">
<property file="myproperties.txt" />
</ant>
Properties supplied to the ant task inherit all the way down as you say, so you only need to change top.xml:
These properties become equivalent to
properties you define on the command
line. These are special properties and
they will always get passed down, even
through additional <ant> tasks with
inheritall set to false (see above).
In top.xml you can create a file with inheritable properties using <propertyfile> task.
Then you can load this file with <property file="..."/>in each of your submodules.
I would like to create a macro as such:
<macrodef name="testing">
<element name="test" implicit="yes"/>
<sequential>
<test/>
</sequential>
</macrodef>
And then use it:
<testing>
<echo message="hello world"/>
</testing>
However, I would like to specify a default for the implicit element... something like:
<macrodef name="testing">
<element name="test" implicit="yes">
<echo message="hello world"/>
</element>
<sequential>
<test/>
</sequential>
</macrodef>
So I can then use it as such:
<testing/>
Except where I want to change the default element.
Is this possible without defining a task via a Java class? So far, I don't see any documentation that indicates how to do it, if so.
Update
I ended up resolving my particular issue by using refid for filesets (which is what I actually was trying to pull into an element). Using the refid, it was simple to just use a macrodef attribute, which CAN have a default value.
Another alternative would be to create a new base macro which uses the element, and then I could have kept my existing macro as using that one... but still, there is no real default mechanism for an element (which would be nice).
So, Simon gets the answer since he's correct! Thanks!
This is not possible based on the nested element element documentation for the macrodef task.
There is a Bugzilla issue open for exactly the functionality you describe, unfortunately it has been open since 2004.
if you define your macrodef as:
<macrodef name="testing">
<element name="additional" optional="true"/>
<sequential>
<echo message="hello"/>
<additional/>
</sequential>
</macrodef>
the following invocation:
<target name="testing-call">
<mylib:testing/>
<mylib:testing>
<additional>
<echo message="world!"/>
</additional>
</mylib:testing>
</target>
will output:
[echo] hello
[echo] hello
[echo] world!