I'm using an Ant build script to collate my Eclipse-based application for distribution.
One step of the build is to check that the correct libraries are present in the build folders. I currently use the Ant command for this. Unfortunately, I have to amend the script each time I switch to a new Eclipse build (since the version numbers will have updated).
I don't need to check the version numbers, I just need to check that the file's there.
So, how do I check for:
org.eclipse.rcp_3.5.0.*
instead of:
org.eclipse.rcp_3.5.0.v20090519-9SA0FwxFv6x089WEf-TWh11
using Ant?
cheers,
Ian
You mean, something like (based on the pathconvert task, after this idea):
<target name="checkEclipseRcp">
<pathconvert property="foundRcp" setonempty="false" pathsep=" ">
<path>
<fileset dir="/folder/folder/eclipse"
includes="org.eclipse.rcp_3.5.0.*" />
</path>
</pathconvert>
</target>
<target name="process" depends="checkEclipseRcp" if="foundRcp">
<!-- do something -->
</target>
A slightly shorter and more straightforward approach with resourcecount condition:
<target name="checkEclipseRcp">
<condition property="foundRcp">
<resourcecount when="greater" count="0">
<fileset file="/folder/folder/eclipse/org.eclipse.rcp_3.5.0.*"/>
</resourcecount>
</condition>
</target>
<target name="process" depends="checkEclipseRcp" if="foundRcp">
<!-- do something -->
</target>
The pathconvert task is probably the preferred way to go in most cases. But it creates a little problem when the directory tree is very large and one uses the echoproperties task. With a very large directory tree, the string generated by pathconvert can be huge. Then echoproperties sprays the huge string, making the output more difficult to work with. I use a macrodef on Linux that creates a property set to "1" if there are files in the directory:
<macrodef name="chkDirContents" >
<attribute name="propertyName" />
<attribute name="dirPath" />
<attribute name="propertyFile" />
<sequential>
<exec executable="sh" dir="." failonerror="false" >
<arg value="-c" />
<arg value='fyles=`ls -1 #{dirPath} | head -1` ; if [ "$fyles" != "" ] ; then echo #{propertyName}=1 > #{propertyFile} ; fi' />
</exec>
</sequential>
</macrodef>
<target name="test" >
<tempfile destdir="." property="temp.file" deleteonexit="true" />
<chkDirContents propertyName="files.exist" dirPath="./target_dir" propertyFile="${temp.file}" />
<property file="${temp.file}" />
<echoproperties/>
</target>
Executing the "test" target will generate the following echoproperties line if there are files in the ./target_dir/ directory:
[echoproperties] files.exist=1
What "test" does:
It generates a temporary filename, ${temp.file}, that can later be used as a property file.
It then executes the macrodef, which calls the shell to check the contents of the dirPath directory. If there are any files or directories in dirPath, it assigns the propertyName property a value of 1 in the temporary file. It then reads the file and sets the property given in the file. If the file is empty, no property is defined.
Note that the temporary file could be reused for subsequent calls of the macrodef if desired. On the other hand, of course, once a property is set, it is immutable.
Related
I've made a simple 'AntExecutor' app in eclipse that can run ant tasks programmatically and it works. But for university purposes I need to keep it independant from IDE. So, funnily, I'm strugling to create ant tasks which would compile,build my 'AntExecutor' app (which executes ant-tasks) :)
Stripped-down version I'm currently trying to define ant-tasks for only contains one source file in 'storageAccess' package:
./src/storageAccess/AntExecutor.java
I've got some libraries that AntExecutor.java makes use of at:
./lib
And the build file is at:
./build.xml
AntExecutor.java also needs ant libraries to execute ant tasks so they're added to CP at compile at. in build file:
<classpath path="${build};D:/DevTools/apache-ant-1.9.8/lib/;"/>
Full build.xml file:
<project name="AntExecutor" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build/classes/"/>
<property name="dist" location="build/jar/"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac destdir="${build}">
<src path="${src}"/>
<classpath path="${build};D:/DevTools/apache-ant-1.9.8/lib/;"/>
</javac>
</target>
<target name="dist" depends="compile"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}"/>
<!-- Put everything in ${build} into RunExecutor.jar file -->
<jar destfile = "${dist}/RunExecutor.jar" basedir="${build}">
<manifest>
<attribute name = "Main-Class" value = "storageAccess.AntExecutor"/>
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/;"/>
</manifest>
</jar>
<copy todir="${dist}\lib">
<fileset dir="lib"/>
</copy>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
now if i run 'ant dist' command I get no errors, build succeeds and RunExecutor.jar file is created at ./build/jar
To check contents of RunExecutor.jar, I ran: jar tf build/jar/RunExecutor.jar
result:
META-INF/
META-INF/MANIFEST.MF
storageAccess/
storageAccess/AntExecutor.class
so it seems like storageAcces.AntExecutor class was indeed successfully compiled to .jar file.
However, if i try running it like this: java -jar build/jar/RunExecutor.jar
I get this error:
Error: Could not find or load main class storageAccess.AntExecutor
Main-question:
How come it can't find the class that is clearly in it.(as 'jar tf' shows) how do I fix this?
Also, what is the corret way to add ant/lib/*.jar files to CP for compiling and running 'RunExecutor.jar' ?
is it okay just to specify the path to them as I do now? :
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/;"/>
or, maybe I should use wildcard like:
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/*.jar;"/>
or, should I frustratingly add all the files one by one?
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/ant.jar;"/> , etc...
The problem with
<attribute name = "Class-Path" value = "D:/DevTools/apache-ant-1.9.8/lib/;"/>
is you are hard-coding path which is not a good practice. This jar will not execute on other machines as there are chances that they won't have lib under same location.
You can directly create executable jar from eclipse project itself. For steps refer this.
You can also put required lib in same jar file and they will by default get added to class-path.
I'm looking for a way to load properties from a file in ant script. Specifically, I want to loop through a list of properties files, and on each loop load the properties from the current file and do something with it. Something like this:
<for param="file">
<path>
<fileset containing my properties files.../>
</path>
<sequential>
<property file="#{file}" prefix="fromFile"/>
<echo message="current file: #{file}"/>
<echo message="property1 from file: ${fromFile.property1}"/>
</sequential>
</for>
The code above results in only the first properties file from being read, even though each loop does go through each properties file name. I know property is immutable, and I can get around it by using local task or variable task from ant-contrib. However, I don't know how to apply them here, or if they even contribute to a solution in this case.
Here I used Antcontrib and two property files in the same directory as the build.xml.
p1.properties:
property1=from p1
p2.properties:
property1=from p2
The trick is to use antcall inside the for loop to call another target. Properties set in the called target at not propagated back to the caller.
build.xml:
<project name="test" default="read.property.files">
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="ant-contrib/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<target name="read.property.files">
<for param="file">
<path>
<fileset dir="." includes="*.properties"/>
</path>
<sequential>
<antcall target="read.one.property.file">
<param name="propfile" value="#{file}"/>
</antcall>
</sequential>
</for>
</target>
<target name="read.one.property.file">
<property file="${propfile}" />
<echo message="current file: ${propfile}" />
<echo message="property1 from file: ${property1}"/>
</target>
</project>
The output is:
Buildfile: /home/vanje/anttest/build.xml
read.property.files:
read.one.property.file:
[echo] current file: /home/vanje/anttest/p1.properties
[echo] property1 from file: from p1
read.one.property.file:
[echo] current file: /home/vanje/anttest/p2.properties
[echo] property1 from file: from p2
BUILD SUCCESSFUL
Total time: 0 seconds
In my original question I had trouble loading entire properties file from within for loop (from ant-contrib). Turns out inside for loops, ant-contrib's own var task works just the same as property task from pure ant. All I have to do is replace <property file="#{file}" prefix="fromFile"/> with <var file="#{file}"/>. The properties loaded will be overwritten with the latest values and I don't even need prefix attribute to keep track of which loop I'm currently on.
I run a script that generates some java code based on definition files. I want to avoid running this task if the definition files have not changed.
<target name="generate" depends="init">
<exec executable="${codeGenTool-path}">
<arg value="${definitionFolder}" />
<arg value="${generatedFolder}" />
</exec>
</target>
I looked at http://ant.apache.org/manual/Tasks/uptodate.html but It seems like I must have a single target file to compare to. The code generation tool creates a folder containing many source files.
This is a good use case for the outofdate task from ant-contrib:
<outofdate>
<sourcefiles>
<fileset dir="${definitionFolder}" />
</sourcefiles>
<targetfiles>
<fileset dir="${generatedFolder}" />
</targetfiles>
<sequential>
<exec executable="${codeGenTool-path}">
<arg value="${definitionFolder}" />
<arg value="${generatedFolder}" />
</exec>
</sequential>
</outofdate>
This will check every file under the definitionFolder against every file under the generatedFolder - you might want to constrain the filesets more tightly, e.g. with includes="**/*.def" or whatever is the relevant file extension.
Alternatively, if you want to avoid "third party" tasks then you could use a dependset task to check the target files against the source ones.
<target name="generate" depends="check.generate, do.generate" />
<target name="check.generate">
<dependset>
<srcfileset dir="${definitionFolder}" />
<targetfileset dir="${generatedFolder}" />
</dependset>
<condition property="gen.required">
<resourcecount count="0" when="equal">
<fileset dir="${generatedFolder}" />
</resourcecount>
</condition>
</target>
<target name="do.generate" if="gen.required">
<exec ....>
</target>
The dependset task deletes all the target files if any of them are older than any of the source files, so we can make do.generate conditional - it will run if there are no files in the generatedFolder, which will be the case when either it's never been run before, or the generated files were out of date.
I am using eclipse, and I am having difficulty in creating jar files.
So I have codes like getClass().getResource("/imagesfolder/dog.jpg").
How would I create Jar files such that the folder containing my images will also be included. Because error occurs if my Jar file is not in my bin folder with the class files and the imagesfolder.
I tried File>Export>Java>Executable Jar>Save in desktop but when I double click it, it does not start. I tried cmd and it worked but with errors that it can't find imagesfolder.
How will I do a jar file in a separate directory that executes with a double click
I have a class TreeIcon; it uses two images, and I store them in a folder 'images' which is within the package of TreeIcon. For whatever reason, I made the package of TreeIcon spacecheck.images (it could just as easily have been com.mycompany.images). Therefore I used following code to access my images:
expandedIcon = new ImageIcon(TreeIcon.class.getResource("images/Expanded.GIF"));
where the 'images' here is the name of the folder containing just the images, not the one that is part of the package. I.E., in my tree structure for the program source, the images are in a folder named spacecheck.images.images.
Note that there's no slash at the start of my string; this means it references a path relative to that of the class. Putting the slash in front of the spec causes getResource to regard the path as absolute within your jar, so I could also have used the string "/spacecheck/images/images/Expanded.GIF".
ANT is The Way
In eclipse you can use Ant to build your .jar file.
From ant.apache.org
Apache Ant is a Java library and command-line tool whose mission is to
drive processes described in build files as targets and extension
points dependent upon each other. The main known usage of Ant is the
build of Java applications. Ant supplies a number of built-in tasks
allowing to compile, assemble, test and run Java applications. Ant can
also be used effectively to build non Java applications, for instance
C or C++ applications. More generally, Ant can be used to pilot any
type of process which can be described in terms of targets and tasks.
Ant is written in Java. Users of Ant can develop their own "antlibs"
containing Ant tasks and types, and are offered a large number of
ready-made commercial or open-source "antlibs".
Ant is extremely flexible and does not impose coding conventions or
directory layouts to the Java projects which adopt it as a build tool.
Software development projects looking for a solution combining build
tool and dependency management can use Ant in combination with Apache
Ivy.
The Apache Ant project is part of the Apache Software Foundation.
Search with google and you will find many documentation, I will show the basic way to do it.
The Build.xml file
First of all create a new file xml, for example "Build.xml" this will be the file that Ant will read.
The you start writing inside it this:
<?xml version="1.0" encoding="UTF-8"?>
This is the basic line you have always to include.
<project name="NameOfYourProject" default="try_jar" basedir=".">
This (with its closing tag </project> at the end of the file, is the main tag, declaring the name of the project and the first task (default) that will be executed, each task is something Ant will do, and is called "Target", you can create a single target that do everything or various target that do few things each, in this case you can create different "flow-chart" that ant will follow. For example I usually create 3 route for Ant: try_jar that is used just to try if all is working in the jar without doing many things, new_version_jar that is the same of try_jar but will update version number, will sign the jar and some other stuff, and javadoc that creates the javadoc for the project. Il will show you the basic try_jar.
<description>
This buildfile is used to build the jar of the game.
</description>
No need to explanation.
<!-- ================= File and Directory Names ==================== -->
<property name="src" location="${basedir}/src" />
<property name="conf" location="${basedir}/conf" />
<property name="build" location="${basedir}/build" />
<property name="dist" location="${basedir}/dist" />
<property name="app.name" value="MyAppName" />
<property name="dist.jarHome" value="${user.home}/MyApplicationMainFolder" />
<property name="app.version" value="0.2" />
<tstamp />
<property name="jar.name" value="${app.name}_${app.version}.${DSTAMP}.jar" />
<property name="jar.completePath" value="${dist.jarHome}/${jar.name}" />
Here we declare the base properties of the jar, we tell it where the source code is, where the build folder should be and so on. We also choose to put all the app in a folder in the base user home (in mac this is /user/UserName/) and create the name for the file that will include the name (obviously) the version and the time when this jar is created. This avoid duplicated or overriding of files that we may want to keep.
<property name="shared.lib" value="${basedir}/lib" />
Here we must specify the directory in which jar files needed by this plugin to run are stored
<!-- =============== Custom Ant Task Definitions =================== -->
<property name="compile.debug" value="true" />
<property name="compile.deprecation" value="false" />
<property name="compile.optimize" value="true" />
This are configuration params for ant
<!-- ================== External Dependencies ======================= -->
<property name="LWJGL" value="lwjgl.jar" />
<property name="Timer" value="timer.jar" />
<property name="Database" value="hsqldb.jar" />
<property name="Splice" value="jarsplice-0.25.jar" />
Here you must specify your external dependencies (something like easymock or powermock if you want to create a test target.
<!-- ================== Compilation Classpath ======================= -->
<path id="compile.classpath">
<fileset dir="${src}">
<include name="**/*.java" />
<exclude name="**/server/*.java"/>
</fileset>
<fileset dir="${shared.lib}">
<include name="**/*.jar" />
</fileset>
</path>
This is what And (with javac command) will build, you have to specify all the folders you want to build and to add (with <fileset>) any jar that is in the buildpath
<!-- =================== All Target ================================ -->
<!-- ================== Try_jar Target ============================ -->
<target name="try_jar" depends="compile, dist, clean_class_files, run" description="Clean build and dist directories, then compile, create jar and finally run" />
This is our target, as specified in "default" the first line, and will run this. Depends tells Ant what it should do before this target. A you can read it will compile, create the jar (dist), remove the class files, and run it.
<!-- ================== Clean Target ============================== -->
<target name="clean" description="Delete old build and dist directories">
<delete dir="${build}" />
<delete dir="${dist}" />
</target>
This is very clear, before to compile a new version we want to remove any old class file to avoid problems. You may think that this is never called, but pay attention to the dependencies of each target.
<!-- ================== Prepare Target ============================= -->
<target name="prepare" depends="clean">
<mkdir dir="${build}" />
<mkdir dir="${build}/classes" />
<mkdir dir="${build}/lib" />
<copy todir="${build}/lib">
<fileset dir="${shared.lib}" includes="${Timer}, ${LWJGL}, ${Database}" />
</copy>
</target>
This prepare the path, creating new needed folders (like build and build/classes) and adding the external dependencies jars.
<!-- ================== Compile Target =========================== -->
<target name="compile" depends="prepare" description="Compile Java sources">
<mkdir dir="${build}/classes" />
<javac srcdir="${src}" destdir="${build}/classes" encoding="8859_1" debug="${compile.debug}" deprecation="${compile.deprecation}" optimize="${compile.optimize}" source="1.6" target="1.6">
<classpath refid="compile.classpath" />
</javac>
</target>
This is the main compiling target, as you can see it depends on prepare (that depends on clean) so until now we are using all <target> tags.
Ant compile .java files using <javac> tag, that needs to know where the source files are, where to put .class files, the encoding, and the three params we specified earlier.
<!-- =================== Dist Target ================================ -->
<target name="dist" description="Creates Jar archive">
<!-- Create the time stamp -->
<tstamp>
<format property="compile.timestamp" pattern="yyyyMMddHHmm" />
</tstamp>
<!-- update version in manifest -->
<replaceregexp file="${basedir}/manifestClient" match="Implementation-Version: .*" replace="Implementation-Version: ${app.version}.${compile.timestamp}" />
<!-- Create Jar file -->
<jar destfile="${jar.completePath}" manifest="${basedir}/manifest">
<fileset dir="${build}/classes" excludes="**/*.bak" />
<fileset dir="${src}/" excludes="mh/" />
<fileset dir="${shared.lib}/native/macosx" />
<zipfileset src="${shared.lib}/${Timer}" />
<zipfileset src="${shared.lib}/${LWJGL}" />
<zipfileset src="${shared.lib}/${Database}" />
</jar>
</target>
this creates the real jar. <tstamp> and <replaceregexp> are used to update the version in the manifest, you can remove them.
Jar tag will create the .jar file, we specified what files to add in the jar that will be avaible to my classes inside. We have also to specify a manifest that will discuss later.
<!-- =================== Delete .class Target===================== -->
<target name="clean_class_files" description="Delete .class files stored inside build directory and dist folder">
<delete dir="${build}" />
<delete dir="${dist}" />
</target>
This target deletes the two folder used to store .class files (and obviously all the files inside).
<!-- ================== Run Target =============================== -->
<target name="run" description="Run MagicHogwarts">
<java jar="${jar.completePath}" fork="true">
</java>
</target>
The end of our build.xml file, that is the run target that runs the jar.
This is almost what you need to compile and and the correct resources to a jar, if something is not like you are expecting, simply try few times and all will go right.
This is the manifest:
Manifest-Version: 1.0
Created-By: 1.6.0 (Sun Microsystems Inc.)
Main-Class: package.to.class.with.main
Built-by: Gianmarco
Implementation-Vendor: Gianmarco
Implementation-Title: Title
I hope this will be useful to you.
I am editing few things to make the post better, but no contents will be different.
Let's say I have a directory structure like this:
animals/dog/details
animals/cat/details
animals/frog/details
animals/horse/details
Using ant, I would like to rename all sub-directories under animals called details to now be named new. So the result would be this:
animals/dog/new
animals/cat/new
animals/frog/new
animals/horse/new
I've tried something like this:
<move tofile="new">
<path id="directories.to.rename">
<dirset dir="animals">
<include name="**/details"/>
</dirset>
</path>
</move>
But get this error:
Cannot concatenate multiple files into a single file.
You can carry out the rename you describe by means of a mapper. For example:
<move todir="animals">
<dirset dir="animals" includes="**/details" />
<globmapper from="*/details" to="*/new"/>
</move>
(There's a similar example at the end of the move task docs.)
The error you saw arose because you've mixed the single-file mode of the move task (tofile) with multiple-file mode.
There's no need to nest the dirset in a path as the move task accepts any file-based resource collection, including dirset.
Use Ant-Contrib's for task and propertyregex task.
<target name="test">
<for param="detailsDir">
<dirset dir="animals">
<include name="**/details"/>
</dirset>
<sequential>
<propertyregex property="output.dir" input="#{detailsDir}" regexp="(.*)/details" replace="\1" />
<move file="#{detailsDir}" toFile="${output.dir}/new" />
</sequential>
</for>
</target>