Register Now
Member Count: 176,922 - November 21, 2008  [Get Time]
Login
Dashboard > TopCoder Competitions > ... > Component Build Process > Java Component Build Process
TopCoder Competitions View a printable version of the current page.  
Java Component Build Process
Added by bwright , last edited by marius_neo on Sep 25, 2008  (view change)
Labels: 
(None)

Please see the New Build Process FAQ for a quick explanation of what the new build process provides beyond the old process.

Please see Build Process Converted Components for a list of components and versions that have been converted.
There is an ongoing effort made internally at TopCoder to switch all the components to this new building format so there are even more components than the ones specified in the page linked above.

Overview

Generally the principles of the build process are:

  • Build scripts that require no component specific customization.
  • All component specific configuration is external to the build script.
  • Standard version rules are used for all components in a fashion that will prevent broken builds.
  • Third party libraries can be easily adjusted for all components in build environment.


1.  Setup the System Environment

This will install the major system components of Java and Ant required for building most Java based products. Please note that there are many versions of Java and Ant available. TopCoder certifies components for the environment specified in the component's Design Specification. If another environment is used then the component should be re-certified. Ant provides a lot of flexibility, these instructions provide an intended process to be followed, other processes may also yield working results.


1.1.  Install JDK

  • Download and install the JDK required by the component to build.
  • Set an environment variable JAVA_HOME to the installation directory
    • JAVA_HOME should be the parent directory of jre and lib directories

1.2.  Install ANT

  • Download and install ANT.
  • Set an environment variable ANT_HOME pointing at the installation directory
    • ANT_HOME should be the parent directory of bin and lib directories


2.  Setup a TopCoder Build Environment

This step will install the required libraries that are standard to all TopCoder components. JUnit is used as a testing harness to run unit, accuracy, failure, and stress tests packaged with each component. Cobertura is used to provide a test coverage report. A standard directory structure is also defined to ensure each component runtime jar is placed in the correct place for other components that may depend on it. Finally a standard properties file is used to provide environment configuration for the build scripts.


2.1.  Install JUnit

Download JUnit\ (you need the Jar file.)


2.2.  Install Cobertura

The following directory structure should be followed exactly:

  • Cobertura Install Directory (currently TopCoder uses version 1.8)
    • cobertura.jar
    • lib (contains libraries Cobertura uses)
      • Jar file for ASM (cureently version 2.2.1)
      • Jar file for Jakarta ORO (currently version 2.0.8)
      • Jar file for log4j (currently version 1.2.9)


2.3.  Designate Source and Library Directories

TopCoder relies on a top level source directory which contains all the Java components as sub directories. The exact name of the Java component directory is flexible. For example a component could be called ConfigurationManager or ConfigMgr-2.1.5 and the build process will work in either case. An example of this directory might be: c:\code\topoder\java-components\

TopCoder components build to a standard location specified by the build configuration, this ensures that the binary jar for a given component version is in a unique location on the build system. Additionally it ensures that components which depend on that binary will find it. And example of this directory might be: c:\code\lib\topcoder-java\


2.4.  Setup topcoder_global.properties file

The build scripts depend on a common build properties file in addition to those which are packaged with the component. This file describes the overall build properties for the specific integration box, including:

  • The TopCoder Library directory
    • The TopCoder integration environment uses ext_libdir as the parent for all third party libraries, however it's not necessary to setup your environment in this fashion, by having each individual jar or dir property point to the exact path.
  • The install location of Cobertura and JUnit for all components
    • Note the build file just references standard properties junit.jar and cobertura.dir to allow for upgrading to newer versions of these tools without editing the build-dependencies.xml file
  • The location of third party libraries (as needed for components on a case by case basis)
  • Standard build parameters
topcoder_global.properties
#execution commands/settings
debug=off
verbose=no

#standard directory definitions
ext_libdir=c:/code/lib/third_party
tcs_libdir=c:/code/lib/topcoder-java

#client directory definitions (optional)
hermes_libdir=c:/code/lib/hermes-java
cronos_libdir=c:/code/lib/cronos-java

#common build script dependencies
junit.jar=${ext_libdir}/junit/3.8.2/junit.jar
cobertura.dir=${ext_libdir}/cobertura/1.8

java_1_3_bootclasspath=c:/Program Files/Java/jre1.3.1_20/lib/rt.jar
java_1_4_bootclasspath=c:/Program Files/Java/j2re1.4.2_13/lib/rt.jar
java_1_5_bootclasspath=c:/Program Files/Java/jre1.5.0_11/lib/rt.jar


