Exclude empty directories with Jar Jar Links - ant

I am using an Ant task from Jar Jar Links to embed classes from a 3rd-party jar file (objenesis) in my distributable jar file (example.jar). Jar Jar will translate classes from the original package (org.objenesis) to one of my choosing.
It works but it leaves empty directories in the distributable jar.
Here is a simplified build.xml:
<target name="jar" depends="compile">
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask"
classpath="lib/jarjar-1.1.jar"/>
<jarjar jarfile="dist/example.jar" excludes="org.objenesis.**">
<fileset dir="build/classes/main"/>
<zipfileset src="lib/objenesis-1.2.jar"/>
<rule pattern="org.objenesis.**" result="org.thirdparty.#1"/>
</jarjar>
</target>
A sample of contents of the example.jar includes (as expected):
org/thirdparty/Objenesis.class
org/thirdparty/ObjenesisBase.class
but also these empty directories (undesirable):
org/objenesis/
org/objenesis/instantiator/
org/objenesis/instantiator/basic/
My question: how to I exclude these empty directories?
I tried the "zap" option (listed in the doc), but that didn't work.

This appears to be a known issue in Jar Jar, listed in their issue tracker: http://code.google.com/p/jarjar/issues/detail?q=empty&id=32
Given that this was raised almost three years ago and doesn't appear to have got any traction, I suppose your options are to contribute a fix, or to work around it.
An example Ant target to work around it, taking advantage of Ant's support for removing empty directories on copy, would be:
<target name="unpolluted-jarjar" description="JarJars without empty directories">
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpath="${location.lib}/build/jarjar-1.2.jar"/>
<jarjar basedir="${location.classes}" destfile="${location.dist.binaries}/my-app.jar">
<zipfileset src="${location.lib}/shipped/dependency.jar"/>
<rule pattern="com.example.dependency.**" result="com.example.my-app.jarjar.com.example.dependency.#1"/>
</jarjar>
<mkdir dir="${location.dist.binaries}/exploded"/>
<unzip src="${location.dist.binaries}/my-app.jar" dest="${location.dist.binaries}/exploded/my-app.jar"/>
<copy includeemptydirs="false" todir="${location.dist.binaries}/unpolluted/my-app.jar">
<fileset dir="${location.dist.binaries}/exploded/my-app.jar"/>
</copy>
<jar destfile="${location.dist.binaries}/my-app-unpolluted.jar">
<fileset dir="${location.dist.binaries}/unpolluted/my-app.jar"/>
</jar>
</target>
It's a bit grungy, but it achieves what you want.

Related

is there a way to make a jar with the Eclipse generated ant script?

