I'm very new to ANT and have started using it in a Flash Builder project.
When I run the compiler I need to pass in some library paths. The whole thing looks like this:
<mxmlc
file="${SOURCE_DIR}/com/${PACKAGE_NAME}/Main.as"
output="${RELEASE_DIR}/assets/swf/${IDENTIFIER}/main.swf"
locale="${LOCALE}"
static-rsls="false"
accessible="true"
>
<compiler.debug>false</compiler.debug>
<load-config filename="${FLEX_HOME}/frameworks/flex-config.xml"/>
<source-path path-element="${SOURCE_DIR}"/>
<external-library-path file="${FLEX_HOME}/frameworks/libs/player/10.0/playerglobal.swc" append="true"/>
<library-path file="${FLEX_HOME}/frameworks/libs/flash-integration.swc" append="true"/>
<library-path file="${FLEX_HOME}/frameworks/libs/flex.swc" append="true"/>
<library-path file="${FLEX_HOME}/frameworks/libs/utilities.swc" append="true"/>
<library-path dir="${basedir}/libs" includes="*" append="true"/>
</mxmlc>
Basically everything after the output= part could be stored in some fashion and reused for all the other compilations. Is there some way to store a big block of xml in ANT and just call it through a variable reference or something..?
Thank you!
You can use macrodef to solve this problem. Simply define a new macro as the above invocation, but taking file and output as arguments.
Related
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.
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!
To explain it briefly, here is an example:
In build.xml, local-db.xml is imported. And in local-db.xml, there is a target named "warmup" which calls one target of the third file -- local-gr.xml using task.
All the common properties and classpath defs are imported and set in build.xml.
in project.properties:
dest-dir=./destination
in build.xml:
<path id="gr-classpath"> ... </path>
<import file="local-db.xml" />
in local-db.xml:
<ant antfile="local-gr.xml" target="deploy" inheritAll="true" inheritRefs="true" />
In local-gr.xml, there are two targets like this:
<target name="deploy">
<tar ....../>
<foreach list="${list1}" delimiter="," parallel="true" trim="true" param="item" target="deploy-single" />
</target>
<target name="deploy-single">
something using property ${dest-dir} and path "gr-classpath"
</target>
Now here is the problem:
The property ${dest-dir} and path "gr-classpath" can be used in "deploy" because I set inheritAll and inheritRefs, but it can't be used directly in "deploy-single". "inherit" doesn't when the target is called by foreach?
I managed to pass ${dest-dir} to "deploy-single" with the help of the , but I didn't find any way to pass the classpathref "gr-classpath" to "deploy-single".
What I did to work around it was to claim the again in "deploy-single", but I don't like it at all.
Why this happens? What can I do to make it more elegant?
The ant-contrib foreach task doesn't by default propagate all properties and references to it's target. But it does have inheritall and inheritrefs attributes that you can use to make that happen.
<foreach list="${list1}" delimiter="," parallel="true" trim="true"
param="item" target="deploy-single"
inheritall="true" inheritrefs="true"
/>
Dear, I currently face some problem to retrieve the value of a property setted in a foreach loop. Maybe one of you could help me...
The purpose is to check if one file of a folder has been modified since the corresponding jar has been generated. This way I know if I have to generate the jar again.
What I do is to go through the folder with a foreach loop and if one file match my test, set a property to true.
The problem is that my variable doesn't seems to exist after my loop... Here is a simplified code example that has the same problem:
<target name="target">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant-contrib.jar"></taskdef>
<foreach target="setVar" param="var" list="a,b"/>
<echo>myreturn in target: ${env.myreturn}</echo>
<property name="env.myreturn" value="c"/>
<echo>myreturn in second: ${env.myreturn}</echo>
</target>
<target name="setVar">
<property name="env.myreturn" value="${var}"/>
<echo>myreturn in setVar: ${env.myreturn}</echo>
</target>
The result of this code is:
target:
setVar:
[echo] myreturn in setVar: a
setVar:
[echo] myreturn in setVar: b
[echo] myreturn in target: ${env.myreturn}
[echo] myreturn in second: c
BUILD SUCCESSFUL
It seems that the variable is correctly set as it could be printed in the "setVar" target but no way to retrieve value from the calling target.
I also know it's not possible to assign a value to a property twice. But the problem doesn't even occurs... When it'll be the case I could add a check on the value of the property before to assign it to be sure it is not already initialized...
Do you have a clue on the way I can solve my problem ???
Many thanks in advance for your help :)
Try <for> task from ant-contrib instead of <foreach>. The <for> task takes advantage of Ant macro facility that came later. It works faster and is more flexible than the older <foreach> task. You are in the same project context when using <for>. That means properties set in the loop will be visible outside of the loop. Of course, normal rules for properties apply... you only get to set it once... unless you use <var> task from ant-contrib to overwrite or unset previously set properties.
Ah the joys of Ant hacking.
Not sure about your foreach problem, but can you not use the uptodate task for your requirement?
Even if I don't need it anymore thanks to sudocode, I found a solution for my question. Maybe it could be useful for someone else...
A collegue talked about the "antcallback" target of ant-contrib: it allows to return a result from a called target to the calling one. With a combination of "for" target and "antcallback" it is possible to do what I wanted to do:
<target name="target">
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib.dir}/ant-contrib.jar"></taskdef>
<for param="file">
<path>
<fileset dir="../myDirectory" includes="**/*" />
</path>
<sequential>
<antcallback target="setVar" return="retValue">
<param name="file" value="#{file}"/>
</antcallback>
</sequential>
</for>
<echo>result: ${retValue}</echo>
</target>
<target name="setVar">
<property name="retValue" value="${file}"/>
</target>
"file" contains the name of the file in the directory. It is given to the called target as parameter with value "#{file}" ('#' necessary due to "for" target implementation).
At the end of the main target, ${retValue} contains the first value setted by the "setVar" target. No error is thrown when trying to set it multiple times, so it's not necessary to check if variable has already been instantiated before to set it in "setVar" target.
The <foreach> task uses the same logic as <antcall> under the covers, and any proprrties set inside a target invoked by <antcall> do not have scope beyond the execution of that target.
In other words, the env.myreturn property that you define in the setVar target is lost as soon as execution of that target completes.
This sort of scripting really isn't what Ant is designed for. The Ant-contrib library tries to patch up the holes, but it's still bending it way out of shape.
If you need to write such scripts, and want to use Ant tasks to achieve them, have a look at Gradle instead. It's a rather lovely blend of Groovy (for scripting) and Ant (for the tasks).
The other approaches here (<for>, <var>, <groovy>properties.put(....)</groovy>, <property>, <antcallback>) did not work with ANT 1.9.4, so I used the file system similar to this (pseudocode):
<target name="outer">
<for> <antcall target="inner" /> </for>
<loadproperties srcfile="tmpfile.properties" />
<echo message="${outerprop}" />
</target>
<target name="inner">
<!-- did not work: -->
<!--
<property name="outerprop" value="true" />
<var name="outerprop" value="true" />
<groovy>properties.put('outerprop','true')</groovy>
<antcallback target="setouterprop" />
-->
<echo message="outerprop=true" file="tmpfile.properties" />
</target>
Maybe the other approaches did not work because of my <antcall>, but I need it here. (outerprop is initially unset)
I'm rather new to Ant but I have experienced it's quite good pattern to create generic ant targets which are to be called with antcall task with varying parameters.
My example is compile target, which compiles multiple systems using complex build command which is a bit different for each system. By using pattern described above it's possible not to create copy paste code for that compile command.
My problem here is, that I'm not aware of any way to pass return value (for example the return value of compiler) back to target which called the antcall task. So is my approach pathological and it's simply not possible to return value from antcall task or do you know any workaround?
Thanks,
Use antcallback from the ant-contrib jar instead of antcall
<target name="testCallback">
<antcallback target="capitalize2" return="myKey">
</antcallback>
<echo>a = ${myKey}</echo>
</target>
<target name="capitalize2">
<property name="myKey" value="it works"/>
</target>
Output:
testCallback:
capitalize2:
[echo] a = it works
BUILD SUCCESSFUL
One approach is to write out a property to a temp file using "echo file= ...." or PropertyFile task. Then read the property back in where required. Kludge but works.
Ant tasks are all about stuff goes in, side effect happens. So trying to program in terms of functions (stuff goes in, stuff comes out) is going to be messy.
That said what you can do is generate a property name per invocation and store the result value in that property. You would need to pass in a indentifier so you do not end up trying to create copies of the same property. Something like this:
<target name="default">
<property name="key" value="world"/>
<antcall target="doSomethingElse">
<param name="param1" value="${key}"/>
</antcall>
<echo>${result-${key}}</echo>
</target>
<target name="doSomethingElse">
<property name="hello-${param1}" value="it works?"/>
</target>
But I believe the more typical approach -instead of antcalls- is to use macros. http://ant.apache.org/manual/Tasks/macrodef.html
Antcall can be used from the ant-contrib jar task.
You can get a similar behaviour with the keyword "depends".
<?xml version="1.0" encoding="UTF-8"?>
<project name="test" default="main">
<target name="main">
<antcall target="build-system-with-depends" />
<!-- wait for different results -->
<waitfor checkevery="1000" checkeveryunit="millisecond" maxwaitunit="millisecond" maxwait="2000">
<available file="dummy.not.present.file" classname="" property=""></available>
</waitfor>
<antcall target="build-system-with-depends" />
</target>
<target name="build-system-with-depends" depends="do-compiler-stuff">
<echo>$${compiler.result}=${compiler.result}</echo>
</target>
<target name="do-compiler-stuff">
<!-- simulate different return states -->
<tstamp>
<format pattern="yyyyMMddHHmmss" property="compiler.result" />
</tstamp>
</target>
</project>