MsBuild 4.0 には結構良い新機能が入っております。
ここんとこツイッターでモニョモニョ言ってる MsBuild インラインタスクとか。
MsBuild ターゲット
- RunBeforeTargets および RunAfterTargets (MSBuild 4.0)
(原文ママ、スキーマ見た感じでは BeforeTargets / AfterTargets が正しい)
これを使って TFS ビルド中にバージョンを埋めるタスクを作りましたのでおすそ分け。
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask
TaskName="CreateAssemblyVersionSource"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<Path ParameterType="System.String" Required="true" />
<VersionAndRevision ParameterType="System.String" Required="true" />
<BuildUri ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string buildNumber = new Uri( BuildUri ).Segments.Last();
Log.LogMessage( "CreateAssemblyVersionSource Path: {0}",Path );
StringBuilder sb = new StringBuilder();
sb.AppendLine( "using System;" );
sb.AppendLine( "using System.Reflection;" );
sb.AppendLine( "using System.Runtime.InteropServices;" );
sb.AppendLine( "using System.Resources;" );
sb.AppendFormat( "[assembly: AssemblyVersion(\"{0}.{1}\")]" ,VersionAndRevision, buildNumber ).AppendLine();
sb.AppendFormat( "[assembly: AssemblyFileVersion(\"{0}.{1}\")]" ,VersionAndRevision,buildNumber ).AppendLine();
File.WriteAllText( Path, sb.ToString() );
]]>
</Code>
</Task>
</UsingTask>
<Target Name="LabelAssembly" Condition="'$(BuildUri)'!=''"
AfterTargets="PreBuildEvent" BeforeTargets="CoreCompile" >
<Message Text="LabelAssembly target running" />
<CreateAssemblyVersionSource
Path="AssemblyVersion.cs"
VersionAndRevision="$(VersionAndRevision)"
BuildUri="$(BuildUri)" />
<ItemGroup>
<Compile Include="AssemblyVersion.cs" />
</ItemGroup>
</Target>
</Project>
やっている事としては結構単純で、 File.WriteAllText で cs ファイルを吐きだす事の AssemblyVersion / AssemblyFileVersion アセンブリ属性を。
それを Compile ItemGroup にInclueしてるだけです。
TFSビルド時には BuildUri にビルドの識別Uriがきますので、これがある時だけターゲットを定義するように Condition しており。
AfterTargets/BeforeTargets を使って CoreCompile ターゲットの前に実行される様に設定しています。
AfterTargets/BeforeTargetsによって関連するターゲットには何も変更を入れる必要がなくターゲットを差し込む事ができるというのが素晴らしい所でございます。
<PropertyGroup>
<VersionAndRevision>1.0.0</VersionAndRevision>
</PropertyGroup>
<Import Project=”ターゲットを保存した場所”/>
をcsprojの末尾でやるとTFSでのビルド番号がバージョンに反映されます。
このインラインタスクでやってる StringBuilder での編集をT4でPreprocessorしたテンプレートによって ITask実装しておくと、ビルドシーケンス中でのコード生成はもはやお手の物って訳で、組み合わせ次第で夢が広がりますねーなお話。
MSの中の人には MsBuild 4.0 関連の情報を MSDN Library のメニューにマージして頂きたいとか…。