3.  Java component file structure

The general files and directories for a Java component are:

  • README - instructions for setting up this component for build
    • Here is an example on how should this file look like:
      Testing instructions in README file
      TO RUN:
      
      1. Install the component structure in ${basedir}
      
      2. Create "topcoder_global.properties" in ${basedir}/.. and add the properties from steps 2,3,4
      
      3. The following standard build script properties must be set
      	ext_libdir - root of third party libraries
      	tcs_libdir - root of topcoder components
      	java_1_5_bootclasspath - installation path for java 1.5 (only needed for compile_targets task)
      
      4. The following standard build script properties can be changed (defaults are noted)
      	debug=off - as expected
      	verbose=no - as expected
      	cobertura.dir=${ext_libdir}/cobertura/1.8
      		installation directory of cobertura script expects {$cobertura.dir}/cobertura.jar
      		and {$cobertura.dir}/lib/containg Jars for asm, jakarta, and log4j to run cobertura task
      
      5. The following properties need to be set for this component
      	None
      
      NB:
      	The folder ${basedir}/test_reflib contains TopCoder components
      	that are required by this component's test code. This component is
      	used to by compile_test and test targets. Components are listed under
      	the following directory structure "component name"/"component version".
      	Components are provided AS IS for development purposes only.
      
      	Included: None.
      
      6. This component requires following third party component.
      	6.1. easymock 2.2
      	6.2. jmock 1.2.0
      	6.3. HSQLDB (http://hsqldb.sourceforge.net/) is used for stress test cases.
      	A table named "stress_test" should be created before running stress test cases, and its scheme can be found in table.sql.
      	Also, any other relational database can be used, in that case, you have to modify the related code yourself.
      
      7. Setup the test environment:
      	7.1. run hsqldb\1.8.0\demo\runServer.bat
      	7.2. run hsqldb\1.8.0\demo\runManager.bat
      	7.3. select HSQL Database Engine Server
      	7.4. create stress_test table.
      
      

      Note that at point 3 from the above documentation must be also specified the greekgod libdir, this meaning the name for library repository of a client (i.e. cronos_libdir - root of the cronos libraries)

  • build.xml and build_dist.xml - ant build script
    TopCoder uses build.xml as an internal build file, containing targets used in publishing components, and build_dist.xml as the external build file (identical, but with the distribution targets removed.) If you have download directly from SVN both files are likely to be in your distribution. If you download from the website, the build_dist.xml file is moved to build.xml. In the case that you have both, it's okay to use either file.
    When downloading those files from SVN for building these files are suposed to be modified ONLY IF they are outdated and in this case you need to replace them with their latest versions. The modifications to be done when building should be made in build.version, build-dependencies.xml and build-override.xml if necessary.
    You can see in the Master build files location that there is a directory for the generic components and a directory for each of the TopCoder's major clients for Java components. When building the component be sure to choose the build files from the right directory by taking into account whether they are generic or custom.
  • build.version - specific component version information, including version number and component name
    • Component versions are specified as major.minor.micro.build - the public version is major.minor.micro and is used by components to find their dependencies. The full version is used for internal management and non-software changes, such as updating documentation.Whenever a modification is done in svn for a specific public version for a component (major.minor.micro) the value for component.version.build is incremented.
      Let's say we're having a specific component A with version to be retrieved from the catalog - 1.0.1 . If there are small changes made on the component which don't need a public version incremented (like 1.1.3) released for the component, the only thing which changes regarding the component version will be the value for component.version.build property which will be always incremented.
      So component A could have version numbers like these ones: 1.0.0.1 ; 1.0.1.1 ; 1.0.1.2 ; 1.0.1.3 ; 1.1.0.1 .
Template build.version file
#property file defining the components information
component.name=Component Name
component.distfilename=component_name
component.package=com.topcoder.component.package
component.packagedir=com/topcoder/component/package
component.version.major=1
component.version.minor=0
component.version.micro=0
component.version.build=1
  • build-dependencies.xml - configuration for the TopCoder and third party dependencies, specific to this component
    build-dependencies.xml file of Data Entitlement 1.3.0 component
    <project name="Dependency Import" default="dist" basedir=".">
        <!-- Properties used in compile_targets ANT target for JDK version x -->
        <property name="component.target" value="1.4"/>
        <property name="component.bootclasspath" value="${java_1_4_bootclasspath}"/>
    
        <!-- TCS dependencies -->
        <property name="logging_wrapper.jar" value="${tcs_libdir}/logging_wrapper/2.0.0/logging_wrapper.jar"/>
        <property name="base_exception.jar" value="${tcs_libdir}/base_exception/2.0.0/base_exception.jar"/>
        <property name="object_factory.jar" value="${tcs_libdir}/object_factory/2.1.0/object_factory.jar"/>
        <property name="object_formatter.jar" value="${tcs_libdir}/object_formatter/1.0.0/object_formatter.jar"/>
        <property name="configuration_api.jar" value="${tcs_libdir}/configuration_api/1.0.0/configuration_api.jar"/>
        <property name="object_factory_configuration_api_plugin.jar"
                  value="${tcs_libdir}/object_factory_configuration_api_plugin/1.0.0/object_factory_configuration_api_plugin.jar"/>
        <property name="typesafe_enum.jar" value="${tcs_libdir}/typesafe_enum/1.1.0/typesafe_enum.jar"/>
    
        <!-- 3rd party dependencies -->
        <property name="junit.jar" value="${ext_libdir}/junit/3.8.2/junit.jar"/>
    
        <!-- Path elements required in the master buiild file (they MUST be defined even if empty). -->
        <path id="component.tcs-dependencies">
            <pathelement location="${typesafe_enum.jar}"/>
            <pathelement location="${base_exception.jar}"/>
            <pathelement location="${object_factory.jar}"/>
            <pathelement location="${object_factory_configuration_api_plugin.jar}"/>
            <pathelement location="${object_formatter.jar}"/>
            <pathelement location="${configuration_api.jar}"/>
            <pathelement location="${logging_wrapper.jar}"/>
        </path>
    
        <path id="component.3rdParty-dependencies"/>
    
        <path id="component.test.3rdParty-dependencies">
            <pathelement location="${junit.jar}"/>
        </path>
    
    </project>
    
build-dependencies.xml file of Hibernate Data Entitlement Enforcer 1.1.0 component
<project name="Dependency Import" default="dist" basedir=".">
    <!-- Properties used in compile_targets ANT target for JDK version x -->
    <property name="component.target" value="1.4"/>
    <property name="component.bootclasspath" value="${java_1_4_bootclasspath}"/>

    <!-- property file defining the component's TopCoder dependencies -->
    <property name="base_exception.jar" value="${tcs_libdir}/base_exception/2.0.0/base_exception.jar"/>
    <property name="data_entitlement.jar" value="${tcs_libdir}/data_entitlement/1.3.0/data_entitlement.jar"/>
    <property name="logging_wrapper.jar" value="${tcs_libdir}/logging_wrapper/2.0.0/logging_wrapper.jar"/>
    <property name="object_formatter.jar" value="${tcs_libdir}/object_formatter/1.0.0/object_formatter.jar"/>
    <property name="security_facade.jar" value="${tcs_libdir}/security_facade/2.1.3/security_facade.jar"/>
    <property name="user_group_manager.jar" value="${tcs_libdir}/user_group_manager/1.0.1/user_group_manager.jar"/>

    <!-- 3rd party dependencies -->
    <property name="junit.jar" value="${ext_libdir}/junit/3.8.2/junit.jar"/>

    <property name="hibernate.dir" value="${ext_libdir}/hibernate/3.2.5.GA"/>
    <property name="hibernate.jar" value="${hibernate.dir}/hibernate3.jar"/>
    <property name="jta.jar" value="${hibernate.dir}/lib/jta.jar"/>
    <property name="dom4j.jar" value="${hibernate.dir}/lib/dom4j-1.6.1.jar"/>
    <property name="commons-logging.jar" value="${hibernate.dir}/lib/commons-logging-1.0.4.jar"/>
    <property name="commons-collections.jar" value="${hibernate.dir}/lib/commons-collections-2.1.1.jar"/>
    <property name="cglib.jar" value="${hibernate.dir}/lib/cglib-2.1.3.jar"/>
    <property name="asm-attrs.jar" value="${hibernate.dir}/lib/asm-attrs.jar"/>
    <property name="asm.jar" value="${hibernate.dir}/lib/asm.jar"/>
    <property name="antlr.jar" value="${hibernate.dir}/lib/antlr-2.7.6.jar"/>

    <property name="mysql.jar" value="${ext_libdir}/mysql/5.0.5/mysql-connector-java-5.0.5-bin.jar"/>


    <!-- the required path elements must be defined even if empty -->
    <path id="component.tcs-dependencies">
        <pathelement location="${data_entitlement.jar}"/>
        <pathelement location="${base_exception.jar}"/>
        <pathelement location="${logging_wrapper.jar}"/>
        <pathelement location="${user_group_manager.jar}"/>
        <pathelement location="${security_facade.jar}"/>
        <pathelement location="${object_formatter.jar}"/>
    </path>

    <path id="component.3rdParty-dependencies">
        <pathelement location="${hibernate.jar}"/>
        <pathelement location="${jta.jar}"/>
        <pathelement location="${dom4j.jar}"/>
        <pathelement location="${commons-logging.jar}"/>
        <pathelement location="${commons-collections.jar}"/>
        <pathelement location="${cglib.jar}"/>
        <pathelement location="${asm-attrs.jar}"/>
        <pathelement location="${asm.jar}"/>
        <pathelement location="${antlr.jar}"/>
    </path>

    <path id="component.test.3rdParty-dependencies">
         <pathelement location="${junit.jar}"/>
         <pathelement location="${mysql.jar}"/>
    <path>
</project>

Note the format used for referencing the dependencies : ${component.repository}/${component.name}/${component.version}/${component.jar.name}
Also note that the values set for component.target and component.bootclasspath (its value in this example is taken from the global properties file) will be used when executing compile_targets target.

In the previous examples that all libraries are specified only by using their name (base_exception.jar, configuration_api.jar, junit.jar, antlr.jar) without specifying in the property name the version number as well. This detail is very important as on the assembly phase the assembler can define properties for the libraries used by him in the topcoder_global.properties file (overriding in this way the property values defined in build-dependencies.xml file - very important when the project needs a specific library version for a library (ex: log4j-1.2.12.jar), but a component has been built with reference to another version of that library (ex: log4j-1.2.9.jar)).
This is the reason why the build-dependencies.xml file is NOT allowable to have structures of this kind:

Structure not allowable in build-dependencies.xml file
    <path id="hibernate.classpath">
        <fileset dir="${hibernate-annotations.dir}">
            <include name="hibernate-annotations.jar"/>
            <include name="lib/ejb3-persistence.jar"/>
            <include name="lib/hibernate-commons-annotations.jar"/>
        </fileset>
    </path>

or

Structure not allowable in build-dependencies.xml file
    <path id="hibernate.classpath">
        <fileset dir="${hibernate-annotations.dir}">
            <include name="**/*.jar"/>
        </fileset>
    </path>

because this would prevent someone from using the libraries that were specified in topcoder_global.properties file.
It seems tedious at first having to write by hand property declarations for all the libraries of a framework from a component developer point of view, but from a assembler point of view this is the way to go (for making things easier for himself the developer should write small apps which would scan the directory structure of the framework and automatically generate xml property declarations and from which the developer should have to remove the libraries not needed in compiling/testing his component).
Example of the above properties as specified in the global properties file:

Third Party Library Locations in topcoder_global.properties file
#test dependencies
junit.jar=${ext_libdir}/junit/4.4/junit.jar

You can see above that the version number for junit.jar library is 4.4 and is different from the value used in build-dependencies.xml file (3.8.2).These properties are easiest specified in the global properties file. Note that components may overlap their dependencies, so by default all components that use junit.jar will use the same version of it. The integration environment should be setup with the same dependencies as application's will use for best results.


Another thing that is worth talking about here is the way in which the libraries are referenced. In the examples bellow can be seen that a TopCoder dependency is declared:

Template on how to declare dependency TopCoder componentns
    <property name="component_name.jar" value="${tcs_libdir}/component_name/component_version/component_jar_name.jar"/>

or that a third party library is declared in the following way :

Template on how to declare dependency 3rd party components
    <property name="component_name.jar" value="${ext_libdir}/component_name/component_version/component_jar_name.jar"/>

or this way :

Template on how to declare dependency 3rd party frameworks
    <property name="framework.dir" value="${ext_libdir}/framework_name/framework_version"/>
    <property name="component_name.jar" value="${framework.dir}/component_jar_name.jar"/>
    <!-- the directory structure of the frameworks differs from framework to framework. For
    some of them we may have internal directories like: lib/, modules/, j2ee1.3/, j2ee1.4) -->
    <property name="framework_library.jar" value="${framework.dir}/lib/framework_library.jar"/>
  • build-override.xml - this is an OPTIONAL file used in building components and is to be used only when the common behavior of the targets defined in build.xml file NEEDS to be overridden (e.a. when testing with cactus the component) or there is a need for custom targets in the build script.
    There will be shown several examples on how this file should look like in order to give you a better understanding of its purpose. Please note in this samples that there are defined extra targets which don't fit in the generic build file and macrodef instructions which have the ability to be overridden when they are declared several times in the build scripts. Since ANT doesn't have the ability to override targets, using macrodef tasks is used as solution for this kind of problem.
