I have a Java application that I am packaging into a JAR. I created an Ant script to do that as I need to also add resources to the JAR (icons etc).
Now, I have libraries that I use in my project (Apache HttpClient and a JSON library). I also copy their contents into the JAR, as it is the simplest way.
My build file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar"
name="Create Runnable Jar">
<target name="create_run_jar">
<jar destfile="C:/Users/Pietu1998/Documents/Java/Whatever.jar"
filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class"
value="net.pietu1998.whatever" />
<attribute name="Class-Path" value="." />
</manifest>
<fileset dir="C:/Users/Pietu1998/Documents/Java/Whatever/bin"
includes="*.class" />
<fileset dir="C:/Users/Pietu1998/Documents/Java/Whatever/res" />
<zipfileset excludes="META-INF/**"
src="C:/Users/Pietu1998/Documents/Java/Whatever/lib/commons-codec-1.6.jar" />
<zipfileset excludes="META-INF/**"
src="C:/Users/Pietu1998/Documents/Java/Whatever/lib/commons-logging-1.1.3.jar" />
<!-- More (like 5) JARs -->
</jar>
</target>
</project>
However, the libraries (JARs) have their own META-INF folders, and they have a lot of stuff in them; files like LICENSE, CONDITIONS and also a folder for Maven called maven.
The project is for personal use, so I just want to get rid of the unnecessary stuff. I have tried some ways to exclude all of the META-INF stuff, but something is always left behind.
excludes="META-INF" leaves everything.
excludes="META-INF/**" leaves the maven folder.
**/excludes="META-INF/**", see above.
I believe I could use a lot of include-excludes or patternsets, but it would lead to a lot of repeating.
Is there a way (not for this specific case) to exclude a folder (META-INF here) and all its contents including subdirectories, and preferably with not too much repeating (for a lot of libraries)?
Using excludes="META-INF/**"" works for me (Ant 1.9.3, Windows 7, JDK 1.7.0_51)
Some test after downloading original commons-logging-1.1.3.jar from here :
<project>
<zipfileset excludes="META-INF/**" src="c:/area51/commons-logging-1.1.3.jar" id="foobar"/>
<!-- for output line by line -->
<pathconvert property="foo" refid="foobar">
<map from="c:/area51/commons-logging-1.1.3.jar:" to="${line.separator}"/>
</pathconvert>
<echo>${foo}</echo>
</project>
output contains only classfiles under org/apache/commons/logging/.. :
[echo] commons-logging-1.1.3.jar
[echo] org/apache/commons/logging/Log.class;
[echo] org/apache/commons/logging/LogConfigurationException.class;
[echo] org/apache/commons/logging/LogFactory$1.class;
[echo] org/apache/commons/logging/LogFactory$2.class;
[echo] org/apache/commons/logging/LogFactory$3.class;
[echo] org/apache/commons/logging/LogFactory$4.class;
[echo] org/apache/commons/logging/LogFactory$5.class;
[echo] org/apache/commons/logging/LogFactory$6.class;
[echo] org/apache/commons/logging/LogFactory.class;
[echo] org/apache/commons/logging/LogSource.class;
[echo] org/apache/commons/logging/impl/AvalonLogger.class;
[echo] org/apache/commons/logging/impl/Jdk13LumberjackLogger.class;
[echo] org/apache/commons/logging/impl/Jdk14Logger.class;
[echo] org/apache/commons/logging/impl/Log4JLogger.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$1.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$2.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl$3.class;
[echo] org/apache/commons/logging/impl/LogFactoryImpl.class;
[echo] org/apache/commons/logging/impl/LogKitLogger.class;
[echo] org/apache/commons/logging/impl/NoOpLog.class;
[echo] org/apache/commons/logging/impl/ServletContextCleaner.class;
[echo] org/apache/commons/logging/impl/SimpleLog$1.class;
[echo] org/apache/commons/logging/impl/SimpleLog.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$1.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$Entry.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$Referenced.class;
[echo] org/apache/commons/logging/impl/WeakHashtable$WeakKey.class;
[echo] org/apache/commons/logging/impl/WeakHashtable.class
Maybe you forgot to set the update attribute from your jar task to true :
<jar destfile=".." update="true" ..>
to overwrite some already existing jarfile with the same name !?
I found a way to do it with the stars.
<zipfileset excludes="META-INF/**/*" src="C:\library.jar" />
This excludes the META-INF folder and all its contents.
* means it excludes all files inside a folder, and
foo/**/bar means it excludes bar in any level inside foo.
So, foo/**/* excludes every file on every level inside foo.
Related
Need your advice on string replacement using ant propertyregex function.
I actually need to manipulate classpath file content by replacing certain section of the folder to the new folder and and remove some folder paths.
The actual content of the class path looks like:
/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home/folder1/javax/ws/rs/javax.ws.rs-api/2.0-m10/javax.ws.rs-api-2.0-m10.jar:/etc/lib/:/usr/java/lib
which should eventually look like
/var/home/newfolder/metrics-core-3.0.2.jar:/var/home/newfolder/javax.ws.rs-api-2.0-m10.jar:/etc/lib/:/usr/java/lib
Basically, whichever string contains /usr/home/folder1, the new folder name should be prefixed to the jar file. The classpath contains 500 different groupId, artifactId with /usr/home/folder1 as the base
I am trying to replace the string in the classpath property from /usr/home/folder1/ to /var/home/newfolder/ as below. Not sure if something is wrong here, but this totally doesn't work..
<target name="classpath-generate">
<propertyregex property="compileclasspath" input="${compileclasspath}" regexp="/usr/home/folder1/" replace="/var/home/newfolder/" defaultValue="${compileclasspath}"/>
</target>
Also, need some help on how to extract just the jar filename and prefix the new foldername.
Highly appreciate your input on this !!
No need for extra libraries like antcontrib or regular expressions.
Use ant api via script task with builtin JavaScript engine (since JDK 1.6.0_06) like that :
<project>
<property name="compileclasspath" value="/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home:/usr/home/folder1/com/codahale/metrics/metrics-core/bla.jar"/>
<property name="replacefrom" value="/usr/home/folder1/com/codahale/metrics/metrics-core"/>
<property name="replaceto" value="/var/home/newfolder"/>
<echo>
$${compileclasspath} initial :
${compileclasspath}
</echo>
<script language="javascript">
//set newProperty
project.setProperty('foobar', project.getProperty('compileclasspath').replace(project.getProperty('replacefrom'), project.getProperty('replaceto')));
// overwrite existing property
project.setProperty('compileclasspath', project.getProperty('compileclasspath').replace(project.getProperty('replacefrom'), project.getProperty('replaceto')));
</script>
<echo>
$${compileclasspath} changed :
${compileclasspath}
new Property $${foobar} :
${foobar}
</echo>
</project>
output :
[echo] ${compileclasspath} initial :
[echo] /usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home:/usr/home/folder1/com/codahale/metrics/metrics-core/bla.jar
[echo]
[echo] ${compileclasspath} changed :
[echo] /var/home/newfolder/3.0.2/metrics-core-3.0.2.jar:/usr/home:/var/home/newfolder/bla.jar
[echo] new Property ${foobar} :
[echo] /var/home/newfolder/3.0.2/metrics-core-3.0.2.jar:/usr/home:/var/home/newfolder/bla.jar
[echo]
To extract the jar names you might use something like :
<project>
<property name="compileclasspath" value="/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home:/usr/home/folder1/com/codahale/metrics/metrics-core/bla.jar"/>
<script language="javascript">
<![CDATA[
var cpitems = project.getProperty('compileclasspath').split(':');
var jars = "";
for (i=0; i < cpitems.length; i++) {
if(cpitems[i].split('/')[(cpitems[i].split('/')).length -1].endsWith('.jar'))
{
jars += cpitems[i].split('/')[(cpitems[i].split('/')).length -1] + ','
}
}
project.setProperty('cpjars', jars.substring(0, jars.length - 1));
]]>
</script>
<echo>$${cpjars} => ${cpjars}</echo>
</project>
output :
[echo] ${cpjars} => metrics-core-3.0.2.jar,bla.jar
Below is an Ant script that uses Ant's built-in file name mapping functionality:
build.xml
<project name="ant-classpath-mapper" default="run">
<property
name="original-classpath"
value="/usr/home/folder1/com/codahale/metrics/metrics-core/3.0.2/metrics-core-3.0.2.jar:/usr/home/folder1/javax/ws/rs/javax.ws.rs-api/2.0-m10/javax.ws.rs-api-2.0-m10.jar:/etc/lib/:/usr/java/lib"
/>
<target name="run">
<pathconvert property="modified-classpath">
<path>
<pathelement path="${original-classpath}"/>
</path>
<firstmatchmapper>
<regexpmapper
from="/usr/home/folder1/.*?([^/]+\.jar)$"
to="/var/home/newfolder/\1"
/>
<identitymapper/>
</firstmatchmapper>
</pathconvert>
<echo>${modified-classpath}</echo>
</target>
</project>
Output
run:
[echo] /var/home/newfolder/metrics-core-3.0.2.jar:/var/home/newfolder/javax.ws.rs-api-2.0-m10.jar:/etc/lib:/usr/java/lib
The <regexpmapper> matches the paths that start with "/usr/home/folder1".
The <regexpmapper> won't match paths such as "/etc/lib/" and "/usr/java/lib". The <firstmatchmapper> ensures that these paths will get matched by the following <identitymapper>.
Let me first provide the background of the problem I'm facing.
I have a directory structure as below.
c:\myDirectory
c:\myDirectory\Project1
c:\myDirectory\Scripts
Under the c:\myDirectory\Scripts there is a script that download the source code (from svn) and creates the c:\myDirectory\Project1 directory.
I have another ant scripts ( c:\myDirectory\Scripts**compile-source.xml ) that compiles the Project1
from an ant script build.xml that is downloaded to c:\myDirectory\Project1
Snippet for c:\myDirectory\Scripts\compile-source.xml
<project name="compile" default="buildAll" basedir=".">
<property file=".\build.properties">
</property>
.......
<import file="${project.home.path}/${project.name}/build.xml"/>
<target name="buildAll">
<antcall target="jar-pack"/>
</target>
</project>
Snippet for c:\myDirectory\Project1\build.xml.
<project name="CommonFeatures" default="jar-pack" basedir=".">
<description>
A build file for the Common Features project
</description>
....
</project>
Note that the basedir for the project is set as "." for both the above ant scripts.
When I execute the script c:\myDirectory\Scripts\compile-source.xml from the c:\myDirectory\Scripts directory the target "jar-pack" present in the c:\myDirectory\Project1\build.xml gets executed.
However, the problem is that basedir attribude in build.xml ( basedir="." ) is the current working directory and in this case its c:\myDirectory\Scripts. Hence the script build.xml errors out since the basedir for build.xml is expected to be c:\myDirectory\Project1. The build.xml script would have worked, if basedir="." were set to "c:\myDirectory\Project1", but unfortunately build.xml file comes from the source code that is downloaded and I'm unable to edit.
So here's my question, Is it possible to do any of the following.
Override the value of the attribude basedir="." in build.xml when the is done in c:\myDirectory\Scripts\compile-source.xml ?
Is it possible to change the basedir in build.xml by any other mechanism so that the script c:\myDirectory\Project1\build.xml is executed under directory c:\myDirectory\Project1 ?
Any other way to resolve this issue?
Any help from Ant experts to overcome this issue is highly appreciated.
You can update basedir using subant task. Check this answer
Create the following build.xml file (assuming it is in Z:/any/folder):
<?xml version="1.0" encoding="UTF-8"?>
<project name="project">
<target name="mytarget">
<subant target="debug">
<property name="basedir" value="X:/any/dir/with/project"/>
<fileset dir="Y:/any/folder/with" includes="build.xml"/>
</subant>
</target>
</project>
The you can execute ant mytarget from Z:/any/folder
You can specifically reference the location of your build file, which is described in this stack overflow thread. This would allow you to get and use the directory your build file resides in as a reference point.
For your case the usage of the subant or ant tasks may be better suited, but nevertheless...
You can (but you should know/consider the side-effects!) extend ant with the common ant-contrib task definitions and use the var task which is able to override properties. Make sure to use the latest version (> 1.0b3).
<!-- adjust to your path and include it somewhere at the beginning of your project file -->
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="lib/ant-contrib-1.0b3.jar" />
<!-- works e.g. for basedir = /foo/bar to update it to /foo/bar/.. ~ /foo -->
<var name="basedir" value="${basedir}/.." />
update: but one has to be careful, because this does not change . (current working directory) (so <property name="x" location="tmp" /> would be relative to . and not to basedir anymore ; update: setting basedir outside of ant or via <project basedir= also sets . to basedir!). Here is some test target proving the effect on both:
<target name="tst.dummy.basedir-override">
<!-- example output:
tst.dummy.basedir-override:
[echo] basedir before: basedir=D:\tst, '.'=D:\tst\.
[echo] updating it via 'var' to '..'
[echo] basedir now: basedir=D:\tst/.., '.'=D:\tst\.
-->
<property name="cur" location="." /> <!-- makes the relative path absolute -->
<echo message="basedir before: basedir=${basedir}, '.'=${cur}" />
<echo message="updating it via 'var' to '..'" />
<var name="basedir" value="${basedir}/.." />
<property name="cur2" location="." /> <!-- makes the relative path absolute -->
<echo message="basedir now: basedir=${basedir}, '.'=${cur2}" />
</target>
I'm using this because the enhancer plugin couldn't handle long CLASSPATHs on Winodws so using the plugin isn't a valid option.
My module.xml file is pretty much the default file but I only removed the enhance target dependency on compile and copy.metadata. I'm not compiling my .java classes because:
I have many libraries to include and it's a headache if I upgrade any set of libraries.
I don't need to because eclipse actually does that
And I removed the part that archives the whole project folder because I'm just building a JAR.
-->
<echo message="==================================================================="/>
<echo message="Enhancing the MODULE files"/>
<echo message="==================================================================="/>
<!-- the classpath to enhance -->
<path id="module.enhancer.classpath">
<pathelement location="${module.classes.dir}"/>
<path refid="module.lib.classpath"/>
</path>
<!-- define the task enhancer -->
<taskdef name="nucleusenhancer"
classpathref="module.enhancer.classpath"
classname="org.datanucleus.enhancer.tools.EnhancerTask" />
<!-- enhance -->
<nucleusenhancer classpathref="module.enhancer.classpath"/>
</target>
<!--
===================================================================
TARGET : modulearchive
===================================================================
-->
<target name="modulearchive" depends="enhance">
<mkdir dir="${module.archive.dist.dir}"/>
<delete file="${module.archive.dist.dir}/${module.archive.dist.file}"/>
<zip zipfile="${module.archive.dist.dir}/${module.archive.dist.file}">
<zipfileset dir="${module.classes.dir}" prefix="" includes="**/*"/>
</zip>
</target>
Also I'm having this warning that wouldn't go away no matter what I do!
<taskdef name="nucleusenhancer"
classpathref="module.enhancer.classpath"
classname="org.datanucleus.enhancer.tools.EnhancerTask" />
>>>E:\Projects\Core - Module\${module.lib.dir} does not exist.
My module.properties file isn't changed much. I commented properties for WAR building and uncommented properties for JAR building:
#######################################
# SAMPLE FOR JAR MODULE (commented out)
#######################################
module.dir=./
module.lib.dir=${module.dir}/lib/dn
module.lib2.dir=${module.dir}/lib/deps
module.classes.dir=${module.dir}/target/classes
module.src.java.dir=./src/main/java
module.build.debug=on
module.archive.dist.dir=dist
module.archive.dist.file=${name}.jar
module.archive.files=**/*
The folder lib beneath my project's folder holds 2 folders, datanucleus main jars for RDBMS while the other folder holds datanucleus dependencies. My compiled classes are beneath target.
The output is simply the following:
Buildfile: E:\Projects\Core - Module\build.xml
default:
[echo] ===================================================================
[echo] Welcome to the build.
[echo] ===================================================================
enhance:
[echo] ===================================================================
[echo] Enhancing the MODULE files
[echo] ===================================================================
[nucleusenhancer] log4j:WARN No appenders could be found for logger (DataNucleus.Enhancer).
[nucleusenhancer] log4j:WARN Please initialize the log4j system properly.
[nucleusenhancer] DataNucleus Enhancer (version 3.1.0.release) : Enhancement of classes
[nucleusenhancer] DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details
modulearchive:
[zip] Building zip: E:\Projects\Core - Module\dist\my-module-name.jar
distribution:
[echo] ===================================================================
[echo] Module file ready at: dist/my-module-name.jar
[echo] ===================================================================
BUILD SUCCESSFUL
Total time: 2 seconds
The enhancement is an actual modification to the compiled classes. If you are not compiling the classes first, there will be nothing for it to enhance.
I have a following directory structure
root_dir
fixed_dir
random_dir
subdir1
subdir2
subdir2.1
subdir3
subdir3.1
subdir3.2
In the ANT build file I know the root_dir, fixed_dir, and one directory that is either random_dir or a subdirectory below random_dir (subdirX). I need to determine the path of random_dir given some subdirX. Is it possible to find this directory in ANT and if so, how?
Here is a tested solution for finding the immediate subdirectory of a root directory that contains some subdirectory subdirX at any level of nesting given the file structure provided in the question.
<property name="root.dir" location="${basedir}/root_dir" />
<property name="subdirX" value="subdir2.1" />
<target name="find-immediate-subdir-of-root-containing-subdirX">
<dirset dir="${root.dir}" includes="**/${subdirX}" id="mydirset" />
<pathconvert property="random_dir" pathsep="${line.separator}" refid="mydirset">
<mapper type="regexp"
from="^(${root.dir}${file.separator}[^${file.separator}]+).*"
to="\1"/>
</pathconvert>
<echo message="${random_dir}" />
</target>
Output
find-immediate-subdir-of-root-containing-subdirX:
[echo] /ant/project/basedir/root_dir/random_dir
BUILD SUCCESSFUL
Total time: 1 second
With Ant addon Flaka you'll get the parent as property of a file object (see Flaka Manual, section 3.7.2, i.e.
<project xmlns:fl="antlib:it.haefelinger.flaka">
<!-- let standard ant tasks, i.e. echo
understand EL expresssions -->
<fl:install-property-handler />
<echo>#{file('${basedir}').parent}</echo>
<!-- or without fl:install-property-handler use fl:echo-->
<fl:echo>#{file('${basedir}').parent}</fl:echo>
</project>
so you would use :
#{file('${yoursubdir}').parent
Using Ant, how can I list sub folders only from the first level, and not going down the directory tree?
say I have:
dir1
-- dir21
----dir211
-- dir22
<dirset dir="dir1"/>
will list all dirs including dir211.
How can I avoid that?
Use dirset like that :
<dirset dir="dir" includes="*"/>
or
<dirset dir="dir1">
<include name="*"/>
</dirset>
EDIT after comment
includes attribute and nested include name should be equivalent, here's some
snippet working on my windows machine, given C:\foo\bar :
<project>
<echo>
${ant.version}
${java.version}
${os.name}
</echo>
<dirset dir="c:/foo" includes="*" id="foobar" />
<echo>${toString:foobar}</echo>
<dirset dir="c:/foo" id="foobaz">
<include name="*" />
</dirset>
<echo>${toString:foobaz}</echo>
</project>
output :
Buildfile: C:\rosebud\AntTest\tryme.xml
[echo] Apache Ant(TM) version 1.8.2 compiled on December 20 2010
[echo] 1.7.0_02
[echo] Windows 7
[echo]
[echo] bar
[echo] bar
BUILD SUCCESSFUL
I wanted to include a date selector in my dirset and only choose the sub directories of my root directory so that meant I couldn't use includes="*". My solution was to use the <depth> selector
<dirset dir="/myroot" excludes="*/*/**">
<date datetime="${cuttoff_time}"
pattern="${timeformat}"
when="before"
checkdirs="true" />
<depth max="1" />
</dirset>