TeamBuild OutDir, Item Expansion and Why Your Copy Task Gave You Nothing
I have been meaning to blog this for a long time and not too long ago the question came up again. So after seeing the question raised about three times I decided to add one more link out to the cloud that might be found to help people out.
One of the sources of this question came from an MSDN forums post and the response I placed on there about sums it up.
Basically the question usually involves creating a build Item that is expected to consists of the output files, such as those in OUTDIR, and then at some point near the end of the script copying those to some other location. But when the script completes this additional target location is empty.
Usually the persons build script will contain some elements of this nature:
1: <ItemGroup>
2: <MyDropFiles Include="$(DropLocation)\$(BuildNumber)\**\*.*"/>
3: </ItemGroup>
4: <Target Name="AfterDropBuild">
5: <Copy
6: SourceFiles="@(MyDropFiles)"
7: DestinationFiles="@(MyDropFiles->'\\mymachine\Drop\$(BuildNumber)\%(RecursiveDir)%(Filename)%(Extension)')"
8: />
9: </Target>
It's important to understand the points in time when things occur inside a build script. It's not a linear activity through the script so regardless of where you may put the Item definition found on line 2 in the script it will be evaluated and the MyDropFiles Item Collection will be populated very early on in the script- pretty much as soon as the script starts. Obviously if you look at the path in this example as soon as teh script starts you will find no files. You probably won't even find the path. There has been no build output and thus not files in the drop location because we haven't even done the first compile- assuming this path are for the files generated as part of this specific script itself. Thus when the AfterBuildDrop target runs there are no items in MyDropFiles (line 6) because MyDropFiles was populated as soon as the script started.
What you really want is for MyDropFiles to be populated just before you use the items. To do this you need to have something more like the following:
1: <!-- get rid of this- it get evaluated and expanded at the wrong time
2: <ItemGroup>
3: <MyDropFiles Include="$(DropLocation)\$(BuildNumber)\**\*.*"/>
4: </ItemGroup>
5: -->
6:
7: <Target Name="AfterDropBuild">
8:
9: <!-- I just do the below as a matter of form and practice for easier maintenance if the property is referred to in multiple locations in this target -->
10: <CreateProperty
11: Value="\\mymachine\Drop\$(BuildNumber)">
12: <Output
13: TaskParameter="Value"
14: PropertyName ="MyDropLocation"/>
15: </CreateProperty>
16:
17: <!-- notice I also don't use *.* in the include but * so that I pick up any files without an extension as well - this may or may not be what you desire-->
18: <CreateItem
19: Include="$(DropLocation)\$(BuildNumber)\**\*">
20: <Output
21: TaskParameter ="Include"
22: ItemName ="MyDropFiles"/>
23: </CreateItem>
24:
25: <Copy
26: SourceFiles="@(MyDropFiles)"
27: DestinationFiles="@(MyDropFiles->'$(MyDropLocation)\%(RecursiveDir)%(Filename)%(Extension)')"/>
28: </Target>
I also recommend reading Aaron Hallberg's blog and most specifically a great post in regards to OUTDIR and other related properties. You can also find this as a recipe (as lame of one as it may be on TeamBuild.com.