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.
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.
Say I have a library and a binary target, let's call them MyLib and MyBin,
MyBin depends on MyLib.
I'm trying to create an Ant buildfile for MyBin that first builds MyLib and then includes it in the classpath when building MyBin.
I've tried using Ant tasks as in Building other, dependent projects with Ant .
However, it's not working, and from ant -v I think the MyBin build-deps target is not even building MyLib. Seems like it's confusing MyBin and MyLib properties? I'm not sure how to prevent this though.
I'm dumping only MyBin/build.xml below, but the MyLib is almost identical, except it does not have the build-deps target.
<project name="MyBin" default="main" basedir=".">
<property name="projectName" value="MyBin" />
<property name="src.dir" location="src" />
<property name="build.dir" location="bin" />
<property name="dist.dir" location="dist" />
<property name="dist.lib.dir" location="dist/lib" />
<property name="lib.dir" value="lib" />
<target name="build-deps" depends="init">
<!-- MyLib main target does clean -> build -> jar to dist folder -->
<!-- Its build.xml uses many of the same property values as above -->
<ant antfile="../MyLib/build.xml" target="main"/>
</target>
<path id="classpath">
<fileset dir="${basedir}/">
<include name="../MyLib/dist/**/*.jar" />
</fileset>
</path>
<!-- Need classpath to run this -->
<target name="compile" depends="build-deps" description="compile the source ">
<javac includeantruntime="false" srcdir="${src.dir}"
destdir="${build.dir}" classpathref="classpath" />
</target>
<!-- Group all dependencies into a big dependency-all.jar -->
<target name="copy-dependencies">
<mkdir dir="${dist.lib.dir}" />
<jar jarfile="${dist.lib.dir}/dependencies-all.jar">
<zipgroupfileset dir="${lib.dir}">
<include name="**/*.jar" />
</zipgroupfileset>
</jar>
</target>
<!-- jar it, extract above dependency-all.jar and zip it with project files -->
<target name="jar" depends="compile, copy-dependencies"
description="package, output to JAR">
<mkdir dir="${dist.dir}" />
<mkdir dir="${dist.lib.dir}" />
<jar jarfile="${dist.dir}/${projectName}.jar" basedir="${build.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}" />
</manifest>
<zipfileset src="${dist.lib.dir}/dependencies-all.jar"
excludes="META-INF/*.SF" />
</jar>
</target>
<target name="clean" description="clean up">
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>
<!-- Default, run this -->
<target name="main" depends="clean, compile, jar" />
</project>
What I see with ant -v in MyBin is something along the lines of:
build-deps:
Project base dir set to: /MyBin
[ant] calling target(s) [main] in build file /MyLib/build.xml
parsing buildfile /MyLib/build.xml with URI = file:/MyLib/build.xml
Project base dir set to: /MyBin
Override ignored for property "projectName"
Override ignored for property "build.dir"
Override ignored for property "dist.dir"
Override ignored for property "dist.lib.dir"
Override ignored for property "lib.dir"
[pathconvert] Set property classpath.name =
[ant] Entering /MyLib/build.xml...
Build sequence for target(s) `main' is [clean, init, copy-dependencies, jar, main]
Complete build sequence is [clean, init, copy-dependencies, jar, main, ]
clean:
[delete] Deleting directory /MyBin/bin
[delete] Deleting directory /MyBin/bin
init:
[mkdir] Created dir: /MyBin/bin
copy-dependencies:
[ant] Exiting /MyLib/build.xml.
On your specific question:
seems like it's confusing MyBin and MyLib properties? I'm not sure how
to prevent this though.
You are using this to invoke the MyLib build:
<ant antfile="../MyLib/build.xml" target="main" />
A build invoked via the <ant> task this way by default inherits all properties from the caller, including basedir, hence the build is run in the wrong place.
Instead you could use, for example:
<ant dir="../MyLib" />
That will run build.xml in the specified directory, set the basedir property, and call the default target, which should be main if you are using a very similar buildfile for the library as you say. If you don't want to inherit properties from MyBin when executing the MyLib task, specify inheritAll=false in the task.
From the <ant> task docs for the dir attribute:
the directory to use as a basedir for the new Ant project (unless
useNativeBasedir is set to true). Defaults to the current project's
basedir, unless inheritall has been set to false, in which case it
doesn't have a default value. This will override the basedir setting
of the called project. Also serves as the directory to resolve the
antfile and output attribute's values (if any).
In my previous post I was trying to implement macrodef in order to substitute repetitive code block with "function like" task. Then I faced some problems. After that I reduced the script, so I can experiment with the tasks I am not used with. Here is what I made up:
<project basedir="../../../" name="do-report" default="extract-common-paths">
<xmlproperty keeproot="false" file="implementation/xml/ant/properties.xml"/>
<!-- -->
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="${infrastructure-base-dir}/apache-ant-1.9.6/lib/antcontrib.jar"/>
</classpath>
</taskdef>
<!-- -->
<macrodef name="get-common-path">
<attribute name="common-path-property"/>
<attribute name="file"/>
<attribute name="file-base-dir"/>
<sequential>
<local name="file-dir-absolute-path"/>
<dirname property="file-dir-absolute-path" file="#{file}"/>
<property name="file-base-dir-absolute-path" location="#{file-base-dir}"/>
<echo>MACRODEF FILE: ${file-dir-absolute-path}</echo>
<echo>MACRODEF FILE-BASE-DIR: ${file-base-dir-absolute-path}</echo>
<pathconvert property="#{common-path-property}" dirsep="/">
<path location="${file-dir-absolute-path}"/>
<map from="${file-base-dir-absolute-path}/" to=""/>
</pathconvert>
</sequential>
</macrodef>
<!-- -->
<target name="clean">
<delete dir="${dita-odt.path.odt-unzipped-base-dir}" includeemptydirs="true" failonerror="no"/>
<delete dir="examples/intermediate/odt-files" includeemptydirs="true" failonerror="no"/>
</target>
<!-- -->
<target name="unzip-writing-odt-file" depends="clean">
<unzip src="${dita-odt.path.writing-odt}" dest="${dita-odt.path.writing-odt-unzipped}"/>
</target>
<!-- -->
<target name="extract-common-paths" depends="unzip-writing-odt-file">
<for param="file">
<path>
<fileset dir="${dita-odt.path.text-xml-base-dir}">
<include name="**/content.xml"/>
</fileset>
</path>
<sequential>
<get-common-path common-path-property="common-path" file="#{file}" file-base-dir="${dita-odt.path.text-xml-base-dir}"/>
<echo>THIS IS THE PATH: ${common-path}</echo>
</sequential>
</for>
</target>
</project>
The FOR task iterates over two files in different directories. Actually FOR works as expected. It passes ${file} as it should. macrodef attribute common-path-property is first set to the converted path of the first file (it is OK). But when the second file is passed to the get-common-path/#file, the attribute common-path-property doesn't change its value and again I receive this:
[echo] MACRODEF FILE: C:\Users\rmrd001\git\xslt-framework\examples\text\t1\t1.1
[echo] MACRODEF FILE-BASE-DIR: C:\Users\rmrd001\git\xslt-framework\examples\text
[echo] THIS IS THE PATH: t1/t1.1
[echo] MACRODEF FILE: C:\Users\rmrd001\git\xslt-framework\examples\text\t2\t2.1
[echo] MACRODEF FILE-BASE-DIR: C:\Users\rmrd001\git\xslt-framework\examples\text
[echo] THIS IS THE PATH: t1/t1.1
Instead I hope to receive this:
[echo] MACRODEF FILE: C:\Users\rmrd001\git\xslt-framework\examples\text\t1\t1.1
[echo] MACRODEF FILE-BASE-DIR: C:\Users\rmrd001\git\xslt-framework\examples\text
[echo] THIS IS THE PATH: t1/t1.1
[echo] MACRODEF FILE: C:\Users\rmrd001\git\xslt-framework\examples\text\t2\t2.1
[echo] MACRODEF FILE-BASE-DIR: C:\Users\rmrd001\git\xslt-framework\examples\text
[echo] THIS IS THE PATH: \t2\t2.1
I hope you understand what I am trying to do. Thank you in advance!
Try printing the macrodef attribute values instead of the property values.
#{attribute} - can change with every execution of a macrodef
${property} - are always set and fixed once and once only in Ant, even within a macrodef
unless preceded by a <local> task, in which case the locally set property value is used within the scope of that block.
Probably what you want is provided by the Ant Local task.
Adds a local property to the current scope. Property scopes exist at
Apache Ant's various "block" levels.
When I use a path reference ID, Ant seems to evaluate any variables inside the definition before any tasks run. For example, ${common.dist} and ${common.lib} below seem to be evaluated before any tasks run.
<path id="compile.classpath">
<fileset dir="lib">
<include name="*.jar" />
</fileset>
<fileset dir="${common.dist}">
<include name="*.jar" />
</fileset>
<fileset dir="${common.lib}">
<include name="*.jar" />
</fileset>
</path>
In the Ant output I see something like this:
Adding reference: compile.classpath
Property "common.dist" has not been set
Property "common.lib" has not been set
...
Build sequence for target(s) `package' is [...]
Complete build sequence is [...]
This makes it seem like the path reference is being processed before any targets are run.
I have a compile target like this:
<target name="compile" depends="init,common">
<javac destdir="build/classes" debug="true" deprecation="true" optimize="true">
<src path="src/java" />
<classpath>
<path refid="compile.classpath" />
</classpath>
</javac>
</target>
If I copy the guts of the path reference into the classpath element inside the compile target, things seem to work fine.
Any tasks outside a target are executed on every build, in order of appearance in the build.xml, before any targets are run. If you want to use properties in a <path> defined outside a target then you need to put the <property> task that defines the properties also outside a target, and before the <path>. If you need to load the properties within a target then you'll have to put the <path> definition inside a target too (either the same one or one that runs after the one defining the properties).
See this question (and my answer) for more details.
The answer is in the Ant manual - path like structures:
By default a path like structure will re-evaluate all nested resource
collections whenever it is used, which may lead to unnecessary
re-scanning of the filesystem ...
I think you maybe forgot to set the ${common.dist} and ${common.lib} properties. They should be outside any target:
<property name="common.dist" location="dist"/>
<property name="common.lib" location="lib"/>
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.