build-override.xml of HTTP Functional Entitlement Enforcer 1.1.0 component
<project name="Override Macrodef Standard Behaviour Import" basedir=".">
    <!-- macrodef override definition in order to change the standard behaviour from
         build.xml/build_dist.xml
     -->

    <target name="instrument" depends="compile">
        <mkdir dir="${testlogdir}"/>
        <mkdir dir="${instrumented.dir}"/>
        <delete file="${cobertura.datafile}"/>
        <copy todir="${instrumented.dir}">
            <fileset dir="${build_classdir}">
                <include name="**/*.class"/>
            </fileset>
        </copy>
        <cobertura-instrument todir="${instrumented.dir}" datafile="${cobertura.datafile}">
            <!-- all included -->
            <fileset dir="${build_classdir}">
                <include name="**/*.class"/>
                <include name="config.xml"/>
            </fileset>
        </cobertura-instrument>
    </target>


    <!-- Create a war used for testing -->
    <target name="war" depends="instrument,compile_tests">
        <mkdir dir="${builddir}/lib"/>
        <jar jarfile="${builddir}/lib/${component}.jar" basedir="${instrumented.dir}"/>

        <property name="test_reflibs_temp_dir" value="test_reflibs_temp_dir"/>
        <mkdir dir="${test_reflibs_temp_dir}"/>
        <!--
        Create a temporary directory where to store the .jar files from test_reflibs/
        directory because in WEB-INF/ directory of the .war archive the .jar files must
        be copied flatten  so <code>lib dir="${basedir}/${test_reflib}"</code> is not an option.
        -->
        <copy todir="${test_reflibs_temp_dir}" flatten="true">
            <fileset dir="${basedir}/${test_reflib}">
                <include name="**/*.jar"/>
            </fileset>
        </copy>
        <war destfile="${builddir}/${distfilename}.war" basedir="${testfiles}" webxml="${testfiles}/WEB-INF/web.xml">
            <webinf dir="${testfiles}/WEB-INF" excludes="web.xml, **/*.jar"/>
            <classes dir="${testfiles}" excludes="web.xml"/>
            <lib file="${builddir}/lib/${component}.jar"/>
            <lib file="${base_exception.jar}"/>
            <lib file="${configuration_api.jar}"/>
            <lib file="${configuration_persistence.jar}"/>
            <lib file="${functional_entitlement.jar}"/>
            <lib file="${logging_wrapper.jar}"/>
            <lib file="${object_factory.jar}"/>
            <lib file="${object_formatter.jar}"/>
            <lib file="${object_factory_configuration_api_plugin.jar}"/>
            <lib file="${security_facade.jar}"/>
            <lib file="${user_group_manager.jar}"/>

            <lib file="${aspectjrt.jar}"/>
            <lib file="${commons.httpclient.jar}"/>
            <lib file="${commons.logging.jar}"/>
            <lib file="${httpunit.jar}"/>
            <lib file="${cargo.jar}"/>
            <lib file="${nekohtml.jar}"/>
            <lib file="${jstl.jar}"/>
            <lib file="${standard.jar}"/>
            <lib file="${cactus.jar}"/>
            <lib file="${junit.jar}"/>
            <lib file="${log4j.jar}"/>
            <lib dir="${test_reflibs_temp_dir}"/>
        </war>
        <!-- Remove temporary directory holding the libs from test_reflibs directory. -->
        <delete dir="${test_reflibs_temp_dir}"/>
    </target>

    <!-- The deployCommand is overriden from the master build file in order to deploy the
         component war to Apache Tomcat Web server. So when executing deploy target this is the
         macrodef that will be executed instead of the one from the master build file. -->
    <macrodef name="deployCommand">
        <!-- standard deploy command -->
        <sequential>
            <copy file="${builddir}/${distfilename}.war" todir="${cactus.home.tomcat5x}/webapps"/>
        </sequential>
    </macrodef>

    <!-- Test macros which are overriden because testing is made  in a custom way by using Cactus
         and not with JUnit. -->
    <macrodef name="test.setup">
        <sequential>
            <antcall target="war"/>

            <cactifywar srcfile="${builddir}/${distfilename}.war" destfile="${builddir}/${distfilename}-cactified.war">
                <classes dir="${build_testclassdir}"/>
                <servletredirector/>
            </cactifywar>
        </sequential>
    </macrodef>

    <macrodef name="test.execute">
        <!-- cactus test task -->
        <sequential>
            <mkdir dir="${testlogdir}"/>

            <cactus warfile="${builddir}/${distfilename}-cactified.war" fork="yes" failureproperty="tests.failed"
                    haltonerror="false">

                <sysproperty key="net.sourceforge.cobertura.datafile" file="${cobertura.datafile}"/>
                <classpath>
                    <path refid="test.build.classpath"/>
                    <path refid="cobertura.classpath"/>

                    <pathelement location="${build_testclassdir}"/>
                    <pathelement location="${instrumented.dir}"/>
                </classpath>

                <containerclasspath>
                    <path refid="cobertura.classpath"/>
                </containerclasspath>

                <containerset>
                    <tomcat5x if="cactus.home.tomcat5x" dir="${cactus.home.tomcat5x}"
                              output="${testlogdir}/tomcat5x.out"
                              todir="${testlogdir}"/>
                </containerset>

                <test name="${package}.AllTests" todir="${testlogdir}">
                    <formatter type="plain" usefile="true"/>
                    <formatter type="xml" usefile="true"/>
                    <formatter type="brief" usefile="false"/>
                </test>

            </cactus>
        </sequential>
    </macrodef>

