At work, we have been deploying the latest build from the build server to the test server manually for a while. I know this is not smart but the fact is, it really doesn’t take that much time to copy and paste files from build server to the test server.
Nevertheless, one day I was finally sick of this dumb process and decided to roll up my sleeves to make the build server do this monkey job automatically. I wrote a MsBuild script, which automatically copies files to the test server from build server after finishing a build and the script below is the simplified version of it:
<PropertyGroup> <Root>$(MSBuildProjectDirectory)</Root> <SourceFolder>$(Root)\Source</SourceFolder> <OutputFolder>$(Root)\Output</OutputFolder> <DeployFolder>$(Root)\Deploy</DeployFolder> </PropertyGroup> <ItemGroup> <DllFiles Include="$(OutputFolder)\*.dll" /> </ItemGroup> <Target Name="BuildAll"> <Csc Sources="$(SourceFolder)\MyClass1.cs" TargetType="library" OutputAssembly="$(OutputFolder)\MyClass1.dll" EmitDebugInformation="True" /> <Csc Sources="$(SourceFolder)\MyClass2.cs" TargetType="library" OutputAssembly="$(OutputFolder)\MyClass2.dll" EmitDebugInformation="True" /> </Target> <Target Name="DeployDlls" DependsOnTargets="BuildAll"> <Copy SourceFiles="@(DllFiles)" DestinationFolder="$(DeployFolder)" /> </Target>
When the target DeployDlls is invoked with following command:
msbuild /t:DeployDlls build.proj
It is supposed to call the target BuildAll first due to the dependency, which builds the .cs files and then the DeployDlls target will run the Copy task to copy output files to the deploy folder. This seems to be a pretty straight forward script but it has a major bug in it. It does not work with a clean build.
The problem is that the ItemGroup clause is actually evaluated before any target was invoked. Therefore, when this script is executed against a clean build (i.e. no DLL files in the Build folder yet), the DllFiles item group will get evaluated into an empty array and therefore nothing will be copied at all after the build finished.
To fix this problem, the DeployDlls target must be updated to:
<Target Name="DeployDlls" DependsOnTargets="BuildAll"> <CreateItem Include="$(OutputFolder)\*.dll"> <Output TaskParameter="Include" ItemName="DllFiles"/> </CreateItem> <Copy SourceFiles="@(DllFiles)" DestinationFolder="$(DeployFolder)" /> </Target>
This way, the CreateItem task will populate DllFiles item group after the build is finished and right before the Copy task is executed.