I have an XSL style sheet that merges external documents, this way
<xsl:copy-of select="document('snippets.xml')/snippets/xxxx/form"/>
I would like that the XSLT Ant build task rebuilds if the file or any of its dependencies changed.
The current Ant task looks like this
<xslt basedir="xxxx/pages/${doc.locale}"
destdir="xxxx/dir/${doc.locale}"
includes="*.xml"
excludes="snippets.xml"
style="build/xxxx/${doc.locale}/myStyle.xsl">
<param name="lang" expression="${doc.locale}"/>
<xmlcatalog refid="docDTDs"/>
Basically I would like to rebuild if the snippets.xml document is changed.
Ant has the uptodate task to check if a target is up-to-date, file modtime-wise, from a set of source files. I'm not completely clear on what your dependency is since the XSLT task could create multiple files (resulting in multiple targets), or if it creates a single file. One of your comments imply a single file.
The following is one way to use uptodate. You basically use the task to set a property that can then be put in the unless attribute of a target:
<property name="file.that.depends.on.snippets"
location="some/path"/>
<property name="snippets.file"
location="xxxx/pages/${doc.locale}/snippets.xml"/>
<target name="process-snippets"
unless="snippets.uptodate"
depends="snippets-uptodate-check">
<xslt basedir="xxxx/pages/${doc.locale}"
destdir="xxxx/dir/${doc.locale}"
includes="*.xml"
excludes="snippets.xml"
style="build/xxxx/${doc.locale}/myStyle.xsl">
<param name="lang" expression="${doc.locale}"/>
<xmlcatalog refid="docDTDs"/>
</xslt>
</target>
<target name="snippets-uptodate-check">
<uptodate property="snippets.uptodate"
targetfile="$file.that.depends.on.snippets">
<srcfiles dir="xxxx/pages/${doc.locale}"
includes="*.xml"
excludes="snippets.xml"/>
</uptodate>
</target>
The XSLT task should do this for you by default. It has an optional "force" attribute
Recreate target files, even if they
are newer than their corresponding
source files or the stylesheet
which is false by default. So by default, unless you override with the "force" attribute, dependencies are checked by the XSLT task.
Related
To eliminate redundancy in my ant build.xml files, I decided out-factor the repeated targets into mytargets.xml file, publish it to to the artifact repository, and then import it in the following way:
<import>
<url url="http://mycompany.com/artifacts/mycompany.com/mytargets/1.2.3/mytargets-1.2.3.xml"/>
</import>
There are two things I don't like about this approach:
mytargets-1.2.3.xml never appears anywhere on the disk where I can easily look at it.
I absolutely need access to http://mycompany.com/artifacts in order to do anything in the project---it completely undermines offline work.
So, I tried creating a setup target to fetch a local copy of mytargets.xml and adjusted my <import> to use this local copy.
<import file="${basedir}/antlib/mytargets/mytargets.xml"/>
However, as you have probably guessed, I cannot even execute my setup my target after adjusting my <import> in this way because the file does not yet exist:
Buildfile: /home/me/myproject/build.xml
BUILD FAILED
/home/me/myproject/build.xml:265: Cannot find /home/me/myproject/antlib/mytargets/mytargets.xml imported from /home/me/myproject/build.xml
Adding optional="true" to the <import> only defers the problem to the first target that depends upon mytargets.xml.
I looked at https://ant.apache.org/manual/Types/antlib.html, but this approach does not appears to permit you to define a <target>.
So, how does someone share bits of ant XML across multiple projects? Am I already doing it the 'one true way'? Or, is there a better way?
If you're mainly just trying to avoid download the remote copy when you have a local copy already available, you can try something like this:
<condition property="mytargets.xml.available">
<available file="${basedir}/antlib/mytargets/mytargets.xml" />
</condition>
<target name="setup" unless="mytargets.xml.available">
<get
src="http://mycompany.com/artifacts/mycompany.com/mytargets/1.2.3/mytargets-1.2.3.xml"
dest="${basedir}/antlib/mytargets"
/>
</target>
<target name="main" depends="setup">
<import file="${basedir}/antlib/mytargets/mytargets.xml" />
...
</target>
So, it seems to me that <target> is inherently local and not intended for reuse. On the other hand, <macrodef> appears intended for reuse.
Here is the 'library', mymacros.xml:
<?xml version="1.0"?>
<antlib>
<macrodef name="mymacro">
...
</macrodef>
</antlib>
Here is the client, myproject/build.xml:
<?xml version="1.0"?>
<project name="myproject">
<target name="mytarget">
<mymacro/>
</target>
<taskdef file="mymacros.xml"/>
</project>
Unlike <import> and <include>, <taskdef> will not cause the build to fail immediately if mymacros.xml is missing, which gives you the opportunity to download it.
I have a build.xml that should receive dynamically parameters to the depends field.
I define this parameter in some other app.xml such as:
ops=op1, op2, op3,op4,op5,.... opn
then I import this app.xml into build.xml and want to use the parameter ops there.
<project name="Project" basedir="." default="help">
<target name="test" depends="{$ops}" description="executea series of commands in ant">
<echo message="batch operation job done. tasks = {$ops}"/>
</target>
</project>
How can I pass a parameter from one ant file to another?
The depends parameter does not take properties.
Ant uses a dependency matrix to determine what should be built and in what order. This matrix is calculated before any part of the build file itself is executed, so properties aren't even set when this is done.
What are you trying to accomplish? Maybe if we have a better idea what you want, we can help you with it. Ant isn't a scripting language like BASH or Python.
As already mentioned, you can't put properties into the Depends field. However, if you are setting a property, you can use it in the If field. Example
<project name="appProject">
<target name="test" depends="target1,target2,target3" description="execute series of commands"/>
<target name="target1" if="do.target1">
<echo message="Target1 executed." />
</target>
<target name="target2" if="do.target2">
<echo message="Target2 executed." />
</target>
<target name="target3" if="do.target3">
<echo message="Target3 executed." />
</target>
</project>
Then you set in your build.xml the given target flag do.target1, do.target2 or do.target3 and it gets executed. Basically what you wanted to have. In the If field properties are only checked for value. Also, you don't have to use the ${ } construction for the properties.
I am currently deploying an oracle adf project using ojdeploy. Unfortunately the packaging of the jar is not as it should be.
So I have images in the paths
lookAndFeel.jar/skins/lightsOff/images
lookAndFeel.jar/skins/lightsOn/images
which I need to merge with
lookAndFeel.jar/META-INF/adf/skins/lightsOff/images
lookAndFeel.jar/META-INF/adf/skins/lightsOn/images
Is there a way to do this with ant?
edit
I read Top 15 Ant Best Practices Point 13, where they state, that I should use zipfileset. But I was not able to do it that way, since my required files are already in the jar. The initial jar creation is done via ojdeploy therefor I don't have an influence on the initial structure of the jar.
I'm trying to understand what the issue is:
Did you create this jar?
Are you merging new data or moving it around?
As others have stated, you need to unjar, munge the results, and then jar it back up. No biggie:
<property name="target.dir" value="${basedir}/target"/>
<property name="work.dir" value="${target.dir}/work"/>
<property name="munge.dir" value="${work.dir}/munge"/>
<target name="munge.jar">
<unjar src="${jar.file}"
dest="${munge.dir}"/>
<here be dragons.../>
<delete file="${jar.file}"/>
<jar destfile="${jar.file}
basedir="${munge.dir}"/>
</target>
Not being 100% sure what you want, you'll have to fill out the <here be dragons.../> part. You'll move, rename, shuffle, delete, and add to your heart's content.
Another possibility is to use the <mapper/> sub-entity to reformat your jar as you unjar it.
<target name="munge.jar">
<unjar src="${jar.file}"
dest="${munge.dir}">
<mapper type="glob"
from="skins/**/*"
to="META-INF/adf/skins/**/*"/>
</unjar>
<delete file="${jar.file}"/>
<jar destfile="${jar.file}
basedir="${munge.dir}"/>
</target>
I haven't tested the above, but it'll give you an idea.
I have a set of build files, some of them calling others -- importing them first. End of line builds may have or may not have a specific target (e.g. "copyother"). I want to call it from my main build file if that target is defined within the end-of-line build script. How can I do it?
Part of the calling script:
<!-- Import project-specific libraries and classpath -->
<property name="build.dir" value="${projectDir}/build"/>
<import file="${build.dir}/build_libs.xml"/>
...
<!-- "copyother" is a foreign target, imported in build_libs.xml per project -->
<target name="pre-package" depends=" clean,
init,
compile-src,
copy-src-resources,
copy-app-resources,
copyother,
compile-tests,
run-junit-tests"/>
I do not want every project to define "copyother" target. How can I do a conditional ant call?
I'm guessing you aren't importing the "other" build scripts into your main build.xml. (Because that wouldn't work. Ant treats imports as local.)
At the same time, you are using depends and not ant/ant call so maybe you are importing them, but one at a time.
You can't do what you want in native Ant. As you noted testing for a file is easy but a target is not. Especially if that other project isn't loaded yet. You definitely have to write a custom Ant task to accomplish what you want. Two avenues:
1) Call project.getTargets() and see if your target is there. This involves refactoring your script to use ant/antcall instead of pure depends, but doesn't feel like a hack. Writing a custom Java condition isn't hard and there is an example in the Ant manual.
2) Add a target to the current project if not already there. The new target would be a no-op. [not sure if this approach works]
For the same of completeness. Another approach is to have some target for checking the target.
The approach is discussed here: http://ant.1045680.n5.nabble.com/Checking-if-a-Target-Exists-td4960861.html (vimil's post). Check is done using scriptdef. So it is not that different from other answer(Jeanne Boyarsky), but script is easy to add.
<scriptdef name="hastarget" language="javascript">
<attribute name="targetname"/>
<attribute name="property"/>
<![CDATA[
var targetname = attributes.get("property");
if(project.getTargets().containsKey(targetname)) {
project.setProperty(attributes.get("property"), "true");
}
]]>
</scriptdef>
<target name="check-and-call-exports">
<hastarget targetname="exports" property="is-export-defined"/>
<if>
<isset property="is-export-defined"/>
<then>
<antcall target="exports" if="is-export-defined"/>
</then>
</if>
</target>
<target name="target-that-may-run-exports-if-available" depends="check-and-call-exports">
You should explore use of the typefound condition, added to ANT in 1.7. You can use it, for example, with the if task from antcontrib like this, but you have to check for a macrodef and not a taskdef due to how it works:
<if>
<typefound name="some-macrodef"/>
<then>
<some-macrodef/>
</then>
</if>
With this, ant files that have a macrodef named "some-macro-or-taskdef" will get it invoked and other ant files without it will not get an error.
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)