</project>

build-override.xml from Asset Text Searcher
<project name="Override Macrodef Standard Behaviour Import" basedir=".">
    <!-- macrodef override definition in order to change the standard behaviour from
         build.xml/build_dist.xml
     -->

    <!-- The test.execute macrodef is overriden from the master build file because
         the tests require a huge amount of memory. So for avoiding an out of memory
         error from the forked JVM we need to setup a higher value for maxmemory attribute
         of the junit task. -->
    <macrodef name="test.execute">
        <!-- standard test task -->
        <sequential>
            <mkdir dir="${testlogdir}"/>
            <mkdir dir="${instrumented.dir}"/>
            <delete file="${cobertura.datafile}"/>
            <cobertura-instrument todir="${instrumented.dir}" datafile="${cobertura.datafile}">
                <!-- all included -->
                <fileset dir="${build_classdir}">
                    <include name="**/*.class"/>
                </fileset>
            </cobertura-instrument>
            <junit fork="true" haltonerror="false" maxmemory="1024m">
                <sysproperty key="net.sourceforge.cobertura.datafile" file="${cobertura.datafile}"/>
                <classpath location="${instrumented.dir}"/>
                <classpath location="${build_testclassdir}"/>
                <classpath refid="test.build.classpath"/>
                <classpath refid="cobertura.classpath"/>
                <test name="${package}.AllTests" todir="${testlogdir}">
                    <formatter type="plain" usefile="true"/>
                    <formatter type="xml" usefile="true"/>
                </test>
            </junit>
        </sequential>
    </macrodef>
