macrodef'd <jar> task not behaving like an implicit fileset? - ant

Using standard Ant 1.9.7 to assemble a bunch of jar files. We've made a macro to help cut down on the verbosity of the XML:
<!-- All 'description' bits snipped for SO posting. -->
<macrodef name="buildjar">
... bunch of <attribute> ...
<element name="also"/>
<sequential>
<jar ....>
<manifest> ... </manifest>
<fileset what="stuff in every jar file" />
<fileset what="and this stuff too" />
<mappedresources if:true="beauty">
<fileset for when truth is beauty/>
<globmapper from="ugly" to="beauty"/>
</mappedresoruces>
<!-- Anything else for this specific jar. -->
<also/>
</jar>
</sequential>
</macrodef>
This works:
<buildjar .........>
<also>
<fileset file="/some/path/somewhere/a_single_file"/>
</also>
</buildjar>
But this does not:
<buildjar .........>
<also>
<include name="/some/path/somewhere/a_single_file"/>
</also>
</buildjar>
There are no errors. Looking at ant -d output, there's no mention of the additional entry at all, where in the first example there is a line for fileset: Setup scanner in dir /some/path/somewhere with patternSet{ includes: [a_single_file] excludes: [] }
Ditto for multiple files. This works:
<buildjar .........>
<also>
<fileset dir="/some/path/somewhere">
<include name="one_file" />
<include name="foo**" />
</fileset>
</also>
</buildjar>
but this does not:
<buildjar .........>
<also>
<include name="/some/path/somewhere/one_file"/>
<include name="/some/path/somewhere/foo**"/>
</also>
</buildjar>
According to the Ant manual's page for <jar>,
This task forms an implicit FileSet and supports most attributes of <fileset> (dir becomes basedir) as well as the nested <include>, <exclude> and <patternset> elements.
So in theory, shouldn't an <include> simply be enough, and become a nested element of the macro'd <jar>?
Obviously, in practice this isn't a problem (we slap a bigass comment in the build files telling people to not leave out the explicit <fileset>). And we can't put the <fileset> into the macro definition like this:
<macrodef name="buildjar">
<element name="also"/>
<sequential>
<jar ....>
.....
<!-- Anything else for this specific jar. -->
>> <fileset dir="some_generic_base_path">
<also/>
>> </fileset>
</jar>
</sequential>
</macrodef>
because then when the calling code does a buildjar without any also blocks, the unrestricted fileset will include the entire some_generic_base_path tree.
Is this simply some interaction between macrodefs and filesets that has taken us by surprise?

Short answer is no - it's not a macrodef problem. To use the implicit fileset you must specify the basedir attribute for the <jar> task. Here's an example that illustrates this:
<jar destfile="my.jar">
<include name="a/b" />
</jar>
<zip destfile="my.zip" >
<include name="a/b" />
</zip>
In the example the <jar> task will succeed and create a manifest-only jar file. But the <zip> task will fail saying:
BUILD FAILED
build.xml:8: basedir attribute must be set, or at least one resource collection must be given!
The jar task is based on the zip task and inherits its check for resources. Because the jar task has some - the manifest - the error is not thrown.
If you want to use the implicit fileset, specify a basedir.

Related

how to check if class file or jar file is instrumented?

First Question -
Is there any way i can verify that the specific jar files or class files has been instrumented using cobertura?
Second Question - Can you please let me know if following ant scipt is fine. I dont get any output from this. nor instrumented file or cobertura.ser and build says ok.
<project>
<property name="cobertura.dir" value="../cobertura-2.0.3" />
<property name="instrumented.dir" value="../destination" />
<property name="jars.dir" value="../basedir" />
<path id="cobertura.classpath">
<fileset dir="${cobertura.dir}">
<include name="cobertura-2.0.3.jar" />
<include name="lib/**/*.jar" />
</fileset>
</path>
<target name="instrument-classes">
<taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
<delete file="cobertura.ser" />
<cobertura-instrument todir="${instrumented.dir}">
<fileset dir="${jars.dir}">
<include name="XXX.jar" />
</fileset>
<fileset dir="${jars.dir}">
<include name="YYYY.jar" />
</fileset>
</cobertura-instrument>
</target>
</project>
You can use a java decompiler to see references to net.sf.cobertura.xxx classes in the instrumented classes. You can also use a simple text editor that accept to visualize binary files (e.g. TextPad) and you will see net.sf.cobertura references as well, if you look carefully (using a text compare tool to compare the original class and the instrumented one makes it more obvious).
Your ant snippet looks all right except possibly for the suspicious:
<property name="jars.dir" value="../basedir" />
Perhaps you meant something like:
<property name="jars.dir" value="${basedir}/.." />
The point is: you should validate that the nested filesets actually include files, otherwise there won't be anything accomplished by the cobertura-instrument task.

