Good discussion. This looks like a good way of managing ant. I heartily agree that each project should have ant in its source tree so that the right version of ant can be used to build the project.
Other options for launching ant (other than using make) are to use ant itself, as long as you agree on some lowest common denomitor ant (say bare bones ant 1.3). I use a python script to configure and launch ant. The python script generates the PATH and the CLASSPATH which it uses when it execs java to start ant. This avoids the dance of having make set environment variables that are picked up by the ant.bat or ant.sh script. -----Original Message----- From: stephan beal [mailto:[EMAIL PROTECTED]] Sent: Thursday, March 21, 2002 7:22 AM To: [EMAIL PROTECTED]; [EMAIL PROTECTED] Cc: [EMAIL PROTECTED]; [EMAIL PROTECTED] Subject: article: combining make and ant Hello, fellow Anters, This post describes one method for keeping Ant itself in your build tree and using 'make' as a front-end for launching ant. This article will only be of use to Unix users (and maybe Cygwin users, but i have no experience with Cygwin so i cannot say for sure). Why mix Ant and Make? There are several advantages to doing so, which i hope will become clear as the article progresses. i'm not condoning mixing the two out of any dislike for Ant - i've come to like Ant quite a lot. i'm proposing mixing the two because it opens up new possibilities without taking any away. The Problem: My company's source tree gets built on a variety of machines, mostly running Solaris or Linux. Installing Ant on each of these machines, and making sure each Ant installation always has the latest copies of our custom tasks, is a huge PITA (pain in the ass). One complication is the fact that any custom Ant extensions (new tasks) must be built and in the CLASSPATH before ant is actually run on our source tree. This means we have to maintain copies of our tasks' jar file(s) on each buildmachine, and that type of maintenance is No Fun. (And anyone who knows me knows that i am incredibly lazy, and therefor always look for the approach which requires the least maintenance .) The Solution: install Ant in your source tree, and use make as a front-end to take care of the drudgery, including environment configuration and building of custom Ant tasks. Our source tree contains a binary installation of ant, and that Ant installation is used to build our source tree. In addition, the Ant tree contains the sources for our custom tasks, and make ensures that these tasks are built before doing the "main build." The source tree looks something like this: ./src/ = our java files ./classes/ = compiled output ./ant/bin, ant/lib = Ant installation ./ant/src = our custom classes ./build.therealbuild.xml = our "real" build.xml ./build.xml = a bogus build.xml, explained below. ./ant/build.antextensions.xml = the build.xml for our custom classes (again, that tree has a bogus build.xml). build.xml: This file is a bogus build file which fails with an error message, explaining the "proper" way to run the build. This is to keep people from running the build with their own ant version. This ensures: a) build compatibility across machines (no surprises caused by different Ant versions). b) the PATH & CLASSPATH are set up exactly how we need it done, without having to rely on the developer to set up h{is,er} environment correctly. This is accomplished by forcing the user to run 'make', which does the configuration for the user. Here's our top-level Makefile: ================================================ SHELL = /bin/bash # using Solaris' bourne shell will almost certainly # not work. Don't use Solaris' make, either: use GNU make. .PHONY: ant clean default: all ant_home = ${PWD}/ant ant = ${ant_home}/bin/ant ant_libdir = ${ant_home}/lib # ${ant_extensions} must be in the classpath BEFORE ant is started # or they won't get loaded in time to be used in <taskdef>s. ant_extensions = ${ant_libdir}/einsurance_ant.jar ant_classpath = ${ant_extensions} ant_args := ${ant_args} -buildfile build.therealbuild.xml ${ant_extensions}: Makefile ${ant_home}/build.antextensions.xml cd ${ant_home} && ${MAKE} ant: ant-main ant-%: ${ant_extensions} @target=$@; target=$${target#*-}; \ ant_cmd="${ant} $$target ${ant_args}"; \ echo "ant command=[$$ant_cmd]"; \ ANT_HOME=${ant_home}; \ PATH=${ant_home}/bin:$$PATH; \ CLASSPATH=${ant_classpath}:$$CLASSPATH; \ export ANT_HOME PATH CLASSPATH; \ $$ant_cmd # note: the 'export' line is not strictly necessary, but i # like it, and it actually is necessary for part of our process. clean: ant-clean all: ant-main ================================================ Now the command: make ant-main will run 'ant main -buildfile build.therealbuild.xml' You can send more arguments to ant with this: ant_args="-logfile foo -emacs" make Those will be prepended to the hard-coded list of arguments. Make is, let's face it, far better at dependency checking than Ant, and we take advantage of make's dependency capabilities in allowing it to build our custom Ant tasks before it runs the main build. The tasks must be built before Ant is run on our source tree, and make takes care of that for us, running ant once to build the tasks then once more to compile our source tree (which uses those just-compiled tasks). If we want to simplify the process of running ant, we can add this to your Makefile: =================== all: @echo "Select a build to run:" @select build in main clean cvs compile; \ do ${MAKE}; ant-$$build;break; done =================== Now running 'make' will present you with a list of ant targets to run, and you just need to tap a number, then Enter. Selecting 'cvs' from the list will run 'make ant-cvs'. Note that the Ant target names are determined dynamically - you never need to add Ant target names to the Makefile unless you want to add shortcuts, like: clean: ant-clean rebuild: ant-clean ant-main Whenever we run make, it will make sure that our custom extensions (${ant_extensions}) are rebuilt, and will then run ant with those extensions in the classpath. We put our custom tasks in a separate source tree, under ./ant/src, so that we can build them separately. We cannot build our source tree if the extensions are not built, because our build.therealbuild.xml contains <taskdefs> which are not valid unless our extensions are built and in our CLASSPATH. Thus we cannot feasibly store the tasks' source code in our main source tree. Rather than deal with all of this mess manually (on each build machine), we let make do it for us. The best part is now that we can check out our source tree to any machine and immediately build it without having to install ant: cvs co ...... cd einsurance make ant <builds extensions> <builds source tree> The Makefile for our extensions (./ant/src/Makefile) looks like this: ======================================== default: all libdir = ${PWD}/lib classdir = ${PWD}/classes ant_home = ${PWD} CLASSPATH := ${classdir}:${CLASSPATH} ant_args := ${ant_args} -buildfile build.antextensions.xml build_extensions: @CLASSPATH=${CLASSPATH}; \ ANT_HOME=${ant_home}; \ ${ant_home}/bin/ant ${ant_args} clean: @CLASSPATH=${CLASSPATH}; \ ANT_HOME=${ant_home}; \ ${ant_home}/bin/ant clean ${ant_args} all: build_extensions ======================================== Note that in this case we let Ant take care of the dependencies for the jar file, since that type of dependency tracking is much simpler to implement in Ant. The ./ant tree knows nothing about our main source tree, by the way, making it a completely standalone environment for developing our custom tasks. Having just converted our tree from Makefile-based to Ant-based, i can say that Ant is certainly more suited for the task - we have over 1700 classes, and make takes over an hour to build the whole thing (because it runs javac once per source file, causing a huge overhead). Ant normally does the build in under 3 minutes. However, make has some features which Ant could not hope to accomplish (like INCREDIBLE dependency checking, and dynamic target names (like ant-%)), and some things are just plain simpler in make (like running shell code, which we simply cannot avoid in some cases). Another, perhaps not immediately obvious, advantage in using make is the ability to run commands which can accept user input. Ant 1.x cannot currently do this. We use exactly this to send our compiled classes to our test servers: in Makefile: ====================================== sync: @echo "Type in the number of the machine you want to send this stuff to:" @select svr in int mirror; do ${MAKE} sync-$$svr; break; done sync-int: echo "syncing to INTEGRATION..."; \ cd ${top_srcdir}/classes; \ rsync -C -z -v -r -W -e ssh . build@integration:/u/www/INT/servlets; \ cd ${top_srcdir}/webapps; \ rsync -C -z -v -r -W -e ssh . build@integration:/u/www/INT/webapps sync-mirror: echo "syncing to MIRROR..."; \ cd ${top_srcdir}/classes; \ rsync -C -z -v -r -W -e ssh . build@mirror:/u/www/WWW/servlets; \ cd ${top_srcdir}/webapps; \ rsync -C -z -v -r -W -e ssh . build@mirror:/u/www/WWW/webapps # if i wasn't so lazy that would be one sync-% # target with variables in it. ====================================== ("Integration" and "mirror" are internally-used terminology referring to our test servers. Your organization probably has something similar.) Now running 'make sync' will send our classes to the remote machine we select. We could also use make to generate properties files for our build, before ant is run. There are lots of possibilities. Granted, using make is not as cross-platform as using Only Ant, but if you're running Unix machines, then a mix of make and Ant is a powerful combination. While the purists (and Windows users ;) out there can justifiably argue, "we must implement it all in build.xml, using Pure Ant and Java", i disagree. We may as well use the tools available to us, especially when they make our jobs easier. ----- stephan Generic Universal Computer Guy [EMAIL PROTECTED] - http://www.einsurance.de Office: +49 (89) �552 92 862 Handy: �+49 (179) 211 97 67 "I ain't gen'rally given to physicality of that nature but it saves a lot of arguing." -- Nanny Ogg -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]> -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