</project>

If you have the curiosity to read the master buid file (build.xml) you can see build-dependencies.xml file and optionally build-override.xml file are imported in the master build file. So you don't have to worry that some of the properties are not seen in those files by the intellisense of some of the IDEs used to edit those build files. A good practice when you develop the build files is to add a temporary import statement for the build.xml file and this way you have all the properties correctly highlighted in you IDE. When you finish developing the build-dependencies.xml/build-override.xml file you can remove the temporary import statement.

  • conf - configuration files for the component
  • docs - documents of the component
    • Typically this directory needs to include a fileset of documents having this format :
      <fileset dir="${docsdir}" casesensitive="no">
          <include name="${distfilename}_Class_Diagram*"/>
          <include name="${distfilename}_Use_Case_Diagram*"/>
          <include name="${distfilename}_Sequence_Diagram*"/>
          <include name="${distfilename}_Requirements_Specification.pdf"/>
          <include name="${distfilename}_Requirements_Specification.rtf"/>
          <include name="${distfilename}_Component_Specification.pdf"/>
          <include name="${distfilename}_Component_Specification.rtf"/>
          <include name="${distfilename}.tcuml"/>
      </fileset>
      

      Note that ${distfilename} represents the component distribution file name.
      The documents belonging, if it is the case, to a previous version of the component need to be removed from docs/ directory when adding in svn the new version. Be sure to have the files from docs/ directory matching EXACTLY the format specified earlier.
      To help out what I'm trying to say here I will add an image on how the structure for docs/ directory should look like.

  • test_files - file resources needed for testing
  • test_reflib - this is an OPTIONAL directory containing TopCoder components that are used by the test code
    • Please note these runtime jars are provided for the purposes of compiling and running the test code, not for any other use, including production. The components that are required for the production code must be referenced in the build-dependencies.xml file.
    • To make things coherent please keep the same structure in this directory for the libraries included as in tcs_libdir. An example file structure of test_reflib/ directory would be the following:
         test_reflib
             -multimap
                -1.0
                   -multimap.jar
      

