How to invoke a macrodef from within another file - ant

I wrote a small macrodef in separate file:
macrodefs.xml
<macrodef name="do-cool-stuff">
<attribute name="message"/>
<sequential>
<echo message="#{message}" />
</sequential>
</macrodef>
I got a second file, my main build file:
build.xml
<target name="build">
<!-- do this and that -->
<!-- cheking out macrodefs.xml via CVS -->
<ant antfile="macrodefs.xml" target="do-cool-stuff" >
<property name="message" value="Hello, World!" />
</ant>
</target>
As you might guess this dosen't work. The error message is something like:
Target 'do-cool-stuff' does not exist in this project.
The only possible solution I found is to provide a extra target in the macrodefs.xml to forward the ant calls.
Is there a possibility to invoke the macrodef from within another file?
Thanks in advance.

You can import the file and use the macro like this:
<import file="macrodefs.xml" />
<do-cool-stuff message="Hello, World!" />
Note that in the macro definition you should use #{curlybrackets} when referencing macro attributes:
<sequential>
<echo message="#{message}" />
</sequential>
There are some examples at the end of the Ant macrodef task docs.
More
What you're trying to do isn't well supported by Ant. The ant and antcall tasks don't allow the 'callee' to affect the caller directly. You can write files in the called task, then load those in the caller. But as you have observed, the pre-process tasks import and include cannot be called from within a target. The ant/antcall tasks only allow you to run targets in subsidiary builds, not macros.
One workaround method (this might be similar to the one you mention, but allows you to put all the real work in the top-level build) would be to have an inner buildfile that includes the top-level import of the macrodefs.xml.
Something like the following. The macrodefs.xml file is as before. (But note that the imported files - including the macro definitions - need to be complete Ant project files, so they must include a project element.)
build.xml:
<target name="build">
<!-- cvs actions -->
<ant antfile="inner-build.xml" target="target-runner">
<property name="target" value="top-target" />
</ant>
</target>
<!-- this target will fail unless invoked from the inner build -->
<target name="top-target">
<do-cool-stuff message="Hello, World!" />
</target>
inner-build.xml:
<project>
<import file="macrodefs.xml" />
<target name="target-runner">
<ant antfile="build.xml" target="${target}" />
</target>
</project>
Effectively you would be doing
build.xml --> inner-build.xml --> build.xml (again)
(cvs) (import macros) (use macros)
The inner buildfile could potentially be generated on-the-fly by the main build - say if you wanted to import multiple macro definition files - but that's getting perhaps too unwieldy.

Related

Ant conditional if within a macrodef

Within ant, I have a macrodef.
Assuming I have to use this macrodef, and there is a item inside said macrodef that I want to run if the property special.property exists and is true, what do I do?
I currently have
<macrodef name="someName">
<sequential>
<someMacroDefThatSetsTheProerty />
<some:thingHereThatDependsOn if="special.property" />
<sequential>
</macrodef>
Which doesn't work - the some:thingHereThatDependsOn doesnt have an "if" attribute, and I cannot add one to it.
antcontrib is not available.
With a target I can give the target an "if", what can I do with a macrodef?
In Ant 1.9.1 and higher, there is now a new implementation of if and unless attributes. This might be what you're thinking of.
First, you need to put them into your namespace. Add them to your <project> header:
<project name="myproject" basedir="." default="package"
xmlns:if="ant:if"
xmlns:unless="ant:unless">
Now, you can add them to almost any Ant task or sub entity:
<!-- Copy over files from special directory, but only if it exists -->
<available property="special.dir.available"
file="${special.dir} type="dir"/>
<copy todir="${target.dir}>
<fileset dir="${special.dir}" if:true="special.dir.available"/>
<fileset dir="${other.dir}"/>
</copy>
<!-- FTP files over to host, but only if it's on line-->
<condition property="ftp.available">
<isreachable host="${ftp.host}"/>
</condition>
<ftp server="${ftp.host}"
userid="${userid}"
passowrd="${password}"
if:true="ftp.available">
<fileset dir=".../>
</ftp>
This is only possible if the ANT "thingHereThatDependsOn" task supports an "if" attribute.
As stated above, conditional execution in ANT, normally, only applies to targets.
<target name="doSomething" if="allowed.to.do.something">
..
..
</target>
<target name="doSomethingElse" unless="allowed.to.do.something">
..
..
</target>
<target name="go" depends="doSomething,doSomethingElse"/>

