由於面向接口編程的關系,許多實現往往是動態注入運行,在一個項目中直接引用實現dll編譯是不合理的。通常我們會在Post Build Event中添加一些xcopy命令將運行時才需要的dll復制到輸出目錄。在發布時會帶來一些問題,比如:使用Visual Studio自帶的Publish功能發布一個Web應用時就不會運行Post Build Event。同樣的在基於TFS Build時也存在類似問題。
TFS Build時會根據對應Definition的名稱創建兩個子目錄:Source、Binaries,Binaries下針對Web應用會創建發布目錄"_PublishedWebsites",如果想要Post Build Event時將dll復制到對應目錄下,最簡單的方式就是再添加xcopy命令(這樣的命令可能有多條),例如:
xcopy /y /i "$(SolutionDir)BuildEvents\Post-build" "$(TargetDir)_PublishedWebsites\$(TargetName)\bin\"
但是會給開發人員帶來歧義、工作量並污染Post Build Event。其實我們想做的就是當MSBuild時的某一個參數值符合要求則執行發布用的命令(這個命令和Post Build Event執行的內容一樣,就是$(TargetDir)不同),這時就需要修改項目文件來添加一些自定義的腳本實現該功能。首先在Build Definition的高級選項里添加MSBuild Arguments,例如:
/p:RMS=1
由於我的目標是和Release Management Service整合,故使用了該縮寫。下面打開項目文件(csproj),開啟AfterBuild,並添加一個Exec Task
<Target Name="AfterBuild"> <Exec Command="$(PostBuildEvent)" Condition=" '$(RMS)' == '1' " /> </Target>
上面的命令實際無法達到最終效果,這里自己繞了一個彎路。主要問題在於對PostBuildEvent的理解,其實它也是一個字符串變量,在執行時它內部使用的$(TargetDir)早已被替換,無法再重新計算結果。例如:
<Target Name="AfterBuild" Condition=" '$(RMS)' == '1' "> <PropertyGroup> <TargetDir>$(TargetDir)_PublishedWebsites\$(TargetName)\bin\</TargetDir> </PropertyGroup> <Message Text="$(TargetDir)" /> <Exec Command="$(PostBuildEvent)" /> </Target>
TargetDir的值確實產生變化,但PostBuildEvent的值也已經被提前計算,我們無法再讓它被動態計算一次。幸好MSBuild 4.0以上版本允許我們使用一部分.NET代碼來修改這些變量,我們只需調用System.String的Replace方法即可,參考如下:
<Target Name="AfterBuild"> <Exec Command="$(PostBuildEvent.Replace("$(TargetDir)", "$(TargetDir)_PublishedWebsites\$(TargetName)\bin\"))" Condition=" '$(RMS)' == '1' " /> </Target>
通過上面的方法就可以將Web應用完整發布,並結合Release Management Service實現持續集成。