> Thanks for the information. I would be interested in seeing how you implemented what you have if > you can give me a high level view without ruffling any feathers. Thanks.
Okay, here's an example java properties file that defines a set of deliverables built from several components, each checked out from CVS with a different tag. (This is a bit more than what you want, as it's geared towards checking out into a sandbox directory where the build happens, then collecting all the deliverables into a holding directory so that the installer project can find them.) #the deliverable files deliverables.files=dist/lib1.jar,dist/lib2.jar #you can also specify entire directories #deliverables.dirs #the list of components -- details of each are specified below components=platform,ui,content #the buildfile build.file=build.xml build.file.tag=BUILDFILE_03 build.dir=src build.target=jars #the tag for the 'platform' component platform.tag=PLATFORM_17 #the directories under CVS covered by that tag platform.dirs=src/platform,src/foo #the tag for the 'ui' component ui.tag=UI_08 #the directories under CVS covered by that tag ui.dirs=src/ui,src/util #leave the tag undefined to check out #to the edge of the main trunk #content.tag= content.dirs=content/site1,content/site1,content/site3 The following ant project will process this sort of properties file. You can just use the 'checkout' target to pull stuff from CVS, or if you also want to build according to the build.* properties, you can use the 'call-build' or 'copy-delivs' targets. <project name="cvsbuild" default="copy-delivs" basedir="."> <!-- This ant file defines targets used to check out code and a build file from CVS, call a target in that build file, and copy the deliverables created by that build file into a deliverables directory. The build file can also be left out if all of the deliverables are retrieved directly from CVS. This is useful for e.g. straight text files, or third-party binaries. When using this ant file, the property 'propfile' must be defined on the command line (e.g. ant -Dpropfile=java-tags.txt). This property is the name of an ant properties file. The propfile is basically a list of CVS modules, and tags corresponding to one or more of the modules. For a given tagged module, two properties should be defined: <module>.tag: the cvs tag for this module <module>.dirs: a comma-separated list of directories which make up this module The <module>.tag property may be left undefined, in which case the module will be checked out to the edge of the tree. The propfile must define the following special property: components: a comma-separated list of the <module> tags to be used in the build. At least one of these two properties must be defined as well: deliverables.files: a comma-separated list of paths to individual deliverable files (typically .zip files) deliverables.dirs: a comma-separated list of paths to deliverable directories. This is used to pull an entire directory out of CVS into the deliverables directory. If the deliverables must be constructed via a buildfile, the propfile should define the following four special properties: build.file: the name of the ant build file for this project. The path to this file is constructed as ${build.dir}/${build.file}. build.dir: the base directory to use when calling ant using ${build.file}. build.target: the name of the target (defined in ${build.file}) to build build.file.tag: the cvs tag to use when checking out the build file. The build.file.tag property may be left undefined, in which case the build file will be checked out to the edge of the tree. This ant file also defines a target to tag the modules listed in a propfile. Typically this is used once a build (the copy-delivs target) has succeeded to mark the source code as stable & as part of whatever release is being built. --> <taskdef name="foreach" classname="org.apache.tools.ant.taskdefs.optional.ForEach"/> <taskdef name="propertycopy" classname="org.apache.tools.ant.taskdefs.optional.PropertyCopy"/> <!-- the temporary directory where we'll check out the modules --> <property name="cvstree" value="${basedir}/cvstree"/> <!-- the directory where the deliverables will be stored --> <property name="deliv.dir" value="${basedir}/deliv"/> <!-- file where cvs actions are logged --> <property name="cvslogfile" value="cvslog"/> <!-- file where the results of building target ${build.target} in ${build.file} are logged --> <property name="buildlogfile" value="buildlog"/> <!-- load in the properties from the propfile --> <property file="${propfile}"/> <target name="check-for-tagname" unless="tagname" description="checks that the 'tagname' property has been defined"> <echo message="You must define the 'tagname' property on the command line. For example:"/> <echo message=" ant -Dtagname=STABLE_2002"/> <fail message="The 'tagname' property has not been defined."/> </target> <target name="check-for-propfile" description="checks that the file 'propfile' exists"> <available file="${propfile}" type="file" property="propfile-ok"/> <antcall target="fail-no-propfile"/> </target> <target name="fail-no-propfile" unless="propfile-ok" description="presents an error message to the user if 'propfile' is invalid"> <echo message="You must define the 'propfile' property on the command line. For example:"/> <echo message=" ant -Dpropfile=tags.txt"/> <echo message="will build according to the tags in the file tags.txt"/> <fail message="The 'propfile' property has not been defined, or points to a non-existent file."/> </target> <target name="init" depends="check-for-propfile"> <mkdir dir="${cvstree}"/> </target> <!-- targets to check out the souce code from cvs --> <target name="checkout" depends="init" description="checks out tagged directories listed in the propfile"> <record name="${cvslogfile}" append="true" action="start"/> <foreach list="${components}" target="checkout-dirs" param="component"/> <antcall target="checkout-build" inheritAll="true"/> <record name="${cvslogfile}" action="stop"/> </target> <!-- WARNING: this depends on init, but it should only be called by the checkout wrapper task, so we're going to leave the explicit dependency off for efficiency --> <target name="checkout-build" if="build.file"> <propertycopy name="comptag" from="build.file.tag" silent="true"/> <antcall target="checkout-dir" inheritAll="true"> <param name="ckout.pkg" value="${build.dir}/${build.file}"/> </antcall> </target> <!-- WARNING: this depends on init, but it should only be called by the checkout wrapper task, so we're going to leave the explicit dependency off for efficiency --> <target name="checkout-dirs"> <propertycopy name="complist" from="${component}.dirs"/> <propertycopy name="comptag" from="${component}.tag" silent="true"/> <foreach list="${complist}" target="checkout-dir" param="ckout.pkg"/> </target> <!-- WARNING: this depends on init, but it should only be called by the checkout-dirs wrapper task, so we're going to leave the explicit dependency off for efficiency --> <target name="checkout-dir-tag" if="comptag"> <echo message="checking out ${ckout.pkg} with tag ${comptag}"/> <cvs dest="${cvstree}" quiet="true" failonerror="true" command="checkout -P -r ${comptag} "${ckout.pkg}""/> </target> <!-- WARNING: this depends on init, but it should only be called by the checkout-dirs wrapper task, so we're going to leave the explicit dependency off for efficiency --> <target name="checkout-dir-notag" unless="comptag"> <echo message="NOTE: no tag defined for ${ckout.pkg}; checking out to edge of tree"/> <cvs dest="${cvstree}" quiet="true" failonerror="true" command="checkout -PA "${ckout.pkg}""/> </target> <target name="checkout-dir" depends="checkout-dir-tag,checkout-dir-notag"/> <!-- targets to tag the souce code in cvs --> <target name="tag" depends="init,check-for-tagname" description="tags directories listed in the propfile"> <record name="${cvslogfile}" append="true" action="start"/> <foreach list="${components}" target="tag-dirs" param="component"/> <antcall target="tag-build" inheritAll="true"/> <record name="${cvslogfile}" action="stop"/> </target> <!-- WARNING: this depends on init, but it should only be called by the tag wrapper task, so we're going to leave the explicit dependency off for efficiency --> <target name="tag-build" if="build.file"> <antcall target="tag-dir" inheritAll="true"> <param name="ckout.pkg" value="${build.dir}/${build.file}"/> </antcall> </target> <!-- WARNING: this depends on init, but it should only be called by the tag wrapper task, so we're going to leave the explicit dependency off for efficiency --> <target name="tag-dirs"> <propertycopy name="complist" from="${component}.dirs"/> <foreach list="${complist}" target="tag-dir" param="ckout.pkg"/> </target> <!-- WARNING: this depends on init, but it should only be called by the tag-dirs wrapper task, so we're going to leave the explicit dependency off for efficiency --> <target name="tag-dir"> <echo message="tagging ${ckout.pkg} with tag ${tagname}"/> <cvs dest="${cvstree}" quiet="true" failonerror="true" command="tag -F ${tagname} "${ckout.pkg}""/> </target> <!-- a target to call out to the build.xml we've gotten out of CVS --> <target name="call-build-bf" depends="init,checkout" if="build.file" description="executes the selected target on the checked-out build file"> <record name="${buildlogfile}" append="true" action="start"/> <echo message="calling target ${build.target} in buildfile ${build.dir}/${build.file}"/> <ant antfile="${build.file}" dir="${cvstree}/${build.dir}" target="${build.target}" inheritAll="false"/> <record name="${buildlogfile}" action="stop"/> </target> <target name="call-build-nobf" unless="build.file" description="makes a note in the log file that no build file is being called"> <echo message="NOTE: No build file is defined, so no call to it is being made."/> <echo message=" Deliverables are assumed to have been checked out directly from CVS."/> </target> <target name="call-build" depends="call-build-bf,call-build-nobf" description="meta-target to call out to a build file, if one is defined"/> <!-- targets to copy the deliverables to where they belong --> <target name="init-deliv" description="creates the 'deliv.dir' directory"> <mkdir dir="${deliv.dir}"/> </target> <target name="copy-delivs" depends="call-build,init-deliv,copy-deliv-files,copy-deliv-dirs" description="meta-target to copy the deliverables"/> <target name="copy-deliv-files" if="deliverables.files" description="copies each of the deliverable files"> <foreach list="${deliverables.files}" target="copy-deliv-file" param="deliv"/> </target> <target name="copy-deliv-dirs" if="deliverables.dirs" description="copies each of the deliverable directories"> <foreach list="${deliverables.dirs}" target="copy-deliv-dir" param="deliv"/> </target> <target name="copy-deliv-file"> <echo message="copying deliverable file(s): ${deliv}"/> <copy todir="${deliv.dir}" flatten="true"> <fileset dir="${cvstree}"> <include name="${deliv}"/> </fileset> </copy> </target> <target name="copy-deliv-dir"> <echo message="copying deliverable dir(s): ${deliv}"/> <copy todir="${deliv.dir}/${deliv}" flatten="false"> <fileset dir="${cvstree}/${deliv}"/> </copy> </target> <!-- housekeeping targets --> <target name="clean-comps" depends="init" description="removes all checked-out components from the temporary cvs tree, including the build file"> <foreach list="${components}" target="clean-dirs" param="component"/> <antcall target="clean-buildfile" inheritAll="true"/> </target> <target name="clean-buildfile" description="removes the checked-out build file"> <echo message="deleting file ${build.dir}/${build.file}"/> <delete file="${cvstree}/${build.dir}/${build.file}"/> </target> <target name="clean-dirs"> <propertycopy name="complist" from="${component}.dirs"/> <foreach list="${complist}" target="clean-dir-or-file" param="clean.pkg"/> </target> <target name="decide-dir-or-file"> <available file="${cvstree}/${clean.pkg}" type="dir" property="tgt-is-dir"/> </target> <target name="clean-dir" if="tgt-is-dir"> <echo message="deleting directory ${clean.pkg}"/> <delete dir="${cvstree}/${clean.pkg}"/> </target> <target name="clean-file" unless="tgt-is-dir"> <echo message="deleting file ${clean.pkg}"/> <delete file="${cvstree}/${clean.pkg}"/> </target> <target name="clean-dir-or-file" depends="decide-dir-or-file,clean-dir,clean-file"/> <target name="clean-delivs-dirs" if="deliverables.dirs"> <foreach list="${deliverables.dirs}" target="clean-delivs-dir" param="dir"/> </target> <target name="clean-delivs-dir"> <echo message="deleting dir ${dir}"/> <delete dir="${cvstree}/${dir}"/> </target> <target name="clean-delivs-files" if="deliverables.files"> <foreach list="${deliverables.files}" target="clean-delivs-file" param="file"/> </target> <target name="clean-delivs-file"> <echo message="deleting file ${file}"/> <delete dir="${cvstree}/${file}"/> </target> <target name="clean-delivs" depends="init,clean-delivs-dirs,clean-delivs-files" description="removes all deliverables from the temporary cvs tree"/> <target name="clean" description="removes the entire temp cvs tree"> <delete dir="${cvstree}"/> </target> </project> Matt McHenry Software Developer Carnegie Learning (412)690-2442 x150