Workaround for nested subant property override behaviour (since 1.8.0)

I am using apache ant in the following way:
I have projects P1, P2, P3. Some of those projects have modules, let say P2 has M1, M2, etc.
All projects have their own ant build script, and all of them have to implement set of predefined targets (build, dist, etc), the scripts expect some properties to be defined when called. let say (build.dir.base)
The modules follow similar structure, so every modules, has its own build file that implements the predefined set of targets, and expects some properties to be set. (let say build.dir.base - same as for the projects)
I also have a global ant script that builds all the projects (or subset)
In code that would look like:
build-all.xml:
<project name="x">
<property name="build.dir.base" location="system.build.base" />
<target name="build">
<echo message="build.dir.base as defined for build-all.xml=${build.dir.base}" />
<subant antfile="build.xml" target="build" inheritAll="false" inheritRefs="false">
<dirset dir="${system.base}" includes="${project-list}" />
<property name="build.dir.base" location="${build.dir.base}" />
</subant>
</target>
</project>
build.xml (one for each project with modules, no subant if project does not have modules):
<project name="y">
<property name="build.dir" location="${basedir}/build" />
<target name="build">
<echo message="build.dir.base as defined for project=${build.dir.base}" />
<subant antfile="build.xml" target="build" inheritAll="false" inheritRefs="false">
<dirset dir="project.base" includes="${module-list}" />
<property name="build.dir.base" location="${build.dir.base}/${name}" />
</subant>
</target>
</project>
And for the projects that have modules:
build.xml (for a module):
<project name="z">
<property name="build.dir.base" location="build.dir.base" />
<target name="build">
<echo message="build.dir.base as defined for module=${build.dir.base}" />
</target>
</project>
This structure allows projects to be built independently, also modules can be build independently or the whole system can be built using the build-all.xml.
Also, the final product has the folowing structure:
${system.build.base}/P1,
${system.build.base}/P2/M1
${system.build.base}/P2/M2
etc
However, since ant >= 1.8.0 this is not possible any more. The reason is that
<property name="build.dir.base" location="${basedir}/build" /> in build-all.xml
takes precedence over <property name="build.dir.base" location="${build.dir.base}/${name}" /> in build.xml (build for projects). So, the destination of the project "submodules" is in ${system.build.base}/M1 instead of ${system.build.base}/P2/M1
This is explained Properties defined on the command line cannot be overridden by nested elements. Since Ant 1.8.0. the same is true for nested structures of tasks: if a build file A invokes B via an task setting a property with a nested element and B contains an tasks invoking C, C will see the value set in A, even if B used a nested element as well.
There is no way to override a property for subant if some of the parents also defined that property. This is severe, since the build should be aware for properties that the parent used for some unrelated reason.
Is there workaround for this incompatible change in behavior? Since, my build system heavily relies on the fact that <subant inheritAll="false" inheritRefs="false"> will do the exec without polluting the sub-builds.
https://issues.apache.org/bugzilla/show_bug.cgi?id=49891 also seems related to this question.
After trying out many different things (including the technique of filtering out the unwanted property then resetting it), I have concluded that it is impossible to override command-line properties in a subant task.
Not a complete answer, but I've found you can prevent specific properties from being inherited by the subtask by doing something like:
<resources id='aoeu'>
<intersect>
<propertyset>
<propertyref builtin="commandline"/>
</propertyset>
<propertyset negate="true">
<propertyref name="property.not.to.be.inherited"/>
</propertyset>
</intersect>
</resources>
<groovy>
ant.propertyset(id:"snth") {
project.references["aoeu"].each {
propertyref(name:"${it.name}")
}
}
</groovy>
<subant buildpathref="buildpath">
<propertyset>
<propertyset refid="snth"/>
</propertyset>
<target name="zvwm"/>
</subant>
It is not impossible!! you can overwrite the properties using a scriptdef
self.getProject().setInheritedProperty(key,value). check out API
You can use the antcontrib var task.
http://ant-contrib.sourceforge.net/tasks/tasks/variable_task.html