Ant conditional if within a macrodef

Within ant, I have a macrodef.
Assuming I have to use this macrodef, and there is a item inside said macrodef that I want to run if the property special.property exists and is true, what do I do?
I currently have
<macrodef name="someName">
<sequential>
<someMacroDefThatSetsTheProerty />
<some:thingHereThatDependsOn if="special.property" />
<sequential>
</macrodef>
Which doesn't work - the some:thingHereThatDependsOn doesnt have an "if" attribute, and I cannot add one to it.
antcontrib is not available.
With a target I can give the target an "if", what can I do with a macrodef?
In Ant 1.9.1 and higher, there is now a new implementation of if and unless attributes. This might be what you're thinking of.
First, you need to put them into your namespace. Add them to your <project> header:
<project name="myproject" basedir="." default="package"
xmlns:if="ant:if"
xmlns:unless="ant:unless">
Now, you can add them to almost any Ant task or sub entity:
<!-- Copy over files from special directory, but only if it exists -->
<available property="special.dir.available"
file="${special.dir} type="dir"/>
<copy todir="${target.dir}>
<fileset dir="${special.dir}" if:true="special.dir.available"/>
<fileset dir="${other.dir}"/>
</copy>
<!-- FTP files over to host, but only if it's on line-->
<condition property="ftp.available">
<isreachable host="${ftp.host}"/>
</condition>
<ftp server="${ftp.host}"
userid="${userid}"
passowrd="${password}"
if:true="ftp.available">
<fileset dir=".../>
</ftp>
This is only possible if the ANT "thingHereThatDependsOn" task supports an "if" attribute.
As stated above, conditional execution in ANT, normally, only applies to targets.
<target name="doSomething" if="allowed.to.do.something">
..
..
</target>
<target name="doSomethingElse" unless="allowed.to.do.something">
..
..
</target>
<target name="go" depends="doSomething,doSomethingElse"/>

Ant: Source and target files are the same. How to detect a change?

We are using JiBX. The important thing to know is that JiBX modifies the already compiled class files.
We do our compile:
<javac destdir="${main.destdir}">
<src path="${main.srcdir}"/>
<classpath refid="main.classpath"/>
</javac>
Then, we call JiBX:
<jibx load="true"
binding="{$binding.file}">
<classpath refid="main.classpath"/>
<classpath refid="main.destdir.classpath"/>
</jibx>
This uses an XML file that updates the classfiles compiled by <javac> above. The problem is how do I know that the files have been compiled, but not processed by JiBX? I'd like to put some logic in my program, so that files are not updated twice by JiBX. Besides, it's bad form to duplicate work that already been done.
After the jibx build step, generate a marker file, e.g.
<touch file="${target.dir}/jibx.marker" />
Only perform the jibx build step if that marker file is older than the .class files (indicating that the javac ran more recently than the last jibx).
For that bit of logic, you can use the traditional ant way:
<uptodate property="jibx.uptodate" targetfile="${target.dir}/jibx.marker">
<srcfiles dir="${main.destdir}" includes="...../*.class" />
</uptodate>
And then use the property with an unless clause when invoking the jixb target.
Or, you can use Antcontrib's outofdate alternative:
<outofdate>
<sourcefiles>
<fileset dir="${main.destdir}" includes="...../*.class" />
</sourcefiles>
<targetfiles>
<fileset dir="${target.dir}" includes="jibx.marker"/>
</targetfiles>
<sequential>
<jibx load="true"
binding="{$binding.file}">
<classpath refid="main.classpath"/>
<classpath refid="main.destdir.classpath"/>
</jibx>
</sequential>
</outofdate>
I'm giving this to Patrice M. because his suggestion put me on the right track. However, it didn't quite work out as he stated. (Sorry, if I got he pronoun wrong, but Patrice can be both a male or female name.)
What I had to do was create two watch files: One for the Java compile, and one for the JiBX changes.
<!-- Check if Javac is out of date. If so, create javac watcher -->
<outofdate verbose="true">
<sourcefiles>
<fileset dir="${main.srcdir}">
<include name="*.java"/>
</fileset>
</sourcefiles>
<mapper type="regexp"
from="${main.srcdir}/(.*)\.java"
to="${main.destdir}/(\1).class"/>
<sequential>
<echo message="Java compiled"/>
<echo message="Java compiled"
file="${target.dir}/${javac.monitor.file}"/>
</sequential>
</outofdate>
<javac destdir="${main.destdir}"
debug="${javac.debug}">
<src path="${main.srcdir}"/>
<classpath refid="main.classpath"/>
</javac>
<!-- Compare javac and jibx monitoring file -->
<!-- If out of date, rerun jibx -->
<outofdate>
<sourcefiles>
<fileset dir="${target.dir}">
<include name="${javac.monitor.file}"/>
</fileset>
</sourcefiles>
<targetfiles>
<fileset dir="${target.dir}">
<include name="${jibx.monitor.file}"/>
</fileset>
</targetfiles>
<sequential>
<jibx load="true"
binding="${target.dir}/binding-gg.xml">
<classpath refid="main.classpath"/>
<classpath refid="main.destdir.classpath"/>
</jibx>
<!-- Create JiBX monitoring file -->
<echo message="Compiled and JiBX"
file="${target.dir}/${jibx.monitor.file}"/>
</sequential>
</outofdate>
I create the javac monitoring file if the source is out of date with the classes because that's when I compile. I have to create the JiBX outofdate monitoring file only when I run JiBX and that's inside the <outofdate> for JiBX.
I guess I could also put a source on the XML JiBX files too just to be sure.

