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}" failοnerrοr="false" />
<delete dir="${reports}" failοnerrοr="false" />
<delete dir="${dist}" failοnerrοr="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}" failοnerrοr="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="[email protected]"
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="[email protected]" />
<success address="[email protected]" />
<success address="[email protected]" />
<success address="[email protected]" />
<success address="[email protected]" />
</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 failοnerrοr="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報告的概要部分。