overwriting ANT properties file

How can i overwrite some existing property with a newly created properties file?
Here is the required structure:
initially load Master.properties
generate new.properties
load new.properties and master.properties
run master.xml (ANT script)
The idea is that Master.properties generates some product version which should be replaced by new.properties. However, other properties in Master.properties should be kept the same.
Reading this does not help as i do not know how can i load the new.properties file
EDIT Here is ANT Script:
<project name="nightly_build" default="main" basedir="C:\Work\NightlyBuild">
<target name="init1">
<sequential>
<property file="C:/Work/NightlyBuild/master.properties"/>
<exec executable="C:/Work/Searchlatestversion.exe">
<arg line='"/SASE Lab Tools" "${Product_Tip}/RELEASE_"'/>
</exec>
<sleep seconds="10"/>
<property file="C:/Work/new.properties"/>
</sequential>
</target>
<target name="init" depends="init1">
<sequential>
<echo message="The product version is ${Product_Version}"/>
<exec executable="C:/Work/checksnapshot.exe">
<arg line='-NightlyBuild ${Product_Version}-AppsMerge' />
</exec>
<sleep seconds="10"/>
<property file="C:/Work/checksnapshot.properties"/>
<tstamp>
<format property="suffix" pattern="yyyy-MM-dd.HHmm"/>
</tstamp>
</sequential>
</target>
<target name="main" depends="init">
<echo message="loading properties files.." />
<echo message="Backing up folder" />
<move file="C:\NightlyBuild\NightlyBuild" tofile="C:\NightlyBuild\NightlyBuild.${suffix}" failonerror="false" />
<exec executable="C:/Work/sortfolder.exe">
<arg line="6" />
</exec>
<exec executable="C:/Work/NightlyBuild/antc.bat">
</exec>
</target>
</project>
in the above script, <exec executable="C:/Work/NightlyBuild/antc.bat"> will run Master.xml ANT script. This Master.xml will load up Master.properties:
<project name="Master ANT Build" default="main" >
<taskdef name="CFileEdit" classname="com.ANT_Tasks.CFileEdit"/>
<!-- ========================================================== -->
<!-- init: sets global properties -->
<!-- ========================================================== -->
<target name="init">
<property environment="env"/>
<!-- ========================================================== -->
<!-- Set the timestamp format -->
<!-- ========================================================== -->
<property file="Master.properties"/>
...
</project>
You should be able to resolve this by looking at the order in which you load (or otherwise specify) your property values. You probably don't need to override property values at all, which something not supported by core Ant.
Maybe you can split your Master.properties into two files - one loaded before you generate new.properties and one loaded after?
Maybe you don't need to generate new.properties at all.
Could you give some more detail on what you need to do?
Since you eventually fork a new Ant process (exec antc.bat), does that not start a fresh environment anyway? If it just loads Master.properties, those are the only properties it will have.
Not sure what your antc.bat does, but it's pretty unusual to exec Ant from Ant in this way. There are two standard tasks which might be useful - Ant and AntCall.
OK running on from your later comments...
Let's say that instead of doing this:
<exec executable="antc.bat">
you instead did something like this:
<ant file="Master.xml" inheritall="false">
<property name="Product_Version" value="${Product_Version}"/>
</ant>
I think that is getting towards what you want. You selectively pass specific values that you have obtained by loading new.properties. See the documentation for the Ant task.
If you still have the problem that you already defined Product_Version before loading new.properties, then I would say get the script you have that produces new.properties to output the version with a different name, e.g. New_Product_Version. Then invoke your master build something like this:
<ant file="Master.xml" inheritall="false">
<property name="Product_Version" value="${New_Product_Version}"/>
</ant>
May be this is a old question. Hopefully OP is reading this.
You can just use the ant task "propertyfile". reference
it can read properties from the file and write back updated values to them.

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.

How do I pass an argument to an Ant task?