How do you use ant to unjar multiple JAR files and rebuild them into one JAR file?

I would like to unjar multiple JAR files and then rebuild into one JAR using an ant build script. Is this possible?
Yes, it's possible with ant. A jar file is basically a zip with a special manifest file. So to unjar, we need to unzip the jars. Ant includes an unzip task.
To unzip/unjar all the jar files in your project:
<target name="unjar_dependencies" depends="clean">
<unzip dest="${build.dir}">
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</unzip>
</target>
Obviously you need to declare ${build.dir} and ${lib.dir} first. The line <include name="**/*.jar" /> tells ant to include all files that end up with the jar extension, you can tweak that include to suit your needs.
To pack everything into a jar, you use the jar task:
<target name="make_jar" depends="compile, unjar_dependencies">
<jar basedir="${build.dir}"
destfile="${dist.dir}/${project_name}.jar">
<manifest>
<attribute name="Main-Class" value="${mainclass}" />
</manifest>
<fileset dir="${build.dir}">
<include name="**/*.class" />
</fileset>
<fileset dir="${src.dir}">
<include name="applicationContext.xml" />
<include name="log4j.properties" />
</fileset>
</jar>
</target>
In this example, we include different filesets. In one fileset we are including all compiled classes. In another fileset we include two config files that this particular project depends upon.
Yes it is !
You have two possibilities :
Espen answer :
One possible solution that creates one
jar file from all the jar files in a
given directory:
<target name="dependencies.jar">
<jar destfile="WebContent/dependencies.jar">
<zipgroupfileset dir="lib/default/" includes="*.jar"
excludes="*.properties" />
</jar>
</target>
This is useful if you don't need to exclude content that are in some jars (like for example some properties configuration file that might override yours, etc). Here the excludes properties is filtering out files from the dir property.
Use zipfileset
The other solution is to use the zipfileset tag where the excludes property this time will filter out content from the jar to be merged.
<jar destfile="your_final_jar.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class" value="main.class"/>
<attribute name="Class-Path" value="."/>
</manifest>
<zipfileset
excludes="META-INF/*.SF"
src="/path/to/first/jar/to/include.jar"/>
</jar>
Of course you can combine the two tags (zipfileset and zipgroupfileset) inside the same jar tag to get the best of the two.
Yes, it's possible.
One possible solution that creates one jar file from all the jar files in a given directory:
<target name="dependencies.jar">
<jar destfile="WebContent/dependencies.jar">
<zipgroupfileset dir="lib/default/" includes="*.jar"
excludes="*.properties" />
</jar>
</target>
There is also a project devoted to repackage jars called JarJar. You can use it to repackage mutiple Jars into one. Depending on your requirements, you can even rename classes to prevent version conflicts.
From their getting started page:
In this example we include classes from jaxen.jar and add a rule that changes any class name starting with "org.jaxen" to start with "org.example.jaxen" instead (in our imaginary world we control the example.org domain):
<target name="jar" depends="compile">
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask"
classpath="lib/jarjar.jar"/>
<jarjar jarfile="dist/example.jar">
<fileset dir="build/main"/>
<zipfileset src="lib/jaxen.jar"/>
<rule pattern="org.jaxen.**" result="org.example.#1"/>
</jarjar>
</target>

