Ant-Ivy post-retrieve-trigger - ant

I am trying to tackle some technical debt in our Ant/Ivy system and one of my current tasks is to address some post-retrieve behaviors we currently have. By default, our build system retrieves Ivy dependencies and then extracts compressed artifacts (tar, tar.bz2, gzip, zip only) to a dependency folder, so that our projects have a consistent dependency location:
(project.root)/dependency/.archive <- the compressed dependency location
(project.root)/dependency/extracted-foo` <- the uncompressed dependency
The extraction occurs in a post-retrieve-artifact trigger so that we get the benefit of some of the metadata (paths, names, types, etc., all prefixed with 'dep'.
We currently have one property that can be set to turn off this default behavior for all the dependencies specified in an ivy.xml file. Thus, we are left with an all-or-nothing situation. If we want something in-between, we currently have to use our build.xml file and write some custom code. This is painful because the metadata is not readily available.
I would like to retain the use of the all-or-nothing flag but allow projects to selectively extract items - we have several projects whose build.xml files would be greatly simplified if we could knock the extraction process down to an attribute on the artifact itself.
Thus, my thinking is to use an extra attribute on the artifact tag to "inject" this information and override the ivy.retrieve.pattern to search for this attribute.
Ivy.xml
<ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra">
<dependencies>
<dependency org="my.org" name="foo" rev="${foo.version}" conf="${conf.archive}->*" transitive="false">
<artifact name="megapin" type="war" e:expand="expand"/>
</dependency>
</dependencies>
</ivy-module>
Build.xml
This is where I think I'm having trouble getting the expand extra attribute to show up.
Question 1: This does add the "extract" attribute to the artifact name at retrieve time. I can use the contains clause to check if that is there in the dep.to Is there a way to retrieve the extra attributes (e.g., ${dep.extra.expand} ?
<property name="ivy.retrieve.pattern" value="${dependency.dir}/[conf]/[artifact]-[rev])(-[expand]).[ext]"/>
</property>
<target name="ivy-post-retrieve-trigger">
<local name="doexpand"/>
<condition property="doexpand">
<contains string="${dep.to}" substring="expand" casesensitive="false"/>
</condition>
<!-- this step works if the flag is set properly, so I'm leaving out these non-relevant steps-->
<...extract if:isset="doexpand"... />
ivysettings.xml
This file basically has the trigger and other resolver settings.
<triggers>
<ant-call target="ivy-post-retrieve-trigger" prefix="dep" event="post-retrieve-artifact"/>
</triggers>
Question 2: Any suggestions on a "noexpand" name? My concern with the <contains> clause is that the "expand" is going to get hit all the time.
I think I am close to getting this working - but the only information I get is: Property "doexpand" has not been set and thus it is skipping the extraction step. Q3 Any tips/advice/examples on how to use the extra attribute on a trigger with Ant/Ivy?

I ended up adding some additional debugging statements to Ivy (as compiled from source). In the ant-ivy/src/java/org/apache/ivy/ant/AntCallTrigger.java I added the following line:
Message.verbose("\tp.name=" + p.getName() + " | p.value=" + p.getValue() );
If I modified my dependency in my Ivy.xml file to be:
<dependency org="my.org" name="foo" rev="${foo.version}" conf="${conf.archive}->*" transitive="false">
<artifact name="megapin" type="war" e:expand="true"/>
</dependency>
This showed
[ivy:retrieve] p.name=dep.expand | p.value=true
At which point I could do something like
<isset property="dep.expand"/>
or
<istrue value=${dep.expand}/>
This answers my Q1. Additionally, I don't need to add this to the Ivy retrieve pattern (thus changing the filename after retrieve), can use a "true" or "false" value as desired (Q2), and this general guidance answers Q3.

Related

Ant xmlproperty task fails due to validation error

I want to extract an application version from a DITA map file. The ditamap file is valid and looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "map.dtd">
<map id="user-manual">
<title><ph keyref="product"/> User Manual</title>
<topicmeta>
<prodinfo>
<prodname><keyword keyref="product"/></prodname>
<vrmlist>
<vrm version="4" release="3" modification="0"/>
</vrmlist>
</prodinfo>
</topicmeta>
<!--
[...]
-->
</map>
The information I want to get is in the <vrm> element.
"Easy peasy," I think to myself. So I use Ant's <xmlproperty> task to just load this XML file.
<project default="test">
<!-- notice #validate -->
<xmlproperty file="path/to/user-manual.ditamap" validate="false"/>
<target name="test">
<echo>${map.topicmeta.prodinfo.vrmlist.vrm(version)}</echo>
</target>
</project>
I don't want it to validate because Ant isn't going to find map.dtd.
Loading the file returns an error:
java.io.FileNotFoundException: /home/user/user-manual/map.dtd (No such file or directory)
If I remove the <!DOCTYPE> declaration or add a nested <xmlcatalog> with the path to the DTD, the file loads and I can use the properties from it.
I tested this with Ant 1.7.1 and 1.9.4. Is this a bug with Ant, or am I misunderstanding how Ant loads XML properties and the purpose of the validate attribute?
How can I make Ant obey my will?
I recommend to not use the <xmlproperty> for this. Please have a look at the docs:
For example, with semantic attribute processing enabled, this XML
property file:
<root>
<properties>
<foo location="bar"/>
<quux>${root.properties.foo}</quux>
</properties>
</root>
is roughly equivalent to the following fragments in a build.xml file:
<property name="root.properties.foo" location="bar"/>
<property name="root.properties.quux" value="${root.properties.foo}"/>
So the name of the properties you set is generated using their paths to the root element, so they rely on the structure of your DITA Map. But many elements in DITA may be set at different positions on your DITA Map. That means, if you move your metadata to another parent element, the property name changes and your build fails. This is probably not, what you want.
I'd recommend to grab those values via XSLT and than set the properties. That way, you could, for example, say, "give me the first occurance of that element with a simple //foo[1] XPath selector. Further on, you have the power of XSLT and XPath to slice values, format dates and so on before setting a property.
Update
You can use the oops consultancy Ant xmltask for that. It is very easy to set a property using <copy>:
<copy path="//critdates/created/#date"
property="document.date"
append="false"/>

Ant tasks don't run IN IzPack installer

.
Hello, everyone
I'm studying IzPack as a tool to be used in a future project and I'm really enjoying it. It's as flexible as I need and makes the process much more easy. I have even submmited a silly pull request at github with a modification I needed to my purposes. Who knows?
Although I don't find it particularly complicated, I've been stuck trying to use a resource for some days. I need that certain Ant Tasks to be executed in certain points of the installation process (right before everything is unpacked is the really one that matters) and that is not working, besides all the efford. :(
My current state, that seems right looking at examples, is the following:
[ My current use of this is based on an example I found here (the docs don't clear too much when It cames to these kind of Actions.]
In my definitions xml file, I included some things:
First, the AntActionsSpect.xml and the .jars, followed by the listeners:
<resources>
...
<res id="AntActionsSpec.xml" src="specs/AntActionsSpec.xml" />
...
</resources>
<jar src="libs/ant/ant.jar" stage="both" />
<jar src="libs/ant/ant-launcher.jar" stage="both" />
<listeners>
<listener classname="AntActionInstallerListener" stage="install" />
<listener classname="AntActionUninstallerListener" stage="uninstall" />
</listeners>
<pack name="test_app" required="yes" installGroups="Application Core">
...
In the specs/AntActionsSpec.xml file, I have the following:
<pack name="test_app">
<antcall order="beforepacks" quiet="no" verbose="yes" buildfile="$INSTALL_PATH/ant-tasks.xml">
<property name="INSTALL_PATH" value="$INSTALL_PATH" />
<target name="touch_beforepacks" />
</antcall>
</pack>
And the ant-tasks.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<target name="touch_beforepacks">
<touch file="$INSTALL_PATH/beforepacks.txt"/>
</target>
</project>
Nothing special here, just creating a dumb file.
The ant-tasks.xml is unpacked right before anyone else. Everything builds with no error, even if I create one "mistake" at AntActionsSpec or ant-tasks.xml, what suggests me that they aren't even been loaded, though if I mess with the path where the definitions file has them, the build will fail.
I would like some help addressing that. I'm probably making some stupid little error and just can't see it by myself. If any of you could provide an example of a running build, that would be sweet.
If I can give any more information, please, let me known so I can update the question.
Thank you very much.
Just found it using a forum on a Google Groups discussion: [izpack-user] Quick question on variable substitution.
Unfortunattly the I will conclude that the docs are misleading. The docs in
"AntActionInstallerListener and AntActionUninstallerListener" until this date are stating that I should use this listener configuration:
<listeners>
<listener classname="AntActionInstallerListener" stage="install" />
<listener classname="AntActionUninstallerListener" stage="uninstall" />
</listeners>
That is what is up there, in the question. Comparing my XML code with the one in the Google Groups discussion, I found a different use of it:
<listeners>
<listener installer="AntActionInstallerListener"
uninstaller="AntActionUninstallerListener" />
</listeners>
In fact, that is the instruction given in the other wiki: Ant Actions (InstallerListener and UninstallerListener), what points out that I something can be wrong under the hood, but that is a story to another episode.
That just works. The Ant tasks are executed properly. :)
I just could not find where freaking Codehaus will allow me to grab a login and edit the docs wiki. >:( . If someone could endorse-me with some testing and then adjust the wiki for future happiness or just give a link to this tired programmer, I'd be happy.

reorganize files inside a jar with ant

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.

Ant: How to test if a target exist (and call it not if it doesn't)?

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.

getting error message: "unknown resolver XYZ"

while resolving my ivy.xml, I get a long list of errors, all stating "unknown resolver XYZ". I know the resolver, it is used in the same project but different task.
As far as I understand, the resolver used to create the cache entry is stored and than cannot be determined by the follow-up resolver.
Question is: how can I avoid this? Seeams like this is not really an error, more like a warning since I am able to resolve all dependencies and continue compiling.
Within the same project, the build resolver will not change because it's defined in your ivysettings.xml file.
This is more likely to be a problem with a stale ivy cache. I'd suggest adding an extra target that purges your cache. Useful when encountering this type of problem:
<target name="clean-all" depends="clean" description="Purge ivy cache">
<ivy:cleancache/>
</target>
Run your ant build with the verbose flag (-v). This will give you clear insight into which settings files are being used throughout the resolve process. My wager is you will find your problem fairly easily and it will be along the lines of the settings file you thought you were using is actually not being used.
In my projects, I find this type of thing often happens when a post-resolve task (such as retrieve) triggers a resolve "automatically" and uses the default ivy settings instead of the one I want it to use at the moment. Chances are, your default settings file does not contain the resolvers you're expecting.
To solve these issues, I make a ivysettings-common.xml containing only resolvers. Then, in each of my settings files, I import the common settings and reference the resolvers in the main chain. That looks like:
<ivysettings>
<settings defaultResolver="all-repositories" />
<include file="ivysettings-common.xml" />
<resolvers>
<chain name="all-repositories" returnFirst="true" >
<resolver ref="project" />
<resolver ref="local" />
<resolver ref="hibernate" />
<resolver ref="ibibilo" />
</chain>
</resolvers>
</ivysettings>
From there, I make the common file my default settings, just "in case of emergency" I know all my resolvers can be found (by adding the following to ivy.properties):
ivy.settings.file = ${basedir}/path/to/ivysettings-common.xml
but I explicitly point all my ivy calls to the appropriate settings file, trying to never rely on the default because the whole reason I use ivy+ant is that I prefer precise control over my build process:
I hope all that helps you or someone else.
~gMale

Resources