To my surprise the build.xml file generated by Eclipse (Neon) for Java has no element containing an invocation of a jar task. As often is the case with code generation I think you have to use it and make no edits so that you can regenerate - or - avoid code generation completely. A comment in the generated file suggests it might be possible to avoid edits by extending the capabilities by importing.
<!-- WARNING: Eclipse auto-generated file.
Any modifications will be overwritten.
To include a user specific buildfile here, simply create one in the same
directory with the processing instruction <?eclipse.ant.import?>
as the first entry and export the buildfile again. -->
I thought I would be able to use the <?eclipse.ant.import?> element in an second file called export.xml. In ant scripting there is supposed to be one project per buildfile so now there is a second project with a dependency on a target in the first project.
Regenerating build.xml reveals that it contains an "import" as expected.
<import file="export.xml"/>
Unfortunately this does not work. Running ant, which I do from the command line, just seems to result in the export/jar project being ignored.
The generated script with the import element (nested on the 7th line)...
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project basedir="." default="build" name="ohana1">
<property environment="env"/>
<property name="debuglevel" value="source,lines,vars"/>
<property name="target" value="1.8"/>
<property name="source" value="1.8"/>
<import file="export.xml"/>
<path id="ohana1.classpath">
<pathelement location="bin"/>
<pathelement location="../export/ohana1/commons-collections-3.2.1.jar"/>
</path>
<target name="init">
<mkdir dir="bin"/>
<copy includeemptydirs="false" todir="bin">
<fileset dir="src">
<exclude name="**/*.launch"/>
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
<target name="clean">
<delete dir="bin"/>
</target>
<target depends="clean" name="cleanall"/>
<target depends="build-subprojects,build-project" name="build"/>
<target name="build-subprojects"/>
<target depends="init" name="build-project">
<echo message="${ant.project.name}: ${ant.file}"/>
<javac debug="true" debuglevel="${debuglevel}" destdir="bin" includeantruntime="false" source="${source}" target="${target}">
<src path="src"/>
<classpath refid="ohana1.classpath"/>
</javac>
</target>
<target description="Build all projects which reference this project. Useful to propagate changes." name="build-refprojects"/>
<target description="copy Eclipse compiler jars to ant lib directory" name="init-eclipse-compiler">
<copy todir="${ant.library.dir}">
<fileset dir="${ECLIPSE_HOME}/plugins" includes="org.eclipse.jdt.core_*.jar"/>
</copy>
<unzip dest="${ant.library.dir}">
<patternset includes="jdtCompilerAdapter.jar"/>
<fileset dir="${ECLIPSE_HOME}/plugins" includes="org.eclipse.jdt.core_*.jar"/>
</unzip>
</target>
<target description="compile project with Eclipse compiler" name="build-eclipse-compiler">
<property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>
<antcall target="build"/>
</target>
</project>
The export.xml file meant to make a jar...
<?eclipse.ant.import?>
<project basedir="." default="export" name="ohana1Export">
<target depends="build,make-jar" name="export"/>
<target name="make-jar">
<jar destfile="../export/ohana1/${ant.project.name}.jar" basedir="bin"/>
</target>
</project>
Note that the Eclipse Ant editor complains about this export.xml file because the target named build, which is a dependency, does not exist in this project/buildfile. The build target is in the generated build.xml. That error might be coming from a "dumb" editor so I went ahead to do a run of ant. Invoking ant from the command line I find that there is no jar file made.
Should I conclude that Eclipse's ant script generator is useless if you need to export a .jar file and that a human should maintain the ant script that meets all the requirements?
Yes, in my opinion the exported build.xml is useless, as of Eclipse Neon, if the intention is to make a .jar.
Specifically do the following.
Manually write the trivial ant script that exports a .jar. The link at the bottom of this post has verbatim text on what the script might look like. You can use the built-in Xml Editor via New > Other > XML > XML File to create this new file which might be called makeJar.xml and save it. If the icon shown in the Package Explorer is still a plain XML file icon refreshing the project may change the icon to an Ant file icon. In the future, you can use Open With to get the Ant Editor instead of the XML Editor. This script will replace the manual exporting of a .jar that the user would otherwise perform via Eclipse.
This script can be added to Project > Properties > Builders. It would be placed second in the list of Builders. First in the list of Builders is the Java Builder which should already exist. When an Eclipse build is invoked the entire list of Builders will be processed in the order shown in the list of Builders. Thus not only will .class files be generated but also the .jar.
What is achieved is greater automation since the .class generation and .jar generation are now integrated, which arguably was the point of using the exported build.xml in a failed attempt to generate the .jar.
Here is the dialog at Project > Properties > Builders that you can use to create a new Builder. Select New then select Ant Builder. I gave the name makeJar to the new Builder.
Here is the dialog for the new Ant Builder that will allow you to browse to your buildfile which is your manually written Ant script that creates a .jar file. In this example the script is makeJar.xml. It also allows you to browse to the base directory to be used when the script is run.
After setting up the new Builder, a project "clean" or project "build" will create .class files and also the .jar.
Eclipse's documentation on this subject is at the link. Note that it seems impossible to link the exact page that contains the instructions so you have to browse down the documentation tree to the section about "Ant buildfiles as project builders".
Link to Eclipse and Ant

