Ant optional classpath element - ant

I have some modules and a main runnable project. I have common build file, and
build.common.xml
<target name="build" >
<path id="libraries.classpath">
<fileset dir="${lib.dir}" includes="*.jar" />
</path>
<javac srcdir="${src.dir}" destdir="${build.dir}" includeantruntime="false" source="1.6">
<classpath refid="libraries.classpath" />
<classpath refid="modules.classpath" />
</javac>
</target>
..and every module declares its own dependencies in their build.xml:
<path id="modules.classpath">
<pathelement path="../ModuleA/${build.dir}" />
...
</path>
The problem is if there is no internal dependency I get the following exception:
"Reference modules.classpath not found."
What is the solution for that? How could I declare an optional classpath element?
Note: If anybody want to suggest to create jars out of my modules, please justify this. I'm going to have 5-10 rapidly changing modules, and I don't want to do unnecesary steps in the build process.
Update: I extracted the build into two different targets and created a condition for them, but did not help (it echoes the 'false' and builds with module-dependencies):
<target name="build">
<condition property="modules.classpath.set" else="false">
<isset property="modules.classpath"/>
</condition>
<echo message="modules.classpath is set: ${modules.classpath.set} " />
<antcall target="build-with-modules" />
<antcall target="build-without-modules" />
</target>
<target name="build-with-modules" if="modules.classpath.set">
<echo message="Building with module-dependencies" />
<javac srcdir="${src.dir}" destdir="${build.dir}" includeantruntime="false" source="1.6">
<classpath refid="libraries.classpath" />
<classpath refid="modules.classpath" />
</javac>
</target>
<target name="build-without-modules" unless="modules.classpath.set">
<echo message="Building with no dependent modules" />
<javac srcdir="${src.dir}" destdir="${build.dir}" includeantruntime="false" source="1.6">
<classpath refid="libraries.classpath" />
</javac>
</target>

Condition isreference:
Test whether a given reference has been defined in this project and - optionally - is of an expected type.
So, try
<condition property="modules.classpath.set" else="false">
<isreference refid="modules.classpath"/>
</condition>
Also on that page, there is a link to a page that describes custom conditions. If none of the provided conditions meets your requirement, then just write one.
Update:
The logic of if and unless in <target> is to check if the property has been set -- for if, the target runs when the property has been set; for unless, the target runs when the property has NOT been set -- not the value of the property.
I have never checked the code of the condition isreference, but I think maybe the else="false" should be removed.
If removing that part still doesn't help, then you may need to use some embedded groovy or beanshell script, or write your own condition.

Related

Change values of list in ANT

