通过SVN自动更新程序集版本信息
时间:2010-09-16 来源:neutra
VS可以指定[assembly: AssemblyVersion("1.0.*")]这样的版本号,每次编译程序集时自动递增Build和Revision。今天同事说到这个自动递增的Revision对于查错作用不大,建议用SVN版本号作为程序集的版本的尾号。我觉得很有道理,于是在上网里查了下方法,比较遗憾的是只查到一篇文章(雷同不算)《通过 TSVN 自动更新程序集版本信息》。
文章里介绍了方法比较复杂,大致如下:
1. 做一个包含[assembly: AssemblyVersion()]的模板
2. 写一个批处理脚本。脚本通过查找注册表定位到TortoiseSVN里的一个小工具SubWCRev.exe的路径,然后调用这个小工具对模板进行操作
AutoRevision.bat::<code>
:: <revsion>$Rev: 4 $</revision>
:: <owner name="Zealic" mail="[email protected]" />
::</code>
::-----------------------------
::<summary>
:: 根据指定的 SVN 工作目录的信息和模板生成目标文件。
::</summary>
::<param name="workDir">要获取信息的 SVN 工作目录。</param>
::<param name="template">模板文件路径。</param>
::<param name="target">生成的目标文件路径。</param>
::<returns>装载量</returns>
::<remark>
:: 当生成项目时 Visual Studio 如果已经打开 %target% 参数指向的文件,那么可能导致模板替换更新不精确。
:: 导致该问题的原因是:SubWCRev.exe 修改了 %target%,但是 VS 检测到了这一情况,会弹出对话框询问用户。
:: 必须重新加载才可以,否则 VS 将会编译未替换过的 %target% 文件。
::</remark>
::=============================
::-----------------------------
::** Initialize
@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
REM Initialize Constants
SET TSVN_INFO_FILE=.\TSVN_INFO.tmp
REM Initialize script arguments
SET workDir=%1
SET template=%2
SET target=%3
REM Goto main entry
GOTO MAIN
::=============================
::-----------------------------
::** Main entry
:MAIN
pushd %workDir%
SET workDir=.\
REM 检查参数
IF %workDir%=="" GOTO ARGUMENT_ERROR
IF %template%=="" GOTO ARGUMENT_ERROR
IF %target%=="" GOTO ARGUMENT_ERROR
REM 查询注册表
reg query HKLM\SOFTWARE\TortoiseSVN /v Directory > %TSVN_INFO_FILE% 2>NUL
REM 查找 TSVN 路径
FOR /F "tokens=*" %%i IN (%TSVN_INFO_FILE%) DO (
ECHO %%i | find "Directory REG_SZ" > NUL
IF %ERRORLEVEL% == 0 (
ECHO %%i > %TSVN_INFO_FILE%
)
)
SET /P TSVN_PATH= < %TSVN_INFO_FILE%
SET TSVN_PATH=%TSVN_PATH:~23,-1%
REM 调用 TSVN 替换模板
IF NOT %ERRORLEVEL% == 0 GOTO UNKNOW_ERROR
"%TSVN_PATH%bin\SubWCRev.exe" %workDir% %template% %target%
IF NOT %ERRORLEVEL% == 0 GOTO UNKNOW_ERROR
GOTO SUCESSED
::=============================
::-----------------------------
::** Error handlers
:ARGUMENT_ERROR
ECHO 传入的参数无效。
GOTO FAIL
:NOT_FOUND_TSVN
ECHO 查询TortoiseSVN 的安装信息失败。
GOTO FAIL
:UNKNOW_ERROR
ECHO 未知错误。
:FAIL
::=============================
::-----------------------------
::** Program exit
:FAIL
DEL /Q %TSVN_INFO_FILE% 2>NUL
ECHO 模板替换失败。
popd
EXIT 1
:SUCESSED
DEL /Q %TSVN_INFO_FILE% 2>NUL
ECHO 成功进行了模板替换。
popd
EXIT 0
::=============================
3. 在项目预生成事件中调用该批处理脚本,传入项目路径、模板路径和输出路径
4. 将输出的文件包含在项目中,并在SVN中排除对该文件的管理
我很赞同这个思路,仔细研究了,觉得通过注册表来查找SubWCRev.exe的路径有点太费劲。印象中这种工具类的小程序一般都可以直接复制出来单独使用的,就像微软的.Net Framework SDK自带的那些小工具一样。于是我试着将这个SubWCRev.exe复制到D盘,然后命令行尝试执行下,发现真的可以执行。于是可以改进这个方法,直接在预生成事件里调用包含在解决方案中的SubWCRev.exe。
我给自己的项目写了个模板(加了个#warning,如果当前编译时有代码有修改就产生一个警告):

//------------------------------------------------------------------------------
// <auto-generated>
// Assembly Version
// 此代码由工具生成。
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
[assembly: AssemblyCompany("my corp")]
[assembly: AssemblyProduct("my product")]
[assembly: AssemblyCopyright("2010")]
[assembly: AssemblyVersion(AssemblyRevision.FullVersion)]
[assembly: AssemblyFileVersion(AssemblyRevision.FullVersion)]
[assembly: NeutralResourcesLanguage("zh-CN")]
internal static class AssemblyRevision
{
public const string Major = "1";
public const string Minor = "0";
public const string Build = "0";
public const string Revision = "$WCREV$";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
public const string BuildTime = "$WCNOW$";
public const string RevisionRange = "$WCRANGE$";
public const bool RevisionMixed = $WCMIXED?true:false$;
public const bool RevisionModified = $WCMODS?true:false$;
$WCMODS?#warning Revision modified:$
}
然后写了个简单的批处理GenerateRevision.bat,目的是减少项目生成事件那边调用时写得代码量不用太长,%~dp0代表批处理所处的目录路径
@"%~dp0SubWCRev.exe" %1 "%~dp0AssemblyRevision.cs" %2
将这AssemblyRevision.cs、GenerateRevision.bat和SubWCRev.exe一起放在解决方案目录下的Tools目录下,并添加到源代码管理。然后对解决方案里的每个项目进行以下操作:将模板里指定过的Assembly级Attribute去掉(通常在Properties\AssemblyInfo.cs里),在项目预生成事件里加入:
"$(SolutionDir)Tools\GenerateRevision.bat" "$(ProjectDir)." "$(ProjectDir).AssemblyRevision.cs"
重新生成项目,将各项目目录生成的.AssemblyRevision.cs添加到项目中(前面加个点是为了排序时可以靠前,比较醒目……),并添加到忽略列表(不然每次编译SVN发现该文件变化了又提示有文件已修改)。再重新编译一次就行了,以后每次编译都会自动将SVN的版本设为程序集的Revision。