Note that the location from which can be retrieved the files needed in building is
Master build files
Note that in this location can be found build files for generic components in generic directory, but also build files specific for each of TopCoder's clients to be used in building custom components.


3.1.  Configure the environment.

You should read the component specification first, make sure in what environment the component will be run. It may include the following issues:
Platform - Windows, Linux, etc
Compiling Target - Java 1.3/1.4/1.5
Dependencies - Including TopCoder or third party components, required version of these components should be used.

Especially for custom ones, for example if the tests compiled successfully under Oracle 10.2, but failed under Oracle 10.1, and the required version is the latter, you should report it to PM. It is very important to respect the constraints imposed by the Component Specification regarding the Environment Requirements.

3.2  Remove useless files

Remove auto-generated files like :

  • conf/putYourConfigFilesHere.txt
  • test_files/putYourTestFilesHere.txt
  • .txt files from ${srcmain}
  • Thumbs.db (this files can be added by mistake in the submission, but they are useless)

Remove also the files which are not really needed by the distribution of the component. I mean here files like .txt files containing details about the final fixes, .log files.
Remove all mocked files, use real dependencies, you can find the dependencies from http://software.topcoder.com/. If the dependencies have not been built yet, report it to PM. If the component could not compile and execute successfully using the real dependencies, report it to PM.