I need to change the values of an ANT-script list in real time.
This is the situation;
I have these properties:
x.y.6.1=something1
x.y.6.2=something2
x.y.6.3=something3
list=6.1,6.2
I want the list to become list=something1;something2
This is the gist of the code;
<target name="target1">
<foreach list="${list}" target="target2" param="var" delimiter="," />
</target>
<target name="target2">
<propertycopy name="var" from="x.y.${var}" silent="true"/>
</target>
Now, the propertycopy part works, however, it will not keep the new value.
I tried many variations, none which worked.
I am using ant-contrib.
Help would be much appreciated!
Adam
The target attribute of your foreach should be the name of the target called.
I guess here it should be <foreach list="${list}" target="agent_version_to_path" param="var" delimiter="," />
If I'm wrong, post your target2 and explain what you're trying to do.
Edit:
Ok for your edit, did you already try override="yes"?
And cannot you change your name of property (var) it is quite confusing!
I'm not a fan of the ant-contrib tasks. Have you considered embedding a scripting language instead?
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>
<groovy>
properties["list"].split(",").each {
println properties["x.y.${it}"]
}
</groovy>
Update
Here's a more complete example that loops and calls another target:
$ ant
Buildfile: build.xml
process:
doSomething:
[echo] something1
doSomething:
[echo] something2
BUILD SUCCESSFUL
Total time: 0 seconds
build.xml
<project name="demo" default="process">
<property file="build.properties"/>
<path id="build.path">
<pathelement location="lib/groovy-all-2.1.5.jar"/>
</path>
<target name="process" description="Process values in a list">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>
<groovy>
properties["list"].split(",").each {
properties.var = properties["x.y.${it}"]
ant.ant(target:"doSomething")
}
</groovy>
</target>
<target name="doSomething">
<echo>${var}</echo>
</target>
</project>
I have solved the problem, in an icky way, but it works great!
<project name="Test" default="main">
<property file="agent.properties" />
<property file="temp_updates.txt" />
<taskdef name="propertycopy" classname="net.sf.antcontrib.property.PropertyCopy" />
<taskdef name="foreach" classname="net.sf.antcontrib.logic.ForEach" />
<target name="main">
<property name="Agent Updates" value="6.1,6.2" />
<antcall target="create_temp_files" />
<antcall target="agent_updates_target" />
<propertycopy name="custom.agent.release.group" from="updates" silent="true" override="true" />
</target>
<target name="agent_updates_target">
<foreach list="${Agent Updates}" target="agent_version_to_path" param="var" delimiter="," />
</target>
<target name="agent_version_to_path">
<propertycopy name="var" from="agent.installer.${var}" silent="true" override="true"/>
<echo message="${var};" file="temp_updates.txt" append="true" />
</target>
<target name="create_temp_files">
<echo message="updates=" file="temp_updates.txt" />
</target>
</project>
on another file, "agent.properties" I had that;
agent.installer.6.3=something3
agent.installer.6.2=something2
agent.installer.6.1=something1
agent.installer.6.0=...
agent.installer.5.6=...
agent.installer.5.0.12=...
agent.installer.5.0.11=...
agent.installer.5.0.9.5=...
agent.installer.3.8=...
agent.installer.3.7=...
As a result, a new file "temp_updates.txt" was created, having
updates=something1;something2;
Which I then loaded into the actual program.
May not be pretty, but it works quite well.
Thank you Skoll and Mark O'Connor for all your help, I used those ideas to come up with this one. I would rate you, but I can't :( Sorry!

ant javac can't find other project compiled classes

The following is a simplified version of my ant script (it's got the project element etc).
I'm new to ant and unable to figure out why 'compileTests' doesn't compile, whereas 'compileFoo' does.
The error I get is 'package does not exist' as the class in the compileTests project can't find the compiled classes in the compileFoo project, even though they've compiled fine, i can see them on the file system and the path to them is listed in the classpath (i assume this is necessary?)
Clearly there is something basic I don't understand. Can someone please help by explaining?
<path id="build_classpath">
<fileset dir="${other_required_jars}" includes="**/*.jar" />
<fileset dir="${foo_build_location}" includes="**/*.class" />
</path>
<target name="compileFoo" description="compile">
<javac srcdir="${foo_source_directory}\test-src" includeantruntime="false" destdir="${foo_build_location}" includes="**/*.java" excludes="" debug="on" optimize="off" deprecation="on" verbose="on">
<classpath refid="build_classpath" />
</javac>
</target>
<target name="compileTests" description="compile">
<javac srcdir="${test_source_directory}\test-src" includeantruntime="false" destdir="${test_build_location}" includes="**/*.java" excludes="" debug="on" optimize="off" deprecation="on" verbose="on">
<classpath refid="build_classpath" />
</javac>
</target>
Your classpath is wrong. A classpath doesn't contain a set of .class files. It cntains a set of jar or directories, each containing the root of a package tree. So the classpath should simply contain one element : ${foo_build_location}:
<path id="build_classpath">
<fileset dir="${other_required_jars}" includes="**/*.jar" />
<pathelement location="${foo_build_location}"/>
</path>

How to make ant not fail if folder on classpath is not found

I'm trying to modify my ant script so that it will build without error whether or not a local lib folder exists. I want to use the same script on multiple wars, some of which will have WEB-INF/lib, and some of which won't. If the folder exists, include it in the classpath, if not, do not include it. I have tried putting but I can't figure out where it should go. I think this should be a lot simpler than I'm making it out to be but my Googl Fu is failing me.
<property name="local.libs" value="WebContent/WEB-INF/lib" />
<path id="local.libs.path">
<fileset dir="${local.libs}" includes="*.jar" />
</path>
<target name="compile">
<mkdir dir="${build.classes.dir}"/>
<javac srcdir="${src.java.dir}" destdir="${build.classes.dir}" debug="true" includeantruntime="false">
<compilerarg value="-Xlint:-path" />
<classpath refid="local.libs.path" />
<classpath refid="server.libs.path" /> <!-- not referenced in snippet -->
</javac>
</target>
I ended up solving this by making the value of local.libs just WebContent/WEB-INF:
<property name="local.libs" value="WebContent/WEB-INF" />
and then the fileset
<fileset dir="${local.libs}" includes="*lib/*.jar" />
Then it would build whether or not the lib folder existed.

ant + javac + properties

In an ant script, I would like to compile only certain packages e.g.
com.example.some_package.foo
com.example.some_package.bar
This is what I want to do, but it doesn't seem to work, because property substitution doesn't seem to work in the <include> tag:
<property name="ROOT_PKG_PATH" location="com/example/some_package"/>
...
<target name="compile-client" depends="init">
<javac srcdir="${srcDir}"
destdir="${buildDir}"
debug="on"
target="1.5"
classpathref="build.classpath">
<include name="${ROOT_PKG_PATH}/foo/**" />
<include name="${ROOT_PKG_PATH}/bar/**" />
</javac>
</target>
How can I get around this without having to retype the entire package path of each package?
Use the value attribute on the property, instead of location:
<property name="ROOT_PKG_PATH" value="com/example/some_package"/>
Example
I'm able to conditionally compile one of my java classes:
./src/some_package/demo1/Demo.java
./src/some_package/demo2/Demo.java
./build/classes/somepackage/demo1/Demo.class
./build.xml
Using the following ANT file:
<project name="demo" default="compile">
<property name="prop" value="some_package/demo1"/>
<target name="compile">
<mkdir dir="build/classes"/>
<javac srcdir="src" destdir="build/classes">
<include name="${prop}/**"/>
</javac>
</target>
</project>

Testing Macrodef Attribute without IF

I am attempting to remove all lines that begin with log if a macrodef attribute is set to prod (example below). I plan on using replaceregexp to remove all lines beginning with log. However, I am not sure how to test if an attribute is set to a specific value, besides using the if task. I would like to not introduce any non-core Ant tasks to perform this, but I can't come up with any other solutions. Do I have any other options besides using the if-task?
Thanks
<macrodef name="setBuildstamp">
<attribute name="platform" />
<sequential>
<if>
<equals arg1="platform" arg2="prod" />
<then>
<replaceregexp match="^log\(.*" value="" />
</then>
</if>
</sequential>
</macrodef>
You should use a reference to a parameter, like this #{platform}.
Also, your replaceregexp task is missing a few parameters.
I think that in your particular case it is better to use linecontainsregexp filter reader. Here is modified code (note negate argument to linecontainsregexp).
<macrodef name="setBuildstamp">
<attribute name="platform" />
<sequential>
<if>
<equals arg1="#{platform}" arg2="prod" />
<then>
<copy todir="dest-dir">
<fileset dir="src-dir"/>
<filterchain>
<linecontainsregexp
regexp="^log\(.*"
negate="true"
/>
</filterchain>
</copy>
</then>
</if>
</sequential>
</macrodef>
They may be a couple of ways to solve this, but none are as straightforward as using the ant-contrib element. I'm not sure if this will get you what you need for your application, but you could try the following:
Using conditional targets. If you can replace your macrodef with a target to call, this may work for you. Note that this will set the property globally, so it might not work for your application.
<target name="default">
<condition property="platformIsProd">
<equals arg1="${platform}" arg2="prod" />
</condition>
<antcall target="do-buildstamp" />
</target>
<target name="do-buildstamp" if="platformIsProd">
<echo>doing prod stuff...</echo>
</target>
Handle the 'else' case. If you need to handle an alternate case, you'll need to provide a few targets...
<target name="default">
<property name="platform" value="prod" />
<antcall target="do-buildstamp" />
</target>
<target name="do-buildstamp">
<condition property="platformIsProd">
<equals arg1="${platform}" arg2="prod" />
</condition>
<antcall target="do-buildstamp-prod" />
<antcall target="do-buildstamp-other" />
</target>
<target name="do-buildstamp-prod" if="platformIsProd">
<echo>doing internal prod stuff...</echo>
</target>
<target name="do-buildstamp-other" unless="platformIsProd">
<echo>doing internal non-prod stuff...</echo>
</target>
Using an external build file. If you need to make multiple calls with different values for your property, you could isolate this in another build file within the same project. This creates a bit of a performance hit, but you would not need the additional library.
in build.xml:
<target name="default">
<ant antfile="buildstamp.xml" target="do-buildstamp" />
<ant antfile="buildstamp.xml" target="do-buildstamp">
<property name="platform" value="prod" />
</ant>
<ant antfile="buildstamp.xml" target="do-buildstamp">
<property name="platform" value="nonprod" />
</ant>
</target>
in buildstamp.xml:
<condition property="platformIsProd">
<equals arg1="${platform}" arg2="prod" />
</condition>
<target name="do-buildstamp">
<antcall target="do-buildstamp-prod" />
<antcall target="do-buildstamp-other" />
</target>
<target name="do-buildstamp-prod" if="platformIsProd">
<echo>doing external prod stuff...</echo>
</target>
<target name="do-buildstamp-other" unless="platformIsProd">
<echo>doing external non-prod stuff...</echo>
</target>
Add ant-contrib to your project. Of course, if you can add a file to your project, the easiest thing would be to just add the ant-contrib.jar file. You could put it under a "tools" folder and pull it in using a taskdef:
<taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${basedir}/tools/ant-contrib.jar" />
It looks like when you are building your project specifically for your Production environment - you are stripping out code you don't want to run in Production. Thus you are creating a different binary than what will run in your Dev or Testing environment.
How about using an environment variable or property file at run-time instead of build-time which determines whether or not logging happens? This way when you're having trouble in Production and you want to use the same exact binary (instead of determining the revision, checking out the code, rebuilding with a different environment flag) you just re-deploy it to your Dev or Test environment and turn on debugging in a properties file or environment variable?

Resources