> 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} &quot;${ckout.pkg}&quot;"/>
  </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 &quot;${ckout.pkg}&quot;"/>
  </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} &quot;${ckout.pkg}&quot;"/>
  </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

Reply via email to