ANT: ways to include libraries and license issues

I have been trying to use Ant to compile and ready a project for distribution. I have encountered several problems along the way that I have been finally able to solve but the solution leaves me very unsatisfied. First, let me explain the set-up of the project and its dependencies.
I have a project, lets call it Primary which depends on a couple of libraries such as the fantastic Guava. It also depends on another project of mine, lets call it Secondary. The Secondary project also features some dependencies, for example, JDOM2. I have referenced the Jar I build with Ant in Primary.
Let me give you the interesting bits of the build.xml so you can get a picture of what I am doing:
<project name="Primary" default="all" basedir=".">
<property name='build' location='dist' />
<property name='application.version' value='1.0'/>
<property name='application.name' value='Primary'/>
<property name='distribution' value='${application.name}-${application.version}'/>
<path id='compile.classpath'>
<fileset dir='libs'>
<include name='*.jar'/>
</fileset>
</path>
<target name='compile' description='Compile source files.'>
<javac includeantruntime="false" srcdir="src" destdir="bin">
<classpath refid='compile.classpath'/>
</javac>
<target>
<target name='jar' description='Create a jar file for distribution.' depends="compile">
<jar destfile='${build}/${distribution}.jar'>
<fileset dir="bin"/>
<zipgroupfileset dir="libs" includes="*.jar"/>
</jar>
</target>
The Secodnary project's build.xml is nearly identical except that it features a manifest as it needs to run:
<target name='jar' description='Create a jar file for distribution.' depends="compile">
<jar destfile='${dist}/${distribution}.jar' basedir="${build}" >
<fileset dir="${build}"/>
<zipgroupfileset dir="libs" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="lu.tudor.ssi.kiss.climate.ClimateChange"/>
</manifest>
</jar>
</target>
After I got it working, trying for many hours to not include that dependencies as class files but as Jars, I don't have the time or insight to go back and try to figure out what I did wrong. Furthermore, I believe that including these libraries as class files is bad practice as it could give rise to licensing issues while not packaging them and merely including them in a directory along the build Jar would most probably not (And if it would you could choose not to distribute them yourself).
I think my inability to correctly assemble the class path, I always received NoClassDefFoundError for classes or libraries in the Primary project when launching Second's Jar, is that I am not very experienced with Ant. Would I require to specify a class path for both projects? Specifying the class path as . should have allowed me to simply add all dependencies to the same folder as Secondary's Jar, should it not?
You may use the MANIFEST.MF "Class-Path: " to cross-reference your jars.
If they are all in the same directory this will probably work as follows (using it in both projects!):
<target name='jar' description='Create a jar file for distribution.' depends="compile">
<pathconvert property="manifest.classpath" pathsep=" ">
<path refid="compile.classpath" />
<flattenmapper />
</pathconvert>
<jar destfile='${build}/${distribution}.jar'>
<fileset dir="bin"/>
<manifest>
<attribute name="Class-Path" value="${manifest.classpath}"/>
</manifest>
</jar>
</target>
This way you can tell the java runtime environment that your jar needs others to work, expecting them to be in the same directory as the jar you are trying to run.
As a result your primary.jar should have secondary.jar in it's classpath and secondary.jar should have guava.jar in it's classpath.
Another way to create the string may be ants manifestclasspath task (https://ant.apache.org/manual/Tasks/manifestclasspath.html) that can handle subdirectories.
If you are goin to use more and more libraries, you may want to have a closer look at ivy or even maven.

Creating an ear-File with ant

I am new to ant i referred many sites , i need to build.xml for my project which consists
of two modules i have application.xml file which represents corresponding war file
so my question is it sufficient to add the application.xml file
<ear destfile="${dist.dir}/${ant.project.name}.ear" appxml="${conf.dir}/application.xml">
<metainf dir="${build.dir}/META-INF"/>
<fileset dir="${dist.dir}" includes="*.jar,*.war"/>
</ear>
whether this will refer the corresponding war files or i need to compile the whole scenario please let me know. how solve this.
I'm not 100% sure what you're asking.
In order to use the <ear> task, you already need to have compiled the required jars and wars.
If those jars and wars have already been built, you simply refer to them in your <ear> task as you did in your example. The application.xml must already exist before you build your ear. The application.xml doesn't build the jars and wars, you have to do that.
If you haven't already built the wars and jars, you need to do that first. A general outline of a build.xml looks something like this:
<project name="foo" basedir="." default="package">
<!-- Some standard properties you've defined -->
<property name="target.dir" value="${basedir}/target"/>
<property name="xxx" value="yyy"/>
<property name="xxx" value="yyy"/>
<property name="xxx" value="yyy"/>
<!-- Compile properties that allow overrides -->
<property name="javac.nowarn" value="false"/>
<property name="javac.listfiles" value="false"/>
<property name="javac.srcdir" value="source"/>
<property name="javac.distdir" value="${target.dir}/classes"/>
<target name="clean"
description="cleans everything nice and shiny">
<delete dir="${target.dir}"/>
</target>
<target name="compile"
description="Compiles everything">
<mkdir dir="${javac.distdir}"/>
<javac srcdir="${javac.srcdir}"
destdir="${javac.destdir}"
[...]
[...]/>
</target>
<target name="package.jar"
depends="compile"
description="Package jarfile">
<jar destfile="${target.dir}/jarname.jar"
[...]
[...]/>
</target>
<target name="package.jar2"
depends="compile"
description="Package jarfile">
<jar destfile="${target.dir}/jarname2.jar"
[...]
[...]/>
</target>
<target name="package.war"
depends="compile"
description="Package jarfile">
<war destfile="${target.dir}/jarname.jar"
[...]
[...]/>
</target>
<target name="package"
depends="package.jar"
description="Make the ear">
<ear destfile="${target.dir}/earfile.ear"
[...]/>
</target>
</project>
Basically, it consists of a bunch of targets and each target does one task. You can have targets depend upon other targets. For example, this particular build.xml will automatically run the package task. The package task depends upon the package.jar task which depends upon the compile task. Thus, the build.xml file will first call compile, then package.jar, then package.
The important thing to remember is that you don't specify the order of the events. You let Ant figure that out, and you let Ant figure out what you need to do. Let's say you've modified a java source file. Ant knows that it has to recompile only that one file. It also knows that it might have to rebuild the jarfile that contains that classfile. And, it then knows it has to rebuild the ear. Most tasks can figure it out on their own, and you don't do a clean for each build. (You notice that the clean target isn't called by package or compile. You have to call it manually).
The only other thing I recommend is that you try to keep your work area clean. Any files you create should be put into the ${target.dir} directory. That way, when you do a clean, you only have to delete that one directory.
I hope this answer your question.

Ant Copying Empty Directories

I am still very new to ant and, although I know coldfusion, I don't know very much about java conventions, but I know that ant is built using java conventions. That being said I am working on an ant process to copy a project to a temp folder, change some code in the project, and then push the temp directory up to an FTP. I am trying to exclude all of my git, eclipse, and ant files from the copy so that my testing platform doesn't get cluttered. I setup a target to do the copy, but it seems that Ant not only is ignoring my excludes (which I am sure I wrote wrong), but it is only copying top level directories and files. No recursive copy. My current target is:
<target name="moveToTemp" depends="init">
<delete dir="./.ant/temp" />
<mkdir dir="./.ant/temp" />
<copy todir="./.ant/temp">
<fileset dir=".">
<include name="*" />
<exclude name=".*/**" />
<exclude name=".*" />
<exclude name="build.xml" />
<exclude name="settings.xml" />
<exclude name="WEB-INF/**" />
</fileset>
<filterset>
<filter token="set(environment='design')" value="set(environment='testing')" />
</filterset>
</copy>
</target>
I know that I am not doing my excludes right, but I don't know what I am doing wrong with them. I see double asterisks (**) used all the time in Ant but I can't figure out
By default an Ant fileset will (recursively) include all files under the specified directory, equivalent to:
<include name="**/*" />
That's the implicit include. If you supply an include, it overrides the implicit one.
Your include
<include name="*" />
Says 'match any file in the fileset directory', but that excludes traversal of subdirectories, hence your issue. Only files and the top-level directories are being copied.
See Patterns in the Ant docs for directory-based tasks: ** matches any directory tree (zero or more directories).
For your case you should be able to simply remove the 'include', so that the implicit 'include all' applies.
Suggest you also investigate the defaultexcludes task, which lets you set up this sort of thing once for the whole project.
Responding to the title of the question. You can include copy of empty directories as follows. (includeemptydirs attribute)
Example:
<copy includeemptydirs="true" todir="${directory}${file.separator}sentinel_files">
<fileset dir="${basedir}${file.separator}sentinel_files"/>
</copy>
Use the documentation provided in:
https://ant.apache.org/manual/Tasks/copy.html

How do I convert an Ant Path into a FileSet?

I'm writing an Ant script to package a project into a WAR file. The software consists of several projects with their own source directories, libraries, etc.
The WAR task has a nested element lib which I'm currently working on. I currently have a reference of the required libs as a Path (containing several FileSets, which I use in a classpath reference. The lib, however, wants the input to be a FileSet, and it refuses a Path.
I tried converting my Path into a FileSet, but then I didn't get it to work as a classpath elsewhere.
Is there a way to convert a Path into a FileSet? I would hate to copy-paste the directories.
<path id="compile.libs">
<fileset dir="${common.path}/lib" includes="*.jar"/>
<fileset dir="${data.path}/lib" includes="*.jar"/>
<fileset dir="${gui.path}/lib" includes="*.jar"/>
<fileset dir="${gui.path}/WebContent/WEB-INF/lib" includes="*.jar"/>
</path>
...when used with <war ..><../> <lib refid="compile.libs"/> </war> leads to:
BUILD FAILED
build.xml:173: compile.libs doesn't denote a zipfileset or a fileset
Assuming the paths are absolute, you can first convert the Path to a comma-delimited list using <pathconvert>, and then convert the list back into a Fileset:
<!-- create path -->
<path id="foo.path">
<pathelement location="/foo/bar/baz.txt"/>
<pathelement location="/qux/quux/quuux.txt"/>
</path>
<!-- convert foo.path to foo.list -->
<pathconvert
refid="foo.path"
property="foo.list"
pathsep=","
dirsep="/"
>
<!--
<fileset> will want relative paths, so we need to strip
the leading /. result: "foo/bar/baz.txt,qux/quux/quuux.txt"
-->
<map from="/" to=""/>
</pathconvert>
<!-- convert foo.list to fileset -->
<fileset id="foo.fileset" dir="/" includes="${foo.list}"/>
(Note the above assumes Unix; you may need to fiddle a bit with separators and whatnot if you're on Windows or you want to make it platform-independent.)
You may have several choices.
You may provide more than one
<lib> nested element to <war>
task. Maybe this would be enough.
You may preassemble all of your
lib files in one temporary
directory and then just reference that
directory as a fileset.
There is an ant-contrib
PathToFileSet task, but it
requires a central root directory,
and this may not be a case with your
compile.libs layout.
Since Ant 1.8.0 you can use a mappedresources. Source: Ant script: Prevent duplication of JAR in javac-classpath war-lib
I think I would try option 1.
I solved this by staging the libs like this :
<copy todir="stage/libs" flatten="true">
<path refid="classpath" />
</copy>
and then using a in the WAR task.simple.
The jars in the classpath used to compile are not the same that needs to be packaged inside the war. For example: I'm sure you need servlet-api.jar to compile your project but you don't need it inside the war because the container provides it. And some jars aren't needed at compile time but at runtime.
I know I'm not answering your question, just want you to think what you are doing.

Resources