I'm not very good with Ant, but we're using it as a build tool. Right now, we can run "ant test" and it'll run through all the unit tests.
However, I'd love to be able to do something like ant test some_module and have it accept some_module as a parameter, and only test that.
I haven't been able to find how to pass command line args to Ant - any ideas?
One solution might be as follows. (I have a project that does this.)
Have a separate target similar to test with a fileset that restricts the test to one class only. Then pass the name of that class using -D at the ant command line:
ant -Dtest.module=MyClassUnderTest single_test
In the build.xml (highly reduced):
<target name="single_test" depends="compile" description="Run one unit test">
<junit>
<batchtest>
<fileset dir="${test.dir}" includes="**/${test.module}.class" />
</batchtest>
</junit>
</target>
You can also define a property with an optional default value that can be replaced via command line, e.g.
<target name="test">
<property name="moduleName" value="default-module" />
<echo message="Testing Module: ${moduleName}"/>
....
</target>
and run it as:
ant test -DmoduleName=ModuleX
What about using some conditional in your test target and the specifying -Dcondition=true?
<target name="test" depends="_test, _test_if_true>
...
</target>
<target name="_test_if_true" if="condition">
...
</target>
<target name="_test" unless="condition">
...
</target>
Adapted a bit from the ant faq.
You can define a property on commandline when invoking ant:
ant -Dtest.module=mymodulename
Then you can use it as any other ant property:
...
<fileset dir="${test.dir}" includes="**/${test.module}.class" />
...
Have a look at Ant's manual.
I tried the solutions posted here for the very same original question. Yes just use ant -D<arg_name>. THe -D is a "keyword" I guess. I'm no ant expert and have not read the manuals in detail. Then inside the ant XML files can be accessed like: ${arg_name}
For instance you can have an argument name like: arg.myarg, so in XML ${arg.myarg}.
Ant really doesn't have parameters_ for the build file. I can think of a few ways to do this:
Use a special target to specify the tests. You can use the <for/> task from AntContrib to allow you to specify multiple tests. You'll need to download the Ant-Contrib jar file. I recommend placing it inside your project under the `${basedir}/antlib/antcontrib" directory. That way, when others checkout your project, they get the needed Ant-Contrib jar file.
<property name="antlib.dir" value="${basedir}/antlib"/>
<property name="antcontrib.dir" value="${antlib}/antcontrib"/>
<!-- Set up the ant contrib tasks for your use -->
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<fileset dir="${antcontrib.dir}"/>
</classpath>
</taskdef>
<target name="select-test"
description="Select the tests to run"
depends="test-compile"
if="junit-tests">
<for parameter="module"
list="${junit-tests}"
delimiter=" ">
<sequential>
<junit
fork="true"
...>
<batchtest todir="$target/unit-tests">
<fileset dir="${test.destdir}">
<include name="**/#{module}.class"/>
</fileset>
</junit>
</sequential>
</for>
</target>
You cab now run multiple tests like this:
$ ant -D"test-one test-two test-three" select-test
You could try this to access one target at a time. Add these lines to your build.xml file :
<project name="whatever" default="default">
<input message="Please select module:" addproperty="mod" />
<target name="default" depends="${mod}/>
...
</project>
This allows you to enter the module you want to execute and execute that itself instead of running the whole build.xml
You might need to make a few more changes to your build.xml for this to work perfectly.
For the arguments , there is Facility called property. You need to set the property. As in ANT plain arguments is taken as target name.
Lest say you have two modules in your project ModuleX and ModuleY where ModuleX has 2 testcases to run and ModuleY with 10 testcases.
You could do something like this :
ant runTestsOnModule -Dtestmodule="ModuleX"
OR to test all modules by calling
ant tests
<target name="runTestsOnModule">
<antCall target="testcase${testmodule}"/>
</target>'
<! -- run single module -->
<target name="runTestsOnModule">
<antCall target="testcase${testmodule}"/>
</target>
<!--run all tests-->
<target name="tests">
<antcall target="testcaseModuleX">
<antcall target="testCaseModuleY">
</target>
<target name="testcaseModuleX">
..run junit task to call 2 testcase
</target>
<target name="testcaseModuleY">
....run junit task to call 10 testcase
</target>

Resources