StatCVS 提供了對 CVS 儲存庫活動(dòng)的深入觀(guān)察
15 May, 2005 - 02:06, in
Java.Technology如果要接手一個(gè)已經(jīng)運行了好幾年的軟件項目,那么怎樣才能得到對項目開(kāi)發(fā)歷史的認識呢?最好的方法可能就是與曾經(jīng)參與該項目的開(kāi)發(fā)人員對話(huà),但是這說(shuō)起來(lái)容易,做起來(lái)卻很困難。原有的開(kāi)發(fā)人員通常都已轉到了其他項目中,而且要找到他們也很難。您可以查看釋出頻率(release frequency),盡管這可能受非技術(shù)性的強制規定控制(有時(shí)可能只是出于“我們要在本財政年度末做一個(gè)發(fā)布”的理由)。您可以查看 bug 和特性請求跟蹤器,還可以在開(kāi)放和關(guān)閉的 bug 討論中挖掘信息?;蛘呖梢灾苯舆M(jìn)入源代碼歷史記錄,用 StatCVS 這樣的工具查看曾做過(guò)哪些記錄,這些記錄是誰(shuí)修改的。我將 StatCVS 運用在各種大型項目上已經(jīng)有好幾年的時(shí)間了,它生成的報告一直都很不錯。在本文中,將演示如何在項目上設置、運行 StatCVS,如何閱讀它生成的報告,以及 StatCVS 需要改進(jìn)的地方。
StatCVS 是一個(gè)
Java 程序,需要
JDK 1.4 或更高版本的支持。從命令行安裝 StatCVS 最容易:只要下載最新的發(fā)行版,將它解壓到一個(gè)目錄中即可;我用的是 /usr/local/statcvs/ 目錄。而且,如清單 1 所示,我還創(chuàng )建了一個(gè)符號鏈接,叫作 statcvs,它鏈接到剛剛安裝的版本上。這可以節約日后的一些打字工作時(shí)間,更重要的是,日后只要把符號鏈接修改為指向要使用的版本,就可以在 StatCVS 的不同版本間切換。
[root@hal local]# pwd
/usr/local
[root@hal local]# ln -s statcvs-0.2.2 statcvs
[root@hal local]# ls -l | grep statcvs
lrwxrwxrwx 1 root root 13 Jan 13 14:27 statcvs -> statcvs-0.2.2
drwxrwxr-x 2 root root 4096 Oct 13 23:32 statcvs-0.2.2
-rw-rw-r-- 1 tom tom 1344753 Jan 13 13:49 statcvs-0.2.2.zip
如果列出 statcvs 目錄中的文件,就可以看到那里沒(méi)有任何適用于 StatCVS 的支持 JAR 文件(supporting JAR file)。惟一的 JAR 文件是 statcvs.jar,它包含 StatCVS 使用的惟一的第三方庫:JFreeChart。這種方法使得開(kāi)始了解 StatCVS 變得更容易,因為不需要關(guān)于類(lèi)路徑的更多知識。
為了演示 StatCVS 的工作方式,需要找到一個(gè)帶有有趣的 CVS 歷史記錄的項目,并生成一些活動(dòng)報告。developerWorks 的項目 Jikes 已經(jīng)進(jìn)行了一段時(shí)間,有大量開(kāi)發(fā)人員,還有一個(gè)公共的 CVS 儲存庫,所以它是一個(gè)好例子。
為了從 StatCVS 得到 Jikes 的報告,需要得到最新的源代碼,并生成一個(gè) CVS 日志文件讓 StatCVS 分析。所以,需要從 Jikes 的 CVS 儲存庫簽出它的源代碼。Jikes 的開(kāi)發(fā)人員允許擁有只讀權限的匿名用戶(hù)對其儲存庫進(jìn)行訪(fǎng)問(wèn),所以可以用這個(gè)方法得到源代碼,如清單 2 所示:
[tom@hal tmp]$ cvs -d:pserver:anoncvs@www-124.ibm.com:/usr/cvs/jikes login
Logging in to :pserver:anoncvs@www-124.ibm.com:2401/usr/cvs/jikes
CVS password: [ Type "anoncvs" here ]
[tom@hal tmp]$ cvs -d:pserver:anoncvs@www-124.ibm.com:/usr/cvs/jikes co jikes
cvs server: Updating jikes
U jikes/.cvsignore
... several thousand lines elided ...
現在機器上已經(jīng)有了 Jikes 代碼,需要創(chuàng )建一個(gè) CVS 日志文件供 StatCVS 處理。要創(chuàng )建這個(gè)文件,需要進(jìn)入 jikes 目錄,運行 cvs log 命令。正如從清單 3 中看到的,我把命令的輸出重定向到了一個(gè)叫作 logfile.txt 的文件:
[tom@hal tmp]$ cd jikes/
[tom@hal jikes]$ time cvs -d:pserver:anoncvs@www-124.ibm.com:/usr/cvs/jikes log > logfile.txt
real 0m40.719s
user 0m0.516s
sys 0m0.314s
[tom@hal jikes]$
只是為了好玩,我對此進(jìn)行了計時(shí)。在我的工作站上,這大約花費了 40 秒的時(shí)間,生成的日志文件大小約為 3.3 MB。
現在可以運行 StatCVS 生成報告了??梢詮拿钚羞\行 StatCVS,也可以從
Ant 運行。首先來(lái)看一下命令行界面,然后再來(lái)討論 Ant。
StatCVS 從命令行運行很容易,因為只有一個(gè) JAR 文件,而且可以把 JAR 文件名直接傳給虛擬機??梢杂貌煌倪x項控制輸出。這里是一些比較有用的選項:
-title [標題] —— 放在報告上的顯示標題。
-output-dir [目錄](méi) —— 報告文件存放的位置;如果目錄不存在,則自動(dòng)創(chuàng )建該目錄。
-include [模式] —— 只包含與指定模式匹配的文件。
-viewcvs [ViewCVS url] —— 儲存庫的 ViewCVS Web 界面的 URL。
下面用以上選項創(chuàng )建報告。首先,必須移動(dòng)到 jikes/ 目錄上,然后從命令行運行 StatCVS,如清單 4 所示:
[tom@hal tmp]$ time java -jar /usr/local/statcvs/statcvs.jar \
-include "cpp;**/*.h" \
-output-dir report \
-title "Jikes" \
-viewcvs http://www-124.ibm.com/developerworks/oss/cvs/jikes/jikes/ jikes/logfile.txt jikes/
StatCVS - CVS statistics generation
real 0m15.232s
user 0m12.014s
sys 0m0.326s
[tom@hal tmp]$
注意,上面使用了 -include 參數,只捕獲
C++ 源代碼文件和頭文件。在 CVS 模塊中有許多其他文件(文檔、配置腳本、報告、Web 頁(yè)面等),但是本文只關(guān)心源代碼。
清單 5 顯示了與清單 4 的命令行調用功能相同的 Ant 任務(wù)定義:
<?xml version="1.0"?>
<project name="StatCvsAnt" default="main" basedir=".">
<taskdef name="statcvs" classname="net.sf.statcvs.ant.StatCvsTask"/>
<target name="main">
<statcvs
projectDirectory="jikes"
cvsLogFile="jikes/logfile.txt"
outputDirectory="report"
title="Jikes"
viewcvsURL="http://www-124.ibm.com/developerworks/oss/cvs/jikes/jikes/"
includeFiles="**/*.cpp;**/*.h"/>
</target>
</project>
報告放在清單 4 指定的報告目錄中。如果用瀏覽器打開(kāi)該目錄中的 index.html 頁(yè)面,該頁(yè)面如圖 1 所示:
可以看到可用報告的分類(lèi):關(guān)于代碼作者的一些統計數據、查看提交日志、代碼段的行,以及關(guān)于文件和目錄大小的一些統計。
代碼行圖表如清單 2 所示,它可能非常有趣:
從這個(gè)圖表中可以看出,代碼最初是在 1999 年初導入的。從那以后,它增長(cháng)得非常穩定,一直到 2001 底,那時(shí)代碼的數量開(kāi)始略有下降。還有幾次,新的代碼被引入或者舊的代碼被重構出去,這些可以從代碼行計數的急劇升降上表現出來(lái)。從 2004 年開(kāi)始,似乎沒(méi)有加入太多代碼,這可能表明 Jikes 已經(jīng)成熟到了某種程度,主要對它做些維護工作即可。
如果從主報告頁(yè)上單擊 Authors 鏈接,就可以看到數字和圖表,它們指出每個(gè)參與者貢獻了多少代碼,如圖 3 所示:
很明顯,ericb 和 shields 對 lion 負責的代碼有所貢獻,而其他參與者也偶爾參與其中。注意,沒(méi)有任何一個(gè)參與者從頭到尾都參與了該項目。這個(gè)事實(shí)清楚地證明:長(cháng)期項目需要那些擁有良好變量名和干凈設計的清晰代碼。
偶爾,StatCVS 在生成報告時(shí)表現得更聰明。如果 CVS 儲存庫只有一個(gè)參與者,那么主報告頁(yè)上的鏈接只會(huì )寫(xiě)上“Author page for joe_smith”,而且不會(huì )生成比較圖表。這樣 StatCVS 會(huì )運行得更快,報告頁(yè)也會(huì )更整潔。
現在再來(lái)看一個(gè)參與者的活動(dòng)圖表。在主報告頁(yè)面上,單擊 Authors 鏈接可以訪(fǎng)問(wèn) Author Activity,如圖 4 所示,圖中顯示了每個(gè)參與者是添加了文件,還是修改了文件:
您可以看到,shields 添加了大多數代碼,這在預料之中,因為這個(gè)人顯然是代碼導入 CVS 之后的第一個(gè)維護者。同樣,ericb 在項目啟動(dòng)之后幾年之間一直進(jìn)行類(lèi)似的工作,主要在修改文件。
提交日志(Commit Log)僅僅是對模塊做的全部修改的一個(gè)列表。這個(gè)報告顯示了誰(shuí)做了修改,以及提交者在所做修改上附加的注釋。而且,因為 Jikes 的儲存庫中有一個(gè) ViewCVS 界面,運行 StatCVS 時(shí)還使用了一個(gè) -viewcvs 參數,所以報告中包含了到已實(shí)際修改的源代碼的鏈接。例如,在 2004 年 12 月 12 日,src/decl.cpp 有一個(gè)改動(dòng)。如果點(diǎn)擊 decl.cpp,會(huì )看到添加了一個(gè) if 語(yǔ)句,還有一個(gè)注釋。圖 5 顯示了 ViewCVS 的一部分,展示了兩個(gè)文件版本之間的差異:
還有其他一些報告:一個(gè)報告顯示了平均文件大小,另外一個(gè)顯示了如何通過(guò)目錄樹(shù)分布代碼,還有一個(gè)則顯示了哪個(gè)文件的版本改動(dòng)最多。Jikes 的整個(gè)報告可以通過(guò)單擊本文頂部或底部的 代碼 圖標得到。只要將它解壓,并用瀏覽器打開(kāi) index.html,就可以看到這個(gè)報告。
前面已經(jīng)看到了如何在一個(gè) CVS 儲存庫上運行 StatCVS。但是,如果擁有多個(gè)儲存庫,那么您就會(huì )希望有一種方法能夠每天夜里為所有的儲存庫生成 StatCVS 報告。因為可以從命令行運行 StatCVS,所以這是一個(gè)用腳本就可以解決的簡(jiǎn)單問(wèn)題。以下是一些需要牢記的事項:
StatCVS 是一個(gè) Java 程序,所以需要大量?jì)却娌拍軉?dòng)。處理大型 CVS 儲存庫時(shí)也需要相當一段時(shí)間。所以如果運行它的機器還有其他用途,那么最好在處理不同的儲存庫之間讓機器休息一會(huì )。如果運行的系統支持優(yōu)先級設置,那么用低優(yōu)先級來(lái)運行耗時(shí)比較長(cháng)的任務(wù)是一個(gè)好主意。
如果定期將儲存庫添加到機器或從機器中刪除儲存庫,那么某些儲存庫可能不包含模塊。所以請事先檢測這種可能性,免得沒(méi)有必要地啟動(dòng) StatCVS。
清單 6 顯示了一個(gè)小小的 Ruby 腳本,可以在擁有公共父目錄的多個(gè)儲存庫上運行 StatCVS;在下載代碼中也有這個(gè)腳本:
#!/usr/local/bin/ruby
require ‘fileutils‘
HOME_DIR = "/tmp/"
CVS_DIR= "/path/to/my/cvs/"
BASE_OUTPUT_DIR = "/var/www/my-projects/"
DELAY = 5
Dir.chdir(HOME_DIR)
# get a list of all the repositories
Dir.new(CVS_DIR).entries.grep(/^[^.]/).each {|file|
# create a working directory
working_directory = "tmp_" + rand().to_s
Dir.mkdir(working_directory)
Dir.chdir(working_directory)
`cvs -d#{CVS_DIR}#{file} -Q co .`
FileUtils.rm_rf(%w{CVS CVSROOT})
# no need to run StatCVS if no modules exist yet
if !Dir.new(".").entries.grep(/^[^.]/).empty?
`cvs -d#{CVS_DIR}#{file} -Q log > log`
cmd = "/usr/java/java/bin/java "
cmd = cmd + "-jar /usr/local/statcvs/statcvs.jar "
cmd = cmd + "-output-dir #{output_directory} log ."
`#{cmd}`
FileUtils.rm("log", :force=>true)
end
# clean up and sleep for a bit to let things settle down
Dir.chdir(HOME_DIR)
FileUtils.rm_rf(working_directory)
sleep DELAY
}
因為 StatCVS 是一個(gè)開(kāi)源項目,所以您可以得到它的代碼。要得到 StatCVS 的代碼,請從 StatCVS 的頁(yè)面下載源代碼 zip 文件,或者從這個(gè) Web 站點(diǎn)上的 CVS 儲存庫簽出代碼。
這里是一些關(guān)鍵的統計數字:
4,463 行代碼,由 JavaNCSS 測量。 176 個(gè)
JUnit 測試。 一個(gè)很好的 Ant 構建文件,可以促進(jìn)定制構建的編輯。 一個(gè)好標志 —— PMD 在 StatCVS 中找不到未使用代碼的例子。
StatCVS 用 JFreeChart 來(lái)創(chuàng )建圖表和圖形。所有的圖表都用可移植網(wǎng)絡(luò )圖形(Portable Network Graphics - PNG)格式生成,大多數現代 Web 瀏覽器都支持這種格式。生成圖表的代碼被很好地封裝在 net.sf.statcvs.renderer 包中。
最大的限制可能是 StatCVS 不支持分支;它只能報告對每個(gè)模塊的 HEAD 所做的修改。所以,如果開(kāi)發(fā)團隊的應用模式是為產(chǎn)品的每個(gè)版本建立一個(gè)新分支,并且只提交到這個(gè)分支,那么 StatCVS 無(wú)法返回正確的結果。這個(gè)問(wèn)題曾在 StatCVS 的郵件列表上討論過(guò),但是看起來(lái)近期不會(huì )得到解決。但是,既然它是開(kāi)源的,誰(shuí)會(huì )知道以后會(huì )怎樣呢?
另一個(gè)限制是 StatCVS 只支持 CVS。隨著(zhù) Subversion 正在迅速贏(yíng)得 CVS 繼承人的地位,所以如果 StatCVS 能夠兩者都支持,那就太棒了。在 StatCVS 的郵件列表上已經(jīng)有了一些關(guān)于這點(diǎn)的討論,但是 Subversion 修改集的格式看來(lái)正是目前的障礙。
挖掘 CVS 儲存庫來(lái)查找使用信息,可能產(chǎn)生大量的數字和圖表。但是要認識這些數字的有用性,以及它們能夠為特定項目開(kāi)發(fā)提供什么樣的深入觀(guān)察,這些則要取決于您的判斷。在腦子里要有這樣一些概念,StatCVS 可以提供一些有趣的可視快照,讓您了解項目源代碼在其生命期間發(fā)生的事。
而且,StatCVS 也是開(kāi)源項目良好運行的一個(gè)優(yōu)秀模型。它的代碼整潔、構建過(guò)程簡(jiǎn)單、文檔清晰。如果您對于如何做好開(kāi)源項目有興趣,那么可以從了解 StatCVS 中學(xué)到許多東西。
您可以參閱本文在 developerWorks 全球站點(diǎn)上的
英文原文。
獲得
StatCVS 的最新發(fā)行版。請務(wù)必參閱
discussion of branch support 和
discussion of Subversion support。最后,獲得
latest StatCVS release 的源代碼。
Jikes 是 Java 程序的一個(gè)快速的開(kāi)源編譯器。Jikes 的 Web 站點(diǎn)中還包括
instructions on how to access its CVS repository。
學(xué)習更多有關(guān)
JFreeChart 的內容,它是 StatCVS 使用的圖形庫。
請下載
CVS 的最新發(fā)行版。其中還有大量的
文檔,
CVS 郵件列表 上的一些高手可能對您很有幫助。
不要錯過(guò)這個(gè)介紹
CVS client and the CVS server 用法的教程(developerWorks,2003 年 7 月)。
還應該參閱以下這篇文章,它介紹了
using Eclipse with CVS and Ant(developerWorks,2003 年 3 月)。
Open Source Development with CVS, Third Edition(Paraglyph Press,2003 年)是一個(gè)對所有 CVS 特性都有用的指南??梢杂?a target="_blank" >PDF format 免費得到它。
Ruby 是一個(gè)解釋型腳本語(yǔ)言,可以進(jìn)行迅速而容易的面向對象編程。它有許多處理文本文件和進(jìn)行系統管理任務(wù)的特性。它簡(jiǎn)單、直接、可擴展、可移植,還有友好的、活躍的
郵件列表。
在
RubyForge 上可以找到數百個(gè)開(kāi)源的 Ruby 項目,其中包括一些有助于 Java 開(kāi)發(fā)的項目。例如,
Ruby JDWP 是創(chuàng )建
Java Debug Wire Protocol 客戶(hù)機的 Ruby 實(shí)現的一個(gè)嘗試。
Michael Squillace 和 Barry Feigenbaum 在他們的文章
Take a shine to JRuby(developerWorks,2004 年 9 月)中討論了 Ruby 與 Java 代碼的交互。
ViewCVS 提供了 CVS 儲存庫的只讀
HTTP 界面。它會(huì )顯示文件版本之間的差異,顯示分支和標志,還支持語(yǔ)法著(zhù)色,使代碼更易閱讀。
JavaNCSS 計數 Java 程序中的代碼行數。它能提供正確的測量,因為它會(huì )跳過(guò)注釋和空白行。
JUnit 框架自動(dòng)運行單元測試。它提供了良好的
GUI ,當所有測試通過(guò)時(shí)顯示綠色進(jìn)度條。這使得“如果條是綠的,代碼就是干凈的”這句話(huà)用得越來(lái)越多。而且 developerWorks 還有一篇關(guān)于
用 Jython 構建 JUnit 測試包 的文章。
Apache Ant 是一個(gè)基于 Java 編程語(yǔ)言的構建工具,developerWorks 上有許多關(guān)于 Ant 的文章和教程,其中包括:
讓編譯和測試過(guò)程自動(dòng)化(2001 年 8 月)
Extending Ant to support interactive builds(2001 年 11 月)
Apache Ant 101: Make Java builds a snap(2003 年 12 月)
Maven 是為 Java 程序設計的項目管理和項目范圍的工具。它定義了標準的項目描述模型,可以自動(dòng)執行許多標準的項目任務(wù)。
請務(wù)必參閱
StatCVS-XML plug-in for Maven 的文檔。
Charles Chan 寫(xiě)了以下這篇有趣的文章,描述了
項目管理:Maven 讓事情變得簡(jiǎn)單(developerWorks,2003 年 4 月)。
PMD 是一個(gè) Java 源代碼的靜態(tài)源代碼分析工具,可以找出沒(méi)有使用的變量、空的 catch 塊、沒(méi)有必要的對象創(chuàng )建,等等。
Elliotte Harold 對 PMD 的世界進(jìn)行了思考,并撰寫(xiě)了這篇文章,解釋如何
用 PMD 鏟除 bug(developerWorks,2005 年 1 月)。
關(guān)于作者
Tom Copeland 從 TRS-80 Model III 上開(kāi)始編程,但是對這項技能的需求已經(jīng)衰退了,所以他現在主要編寫(xiě) Ruby 和 Java 代碼。他參與過(guò)各種開(kāi)源項目,其中包括 PMD 和 GForge,他還幫助管理 RubyForge,這是一個(gè)開(kāi)源的 Ruby 項目?jì)Υ鎺?。他和妻?Alina 有四個(gè)孩子(Maria、Tommy、Anna 和 Sarah),他們一起住在北佛吉尼亞。