How do I "expand" an ant path (accessed with refId=..) to all files in the path except some?

I am trying to get ant4eclipse to work and I have used ant a bit, but not much above a simple scripting language. We have multiple source folders in our Eclipse projects so the example in the ant4eclipse documentation needs adapting:
Currently I have the following:
<target name="build">
<!-- resolve the eclipse output location -->
<getOutputpath property="classes.dir" workspace="${workspace}" projectName="${project.name}" />
<!-- init output location -->
<delete dir="${classes.dir}" />
<mkdir dir="${classes.dir}" />
<!-- resolve the eclipse source location -->
<getSourcepath pathId="source.path" project="." allowMultipleFolders='true'/>
<!-- read the eclipse classpath -->
<getEclipseClasspath pathId="build.classpath"
workspace="${workspace}" projectName="${project.name}" />
<!-- compile -->
<javac destdir="${classes.dir}" classpathref="build.classpath" verbose="false" encoding="iso-8859-1">
<src refid="source.path" />
</javac>
<!-- copy resources from src to bin -->
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset refid="source.path">
<include name="**/*"/>
<!--
patternset refid="not.java.files"/>
-->
</fileset>
</copy>
</target>
The task runs successfully, but I cannot get the to work - it is supposed to copy all non-java files over too to emulate the behaviour of eclipse.
So, I have a pathId named source.path which contains multiple directories, which I somehow needs to massage into something the copy-task like. I have tried nesting which is not valid, and some other wild guesses.
How can I do this - thanks in advance.
You might consider using pathconvert to build a pattern that fileset includes can work with.
<pathconvert pathsep="/**/*," refid="source.path" property="my_fileset_pattern">
<filtermapper>
<replacestring from="${basedir}/" to="" />
</filtermapper>
</pathconvert>
That will populate ${my_fileset_pattern} with a string like:
1/**/*,2/**/*,3
if source.path consisted of the three directories 1, 2, and 3 under the basedir. We're using the pathsep to insert wildcards that will expand to the full set of files later.
The property can now be used to generate a fileset of all the files. Note that an extra trailing /**/* is needed to expand out the last directory in the set. Exclusion can be applied at this point.
<fileset dir="." id="my_fileset" includes="${my_fileset_pattern}/**/*">
<exclude name="**/*.java" />
</fileset>
The copy of all the non-java files then becomes:
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset refid="my_fileset" />
</copy>
That will copy the source files over retaining the source directory structure under todir. If needed, the flatten attribute of the copy task can be set to instead make all the source files copy directly to todir.
Note that the pathconvert example here is for a unix fileseystem, rather than windows. If something portable is needed, then the file.separator property should be used to build up the pattern:
<property name="wildcard" value="${file.separator}**${file.separator}*" />
<pathconvert pathsep="${wildcard}," refid="source.path" property="my_fileset">
...
You could use the foreach task from the ant-contrib library:
<target name="build">
...
<!-- copy resources from src to bin -->
<foreach target="copy.resources" param="resource.dir">
<path refid="source.path"/>
</foreach>
</target>
<target name="copy.resources">
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset dir="${resource.dir}" exclude="**/*.java">
</copy>
</target>
If your source.path contains file paths as well then you could the if task (also from ant-contrib) to prevent attempting to copy files for a file path, e.g.
<target name="copy.resources">
<if>
<available file="${classes.dir}" type="dir"/>
<then>
<copy todir="${classes.dir}" preservelastmodified="true">
<fileset dir="${resource.dir}" exclude="**/*.java">
</copy>
</then>
</if>
</target>

Resources