TeamCity for Java Projects
https://www.safaribooksonline.com/library/view/learning-continuous-integration/9781849699518/ch04.html
我们将用TeamCity为Java项目建立CI(持续集成)的环境:
- 用Ant 创建Java项目文件
- 为项目执行简单和复杂的Maven生命周期活动
- 开始用Gradle来创建一个项目
- 学习数据库迁移,以及它们在CI中的的角色
在这个过程中,我们还将寻找其他的重要工具在Java生态系统,如JUnit,Emma,JaCoCo等等。我们也将探索TeamCity这个丰富的集成工具,,从而使它非常简单和直接的设置我们的建立。
在TeamCity中使用Ant
Apache Ant (http://ant.apache.org/) is a build tool along the lines of Make, especially for Java projects. It is written in Java, and hence provides the ability for teams already using Java to extend their build tool using Java as well. However, Ant is not limited to Java projects alone and can be used to build any source code, including .NET, Python, and Ruby.
The build files in Ant are written using XML, and one of the main features of Ant is its cross-platform nature. We will first cover some basics of Ant, including installation, a sample build file, and getting Ant to build our Java source code in a developer workstation, before proceeding to set up Ant builds on TeamCity.
Installing Ant
Ant packages can be downloaded from http://ant.apache.org/bindownload.cgi. Installing Ant involves extracting the downloaded package and adding the bin directory to the PATH
environment variable. Also, the ANT_HOME
environment variable has to be set up, pointing to the extracted folder location:
wget http://www.trieuvan.com/apache/ant/binaries/apache-ant-1.9.3-bin.tar.gz
tar xvfz apache-ant-1.9.3-bin.tar.gz
export ANT_HOME="~/Downloads/apache-ant-1.9.3"
export PATH="$PATH:$ANT_HOME/bin"
With the previous set of commands, first we download the binary distribution using the wget
command. Then, we extract the just-downloaded file using the tar
command. We then set the ANT_HOME
environment variable to the location that we extracted Ant to. We also add the bin folder in ANT_HOME
to PATH
so that the Ant command is available for use.
Tip
The steps given previously will change slightly for different operating systems. The essential steps are downloading the package, decompressing it, and adding environment variables.
The environment variables set using the export
command are available only for the current session. To persist these environment variables, steps appropriate to the platform, such as adding these commands to the ~/.bash_profile
file, have to be performed.
A simple Ant build file to build a Java source will look like the following code:
<project name="ant_ci_example" default="dist" basedir=".">
<description>
Build file for sample Java project
</description>
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile">
<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/ant-ci-example.jar" basedir="${build}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
This is a basic build file that defines a project named ant_ci_example
. Within it, we define a few properties using the <property />
tag to configure where source code is located, where the build output is to be placed, and finally, where the distribution file needs to be generated.
We then define targets to clean our builds, perform a compile, and generate the distribution.
Note
A target in Ant, defined using the <target />
tag, is a series of steps that perform someactivity during the build process. As mentioned previously, clean
is one of the targets defined in the build file that performs two cleanup steps—delete the build folder and delete the distribution folder. Ant has powerful concepts around targets, whereby targets can depend on other targets such that any execution of a particular target will also execute its dependencies. Ant also knows if a particular target has already been executed as part of the current build and will not execute it again.
The previous build file can be saved as build.xml
in the directory with our Java source. Now, running Ant from the command line will run the build—execute the targets—and hence generate the needed distributions.
Note that, in the build file, the project has the dist
target as default, and hence when we run the build file with Ant, the dist
target is executed. But, since the dist
target has a dependency on the compile
target, which itself has a dependency on the init
target, the init
and compile
targets are executed as well, in that order. A heartening BUILD SUCCESSFUL message should inform us that everything is working fine. We can also see which targets were executed and what they did in the build log.
With the basics of Ant done, it is time to set up TeamCity to perform the builds for us. As in Chapter 3, Getting Your CI Up and Running, I have set up a sample project on GitHub, which has Java source with the basic Ant build file that we just discussed. The project is located at https://github.com/manojlds/Ant_CI_Example.
We will begin by adding a new project—JAVA CI with TeamCity—and add a new build configuration within it— named ant_build
. The VCS root for this build will be Git-based, and points to the repository on GitHub. These steps are similar to the ones we covered in Chapter 3, Getting Your CI Up and Running.
When it comes to adding a build step, we will choose Ant as the build runner in this case. A view of the settings that are to be configured for this build runner is shown in the following screenshot:

The Path to a build.xml file option allows us to specify the build file name. It is prepopulated with build.xml
, which is Ant's default build file name. Alternatively, we can choose the Build file content option and specify the build file content directly in TeamCity.
Note
It is not recommended to use the Build file content option. It is ideal to have the build file version controlled in VCS along with the rest of the source code. This ties in with one of the practices of CI—everything that is needed to build the project is put in VCS with the rest of the source code.
Working directory can be specified if we want Ant to be executed from a directory other than the checkout directory. Leaving it blank uses the checkout root as the working directory. The Targets option is used to specify the name of the targets that are to be run. This is very much similar to how we can specify targets from the command line—using a space-separated list of targets. Leaving this blank will call the default target, which is fine for our build file.
Ant home path is the location of Ant that we would like to use for the build. TeamCity comes bundled with its own version of Ant (1.8.2 for TeamCity 8.0) that it uses automatically if this setting is left blank. We can alternatively provide this path if we want to use our own version of Ant (older or newer, as needed).
The Additional Ant command line parameters setting can be used to pass additional flags to Ant, such as the flag to produce verbose build output.
The build runner also provides the ability to set Java parameters—JDK home path, which is taken from JAVA_HOME
if not specified, and other JVM command line parameters, such as memory settings. The latter can be tweaked as needed, based on the performance of the build task.
There are parameters related to Test and Coverage available for the Ant build runner too. The Run recently failed tests first option runs tests that failed in the previous build first, before other tests, so that we can get quick feedback on whether the failing tests have passed in this build. The Run new and modified tests first option runs new tests, or tests that were changed from previous builds first, for similar reasons.
Code coverage-related parameters allow us to configure coverage tools, which we will look into in detail in the upcoming sections of this chapter.
Click on Save to add the build step and create the build configuration.
Let's trigger the build to see the Ant build passing.
With the basic build passing, it's time to add some unit tests. The unit tests' target in the updated build file is as follows:
<target name="unit-tests" depends="compile">
<junit printsummary="yes" haltonfailure="yes" showoutput="true" fork="true" forkmode="once">
<classpath>
<pathelement location="lib/junit-4.11.jar"/>
<pathelement location="lib/hamcrest-core-1.3.jar"/>
<pathelement location="${build}"/>
<pathelement location="${src}"/>
</classpath>
<formatter type="xml"/>
<batchtest fork="yes" todir="${reports.tests}">
<fileset dir="${src.tests}">
<include name="**/*Test.class"/>
</fileset>
</batchtest>
</junit>
</target>
The dist
target now depends on unit-tests
(we don't want a distribution that hasn't passed our tests). unit-tests
depends on compile
as we need to compile the source code before running the tests.
Tip
The full contents of the build.xml
file can be obtained athttps://github.com/manojlds/Ant_CI_Example/blob/master/build.xml.
Due to TeamCity's close integration with Ant, the status message should be updated with the number of tests passed/failed information when the build finishes running, as shown in the following screenshot:

We saw that the Ant build runner had options to set up code coverage. Let's also enable that feature now that we have unit tests running in our build. Go to the ant_build
build configuration's edit page and then to the edit page of the build step.
Let's add Emma as a coverage tool for the step.
The Coverage instrumentation parameters field is used to send additional parameters, such as filters for classes, to be ignored from coverage (in this case, the Test
classes).
Adding the coverage to our build brings in a lot of useful features. The coverage reports are automatically uploaded to the TeamCity server, and they are available through the Code Coverage tab, accessed from the build run page of the build configuration, as seen in the following screenshot:

Additionally, the coverage trend can also be seen from the graphs available in theStatistics tab of both the build configuration and the project.
When adding coverage to our Ant build, we used the Emma coverage feature of the Ant build runner in TeamCity.
Alternatively, we could have used the Emma-based tasks available for Ant and have the coverage done from our build file itself.
This is a situation that is not specific to Ant alone. It can occur in any project/stack. A rule of thumb is that our build scripts should be able to do things the same way between the CI server and a local developer box. Using TeamCity features, such as the coverage in this case, obviously means that we won't be doing coverage the same way on a developer machine as well. While it is fine to use some features like the Ant build runner, which mainly provides an easy way to set up calls to Ant, it may not be OK to use other features, such as the Emma coverage provided by TeamCity.
Also, as mentioned, maintaining everything needed to build a project in VCS is an important CI principle. Having things like coverage configured in build scripts is the easiest way of being true to that principle. Relying on TeamCity to provide such features may not make that possible.
Having said all that, there are use cases where we may want to use the built-in feature, especially when we are starting out with CI as these features make it dead simple to set up the steps necessary for CI.
Also, since this book is about highlighting the various features that TeamCity brings to the table, the book will be going into detail about many of them, but it is not a recommendation to use these features in all scenarios.
We previously saw in Chapter 3, Getting Your CI Up and Running, that build parameters are classified into three types:
- Configuration parameters
- System properties
- Environment variables
We saw how configuration parameters and environment variables are used. System properties are very useful with tools like Ant. In our sample build file, we defined many properties, like the one to specify the path to the source build. Using system properties, it is possible to override these values, or even add new properties that can be used in our Ant build file. For example, our test reports were being created at the location specified by the reports.tests
property:
<property name="reports.tests" location="reports"/>
By adding a system property with the same name (but with a system.
prefix) in TeamCity, we can automatically set this value to something else, say teamcity-reports
. This step is shown in the following screenshot:

Tip
These system properties are passed to the Ant command using the –Dproperty-name=property-value
syntax. It is not only the user defined system properties, but the TeamCity generated ones, such as build.vcs.number.1,
are passed to Ant as well.
Using the system properties in TeamCity is recommended over passing these ourselves through the command line. TeamCity properly escapes the properties when passing them to tools like Ant. Also, all the properties are defined in one page, so it is very easy to see what properties are needed and edit them when needed.
With the TeamCity features related to Ant covered, we will move on to the kind of support that TeamCity provides for another very popular Java tool—Maven.