3.3.  Deploy Dependent Components

In the file build-dependencies.xml there is a listing for each TopCoder component and version that is a dependency. The runtime jars for each of these components must be deployed to the TopCoder library location for the build to work. The pattern is ${tcs_libdir}/"component name"/"component version"/"component name".jar. For example using the above value for tcs_libdir, the current version (1.1.0) of typesafe enum component would be located at: c:/code/lib/topcoder-java/typesafe_enum/1.1.0/typesafe_enum.jar

These jars can either be downloaded and saved in the correct location - or by running the build for each dependent component.


3.4.  External libraries directory structure.

TopCoder is not allowed to re-distribute the 3rd party libraries on which some of the components are dependent (like junit for example). This is the reason why attached to document you will find here a listing of TopCoder's ${ext_libdir} directory. Please structure your ${ext_libdir} directory in the same way as TopCoder has structured these libraries to avoid having problems when integrating the component on the Bamboo server.Note that there will be other libraries added, when needed, to the external libraries directory on the Bamboo machine, so please check out from time to time if anything has updated on the 3rd party directory listing.


4.  Custom testing scenarios used in the build scripts

4.1.  Performing initialization/cleanup operations when testing against a database

As you have seen in chapter 3 of this document in build-override.xml can be scripted custom testing scenarios for the components which need extra initializations/cleanup for the testing environment.
One of the most common types of components are the ones which are interacting with databases.Here will be detailed how to handle this kind of scenario. As an example will be used the component Auction Framework which can be found in TopCoder SVN repository here.
Bellow you can see the content of the build-override.xml file for this component:

build-override.xml from Auction Framework
<project name="Override Macrodef Standard Behaviour Import" basedir=".">
    <!-- macrodef override definition in order to change the standard behaviour from
         build.xml/build_dist.xml
     -->

    <macrodef name="test.setup">
        <sequential>
            <sql
                driver="com.mysql.jdbc.Driver"
                url="jdbc:mysql://localhost:3306/test"
                userid="root"
                password="topcoder"
                autocommit="true"
                src="test_files/dbsetup.sql">
            <classpath>
                <pathelement location="${mysql.jar}"/>
            </classpath>
            </sql>

        </sequential>
    </macrodef>

    <macrodef name="test.teardown">
        <sequential>
            <sql
                driver="com.mysql.jdbc.Driver"
                url="jdbc:mysql://localhost:3306/test"
                userid="root"
                password="topcoder"
                src="test_files/dbteardown.sql">
            <classpath>
                <pathelement location="${mysql.jar}"/>
            </classpath>
            </sql>
        </sequential>
    </macrodef>
</project>

