ANT是用过的最好的Build工具.CruiseControl则通过不断检查SCM (VSS, ClearCase, StarTeam, etc), 一旦发现改动, 调用ANT进行编译, 部署, 实现即时的集成. 一旦编译失败, 则立刻自动发Email给"始作俑"者, 提醒其修正代码. CC自带了一个Web应用, 可是随时查看编译状况和历史状况(包括自从前一次Build, 有哪些文件,是谁做了改动, Build失败的原因, Build的成果(自定义的, 比如最后的打包文件, 自动生成的JavaDoc) 总之, 通过集成Ant和CC, 尽量避免了Daily Build容易Broken的缺点, 而且自动化程度更高. 另外Ant和CC都分别有.net版本, 在下一个.net项目中, 还打算使用它们来进行集成控制.
安装CruiseControl
和AntHill一样,使用CruiseControl构建持续集成系统,需要Tomcat,Ant,CVSNT和WinCVS的支持(参见AntHill:构建Nightly Build系统)。安装CruiseControl很简单,下载cruisecontrol-2.2.zip(参见持续集成资源),解压到安装目录C:/BuildServer,即可完成安装。
安装Tomcat4.1到C:/BuildServer目录,并在C:/BuildServer目录创建workingCopy子目录,作为CruiseControl的工作目录。BuildServer的目录结构如下图所示,其中CruiseControl的启动脚本cruisecontrol.bat(或cruisecontrol.sh)位于BuildServer/cruisecontrol-2.2/main/bin目录下。
准备构建目录结构
1.创建CruiseControl的日志目录:C:/BuildServer/cruisecontrol-2.2/main/logs/frameworkProject。
2.创建CVS的工作目录:C:/BuildServer/workingCopy/frameworkProject。
3.创建工作目录的代码库目录:C:/BuildServer/workingCopy/frameworkProject/lib。
4.创建工作目录的单元测试报告目录:C:/BuildServer/workingCopy/frameworkProject/reports/junit/data。
5.创建工作目录的jar文件存放目录:C:/BuildServer/workingCopy/frameworkProject/dist。
初始化代码工作目录
在C:/BuildServer/workingCopy/frameworkProject目录下,使用WinCVS CheckOut src模块:cvs co src。
准备项目的依赖代码库
把构建项目的依赖代码库复制到C:/BuildServer/workingCopy/frameworkProject/lib。完成之后的目录结构如上图所示。
编写项目构建脚本
构建脚本build.xml和build-cc.xml位于C:/BuildServer/workingCopy/frameworkProject目录下。其中build.xml是项目的Ant构建脚本,而build-cc.xml负责帮助CruiseControl从CVS服务器上update源代码到本地的工作目录,然后调用build.xml构建项目。
build-cc.xml
<?xml version="1.0"?> <project name="CruiseControlWrapperForFrameworkProject" default="build" basedir="."> <target name="update"> <!-- update the working copy from the cvs --> <exec executable="cvs"> <arg line="-d :pserver:talent:talent @10.75.140.128:/cvsroot update src"/> </exec> </target> <target name="build" depends="update"> <!-- invoke the project's build script --> <ant antfile="build.xml" target="jar"/> </target> </project>build.xml
<?xml version="1.0"?> <project name="frameworkProject" default="compile"> <description> This is our framework project which we're putting on CruiseControl </description> <target name="setup" depends="setup.properties, setup.paths" /> <target name="setup.properties"> <property name="src.main" value="src" /> <!--<property name="src.test" value="src/test" />--> <property name="classes" value="classes"/> <property name="classes.main" value="${classes}/main" /> <!--<property name="classes.test" value="${classes}/test" />--> <property name="libs" value="lib" /> <property name="dist" value="dist" /> <property name="reports" value="reports" /> <property name="reports.junit.data" value="${reports}/junit/data" /> </target> <target name="setup.paths"> <path id="classpath.main"> <pathelement location="${classes.main}" /> </path> <path id="classpath.lib"> <fileset dir="${libs}"> <include name="**/*.jar" /> </fileset> </path> </target> <target name="clean" depends="setup"> <delete dir="${classes}" failonerror="false" /> <delete dir="${reports}" failonerror="false" /> <delete dir="${dist}" failonerror="false" /> </target> <target name="compile" depends="setup, compile.main" /> <target name="compile.main" depends="setup"> <mkdir dir="${classes.main}" /> <javac srcdir="${src.main}" destdir="${classes.main}"> <classpath refid="classpath.lib" /> </javac> </target> <target name="compile.tests" depends="setup"> <mkdir dir="${classes.test}" /> <javac srcdir="${src.test}" destdir="${classes.test}" classpathref="classpath.lib" /> </target> <target name="test" depends="compile"> <delete dir="${reports.junit.data}" failonerror="false" /> <mkdir dir="${reports.junit.data}" /> <junit printsummary="yes" haltonfailure="no" failureproperty="tests.failed"> <classpath refid="classpath.lib" /> <formatter type="xml" /> <batchtest fork="yes" todir="${reports.junit.data}" failureproperty="tests.failed"> <fileset dir="${src.test}"> <include name="**/*Test*.java" /> <exclude name="**/AllTests.java" /> </fileset> </batchtest> </junit> <fail if="tests.failed" message="Some unit tests failed" /> </target> <target name="jar" depends="compile, test"> <mkdir dir="${dist}" /> <jar destfile="${dist}/framework.jar" basedir="${classes.main}" /> </target> <target name="all" depends="jar" /> </project>配置CruiseControl config.xml 下面以一个简单的config.xml文件为例,说明CruiseControl的配置方法。配置文件config.xml位于C:/BuildServer/cruisecontrol-2.2/main/bin目录下,和启动脚本cruisecontrol.bat放在一起。
<?xml version="1.0"?> <cruisecontrol> <project name="frameworkProject"> <dateformat format="yyyy/MM/dd HH:mm:ss" /> <bootstrappers> <currentbuildstatusbootstrapper file="../logs/currentbuild.txt" /> </bootstrappers> <modificationset quietperiod="60" > <cvs LocalWorkingCopy="../../../workingCopy/frameworkProject/src"/> </modificationset> <schedule interval="3600" > <ant antWorkingDir="../../../workingCopy/frameworkProject" buildfile="build-cc.xml" /> </schedule> <log dir="../logs/frameworkProject"> <merge dir="../../../workingCopy/frameworkProject/reports/junit/data"/> </log> <publishers> <currentbuildstatuspublisher file="../logs/currentbuild.txt" /> <artifactspublisher dir="../../../workingCopy/frameworkProject/dist" dest="../logs/frameworkProject" /> <htmlemail mailhost="talenttech.com.cn" returnaddress="buildmaster@talenttech.com.cn" subjectprefix="[Talent Build Server]"//自己替换[] buildresultsurl="http://10.75.140.128:9000/cruisecontrol/buildresults/ ;frameworkProject" logdir="C:/BuildServer/cruisecontrol-2.2/main/logs/frameworkProject" xsldir="C:/BuildServer/cruisecontrol-2.2/reporting/jsp/xsl" css="C:/BuildServer/cruisecontrol-2.2/reporting/jsp/css/cruisecontrol.css"> <failure address="liutao@talenttech.com.cn" /> <success address="liutao@talenttech.com.cn" /> <success address="wangkai@talenttech.com.cn" /> <success address="wangchuang@talenttech.com.cn" /> <success address="wu.gaofeng@126.com" /> </htmlemail> </publishers> </project> </cruisecontrol><cruisecontrol> <cruisecontrol>是配置文件的根元素,它可以拥有一个或多个<project>子元素。本例中它拥有一个项目名为frameworkProject。 <project> <project>元素是一个完整的build任务,包括检查配置管理库是否有新的修改,构建项目并发布项目构建结果。它告诉CruiseControl构建什么,何时构建,如何构建以及如何发布构建报告。它有一个必需的属性name和一个可选属性buildafterfailed。 属性buildafterfailed定义了当构建失败时,是否要继续进行,缺省是"true"。 <project>元素的子元素包括,<bootstrappers>,<modificationset>,<schedule>,<log>,<publishers>,<dateformat>和<plugin>,其中<modificationset>和<schedule>是必需的元素。 <dateformat> <dateformat>用于定义日期的格式,缺省格式是:MM/dd/yyyy HH:mm:ss。 <bootstrappers> <bootstrappers>元素是启动任务Plugin的容器,用于定义构建任务启动前需要执行的任务。常用的Plugin包括: 1. <currentbuildstatusbootstrapper>,定义一个CruiseControl的构建状态信息文件。CruiseControl的Build Result JSP从该文件读取状态信息并显示在页面上。属性file用于指定构建状态文件目录和文件名。 2. <cvsbootstrapper>,用于在项目构建开始前从CVS服务器上update指定的文件。通常可以用于更新项目的构建脚本。属性localWorkingCopy指定CVS本地工作目录,属性file指定需要update的文件名,相对于属性localWorkingCopy指定的目录。 <modificationset> <modificationset>元素用于告诉CruiseControl是否需要构建项目,即配置管理库的代码是否存在更新。它拥有两个可选属性requiremodification和quietperiod。 属性requiremodification告诉CruiseControl,在配置管理库没有代码更新的情况下,是否需要构建。缺省为"true",即没有更新则无须进行构建。 属性quietperiod告诉CruiseControl,最新一次代码提交后CruiseControl需要等待的时间(秒)。用于防止CruiseControl在开发人员提交代码时进行项目构建。缺省为"60"秒。 在本例中使用<cvs>来检查和工作目录相关的代码在CVS配置管理库是否有更新。<cvs>使用"cvs log"命令来检查最新更新工作目录和当前代码库的差异。 <schedule> 到目前为止,以上的配置文件内容已经定义了CruiseControl构建什么以及何时构建。<schedule>元素告诉CruiseControl每隔多长时间(秒)启动一次构建任务。它有一个可选的属性interval,用于定义以秒为单位的时间间隔。缺省为"300"秒。 在本例中,属性interval设为"3600",这意味着CruiseControl每隔一个小时使用<modificationset>定义的任务检查一次代码库。 <schedule>元素拥有三个子元素<ant>,<maven>和<parse>。 <ant>子元素告诉CruiseControl何时或每隔几次运行Ant来构建项目。 在本例中,antWorkingDir属性设定Ant的工作目录,buildfile属性设定构建脚本build file的目录。 属性multiple告诉CruiseControl每隔几次执行一次本<ant>任务。 除此之外,还可以指定Ant的运行时间(time属性),build file的target(target属性,不设定则为build file的缺省target)。 请参见CruiseControl的配置文档(位于${CruiseControl_Home}/main/docs目录下)。 <log> <log>元素设定CruiseControl日志文件的存放目录,并通过<merge>子元素指定合并什么样的XML文件(构建过程中产生的文件)到CruiseControl的日志文件中。 <merge>子元素的pattern属性定义匹配的文件名模式,缺省为".xml";dir属性用于指定一个目录,这个目录下所有匹配模式的文件将合并到CruiseControl的日志文件中。 <publishers> <publishers>元素用于指定构建任务结束后,CruiseControl如何发布项目构建结果。项目构建结果的发布方式可以是Email,网页,复制代码库到指定的目录,或是发布代码库到FTP服务器。 在本例中,共有<currentbuildstatuspublisher>,<artifactspublisher>和<htmlemail>三个publisher。 <currentbuildstatuspublisher> publisher把下次构建的时间写入指定文件,文件名由file属性设定。 <artifactspublisher> publisher元素把项目构建产品复制到指定的目录,dir属性定义源目录,dest定义目标目录的父目录(实际目录还要加上构建时的时间戳,如:父目录/19890604203828)。 <htmlemail> publisher把构建结果以HTML格式通过Email发布。缺省情况下,HTML格式的Emai和CruiseControl Web应用的构建结果JSP页面相同。 本例中<htmlemail>的属性和子元素的作用很容易理解,更多的配置项参见联机文档。 创建CruiseControl的Web应用 1. 在C:/BuildServer/cruisecontrol-2.2/reporting/jsp目录下运行build war命令; 2. 提示设置user.log.dir属性时,输入C:/BuildServer/cruisecontrol-2.2/main/logs; 3. 提示设置user.build.status.file属性时,输入currentbuild.txt; 4. 提示设置cruise.build.artifacts.dir属性时,输入/artifacts/frameworkProject; 5. Web应用构建完成后,可以在C:/BuildServer/cruisecontrol-2.2/reporting/jsp/dis目录下找到cruisecontrol.war文件; 6. 把cruisecontrol.war复制到tomcat的webapps目录下,发布CruiseControl的Web应用; 启动CruiseControl 使用C:/BuildServer/cruisecontrol-2.2/main/bin目录下cruisecontrol.bat启动CruiseControl。用法如下: C:/BuildServer/cruisecontrol-2.2/main/bin>cruisecontrol [options] 命令cruisecontrol的选项包括: -port [number] JMX服务器的Http Controller的端口;缺省为8000 -rmiport [number] JMX服务器的RMI Controller的端口;缺省为1099 -xslpath directory JMX的XSL文件存放目录; -configfile file 配置文件的路径;缺省为当前目录下的config.xml文件 -debug 将CruiseControl内部的日志级别调整到DEBUG 只有指定port和/或rmiport属性时,JMX服务器才启动。如果要修改port参数(不使用缺省的8000端口),必须要修改reporting/jsp目录下的controlpanel.jsp文件,然后再重新创建和发布CruiseControl的Web应用;也可以直接修改Tomcat/webapps/cruisecontrol目录下的controlpanel.jsp页面。 CruiseControl的Web界面 在浏览器地址栏输入:http://BuildServer-IP:9000/cruisecontrol/,可以访问CruiseControl的Web应用,如下图所示: 在左侧区域,按时间顺序列出最新Build结果的连接。如果Build结果超过10个,左侧区域只显示10个连接,其余的Build结果可以在下面的下拉框中找到。上图中因为Build结果少于10个,所以下拉框是空的。 当点击右上角的Control Panel按钮时,如果出现错误,则是因为没有使用CruiseControl的JMX支持。要启动JMX支持,请看下节内容。 使用JMX控制台 要使用CruiseControl的JMX控制台很简单,只需在启动cruisecontrol时指定JMX Server的Http Controller端口即可:cruisecontrol -port 8000。 进入CruiseControl的Web应用界面,点击右上角的Control Panel按钮,则出现CruiseControl的"JMX Control Panel"。 可以通过JMX控制面板在运行时修改配置,而不需要重启CruiseControl。点击CruiseControl Project MBean,尝试它的管理功能。 例如,可以修改MBean的"BuildInterval"属性,来更改项目构建的时间间隔;修改ConfigFileName,则可以更改CruiseControl的配置文件;点击MBean的build操作"invoke"按钮,则可以强制启动项目构建任务。 持续集成资源 1. 持续集成:http://www.martinfowler.com/articles/continuousIntegration.html 2. AntHill:http://www.urbancode.com/projects/anthill/ 3. CruiseControl:http://cruisecontrol.sourceforge.net/ 4. CVSNT:http://www.cvsnt.org/ 5. WinCVS中文版:http://www.8848software.com/wincvs/ 6. Driving On CruiseControl Part1: http://www.javaranch.com/journal/200409/DrivingOnCruiseControl_Part1.html 7. Driving On CruiseControl Part2: http://www.javaranch.com/journal/200410/DrivingOnCruiseControl_Part2.html 8. Scheduled Builds:http://www.pragmaticprogrammer.com/starter_kit/au/scheduled.pdf Java代码规范检查工具很多,CheckStyle是其中最有名的,它的Eclipse插件,使用非常方便。我在最近的项目中,之所以选择JCSC,是因为JCSC不但可以检查代码规范,而且给出了NCSS(Non Commenting Source Statements)和CCN(Cyclomatic Complexity Number),前者近似等于Java的有效代码行,后者则用于评价类方法的复杂度。 下面的安装配置延续我的Blog,CruiseControl:持续集成工具的例子。 安装JCSC 1、从SourceForge下载JCSC.zip 2、将JCSC.zip解压到D:/BuildServer目录 3、添加JCSC_HOME系统环境变量,本例中为D:/BuildServer/jcsc 4、添加JCSC_HOME/bin到系统路径 配置ANT 1、把JCSC_HOME/jcsc/lib的jar文件复制到/ANT_HOME/lib 配置CruiseControl 1、把JCSC_HOME/lib的jar文件复制到DEFAULT_CCDIR/main/lib 2、修改cruisecontrol.bat,在cruisecontrol.jar之前添加%LIBDIR%/JCSC.jar,在类路径后面添加%LIBDIR%/xercesImpl.jar 3、复制/JCSC_HOME/html/xml/xsl/cruisecontrol/jcsc.xsl文件到TOMCAT_HOME//webapps/cruisecontrol/xsl目录 4、修改TOMCAT_HOME//webapps/cruisecontrol/xsl/jcsc.xsl文件,把JCSC Details的链接改成"/cruisecontrol/jcsc/index.html" 5、在TOMCAT_HOME/webapps/cruisecontrol/buildresults.jsp页面中添加 6、在webapps/cruisecontrol目录下创建jcsc目录 7、复制JCSC_HOME/html/xml目录下的xsl子目录和index.html文件到webapps/cruisecontrol/jcsc目录 8、复制JCSC_HOME/html/web/jcsc-result/rules.jcsc.xml文件到webapps/cruisecontrol/jcsc目录 9、把JCSC生成的overview.xml合并到CruiseControl的log.xml文件。修改CruiseControl的config.xml文件,如 <log dir="../logs/frameworkProject"> <merge dir="../../../workingCopy/frameworkProject/reports/junit/data" /> <merge file="D:/BuildServer/tomcat-4.1.31/webapps/cruisecontrol/jcsc/overview.xml" /> </log> 修改ANT的构建脚本build.xml 1、添加JCSC属性
<!-- jcsc home config --> <property name="jcsc.home" value="D:/BuildServer/jcsc" /> <property name="jcsc.log.dir" value="D:/BuildServer/tomcat-4.1.31/webapps/cruisecontrol/jcsc" /> <property name="jcsc.rules.dir" value="D:/BuildServer/tomcat-4.1.31/webapps/cruisecontrol/jcsc/rules.jcsc.xml" />2、定义JCSC的ANT Task
<taskdef name="jcsc" classname="rj.tools.jcsc.ant.JCSCTask" />3、<target name="clean">中添加删除JCSC日志文件的任务
<delete failonerror="false"> <fileset dir="${jcsc.log.dir}" includes="*.xml" excludes="xsl,rules.jcsc.xml,index.html" /></delete> 4、添加JCSC Target
<target name="jcsc"> <jcsc rules="${jcsc.rules.dir}" destdir="${jcsc.log.dir}"> <fileset dir="${src.main}" includes="**/*.java" /> </jcsc> </target>5、调用JCSC Task
<target name="all" depends="clean, jar, test, coverage.report, jcsc" />使用Rules Editor修改Rules文件 1、运行JCSC_HOME/bin/ruleseditor.bat命令,使用Rules Editor编辑Rules定义文件,可以编辑Open Brace'{' On New Line,Class/I-Face Header等选项 2、也可以直接修改rules.jcsc.xml文件 集成在CruiseControl的JCSC报告如下图所示。 在htmlmail邮件中集成JCSC报告 要在CruiseControl的htmlmail邮件中集成JCSC报告,需要修改CruiseControl的代码。在HTMLEmailPublisher类的String[] xslFileNames变量中加入jcsc.xsl,即可在htmlmail中包含JCSC报告的概要部分。