As you can see in the overriden test.setup and test.teardown overriden macrodef elements two files are reffered:

  • test_files/dbsetup.sql
  • test_files/dbteardown.sql
    In order to provide you with the details needed to comprehend this kind of testing scenario the content of those two files will be presented here:
    dbsetup.sql
    # Creating the database used in testing Auction Framework component
    drop database if exists `TCJAVA-23768027`;
    
    create database `TCJAVA-23768027`;
    
    use `TCJAVA-23768027`;
    
    # Auction Framework 1.0 Tables
    
    # The transaction is needed because the mysql jdbc driver would give sql errors otherwise when
    # running this script because <code>auction</code> table is referenced bellow in the script.
    START TRANSACTION;
    CREATE TABLE auction (
      auction_id  BIGINT NOT NULL,
      summary     VARCHAR(255) NOT NULL,
      description VARCHAR(1000),
      item_count  INT NOT NULL,
      minimum_bid INT NOT NULL,
      start_date  DATETIME NOT NULL,
      end_date    DATETIME NOT NULL,
      PRIMARY KEY (auction_id)
    );
    COMMIT;
    
    CREATE TABLE bid (
      auction_id       BIGINT NOT NULL,
      bidder_id        BIGINT NOT NULL,
      effective_amount INT,
      max_amount       INT NOT NULL,
      timestamp        DATETIME NOT NULL,
      FOREIGN KEY (auction_id) REFERENCES auction(auction_id)
    );
    
    # Tables for IDGenerator
    
    START TRANSACTION;
    CREATE TABLE id_sequences (
      name				VARCHAR(255) NOT NULL  PRIMARY KEY,
      next_block_start	BIGINT NOT NULL,
      block_size		INT NOT NULL,
      exhausted			INT NOT NULL default 0
    );
    COMMIT;
    
    INSERT INTO id_sequences (name, next_block_start, block_size) VALUES ("db_auction_persistence_id", 0, 1);
    
    
dbteardown.sql
# Dropping the database used in testing Auction Framework component
drop database if exists `TCJAVA-23768027`;

For having an example on how to setup/teardown the testing environment for a component that needs Informix Db Engine check out from Ban Management Persistence SVN Repository the files:

  • test_files/dbsetup.sql
  • test_files/dbteardown.sql
    which should give you a starting point on how to handle this thing.

For testing a component that needs Oracle db, please check the section 4.2.2 from the document http://www.topcoder.com/wiki/display/tc/.Net+Component+Build+Process and you'll have the details necessary to complete this task.

So to summarize when encountering components which need db initialization / cleanup you must :

  • create the .sql scripts for initialization/cleanup of the database. Note that the database name is not a common name, it is the build key (TCJAVA-23768027) of the Bamboo plan for the component(this way we ensure that it is unique and doesn't collide with the databases created by some other components). If the component requires more databases for testing, use the build key as a prefix for the name of the databases.
  • add a build-override.xml file in which override test.setup and test.teardown macrodefs. In test.setup you must run the scripts used for initializing the database schema & its tables. In test_teardown macrodef you have to drop the database created in test.setup


5.  Build the Component

From the command line the following ant targets are most useful:

  • ant compile - build the component code
  • ant compile_tests - builds the test code
  • ant compile_targets - verify if the component sources compile for the target JDK
  • ant test - runs the unit tests
  • ant coveragereport - generates test coverage reports
  • ant reports_all - builds the JUnit and Cobertura jars
  • ant deploy-lib - tests the component and then moves the runtime jar to the standard library
  • ant dist_tcs - moves the runtime jar as well as the jar containing the distribution of the component to the library directory.
    • The library directory component will be ${tcs_libdir} for a generic component, but can also be the library directory for the client's components in case if a custom component is built.


6.  Commit the modifications made on the component after the build is done to SVN

Please make sure, before commiting, that every change made to the component (from file additions to modifications or remove of useless files) to appear in the Commit dialog.
You should log the modifications you made when updating the svn, including trivial things like "Useless files are removed". Normally when the building of a component is done you should put a comment like "Build x.x.x.x successful" where x.x.x.x is the version for the component.

At the end of this process, the SVN file structure of the component to be built will look like this :

Be sure always to check if the file structure is similar to what can be see in the image presented earlier.
The file build-override.xml can be present in this file structure when, as it was previously stated, a custom test scenario is made or custom targets are needed in the build script.
In the case one or more of the conf/, test_reflib/, test_files/ directories are empty you don't need to add them to svn as they are useless in this case for the component.
You may also find some other files in the trunk/ of the component (like bamboo_build.xml, Changelog.txt, etc.). Please leave them as they are as they concern internal operations made at TopCoder.

In the image above you can see that some of the items are highlighted in yellow. Please pay extra-attention to having all these items in svn and updated correctly to match the component's needs.



Please feel free to comment any of the details presented in this tutorial if you feel that it can be polished to be easier to understand or if there is more information needed.