I thought this would be simple, but I can't figure out how to recursively copy items to multiple directories.
<
ItemGroup><Host Include="localhost">
<Hostname>localhost</Hostname>
<Domain>$(NamDMZ)</Domain>
</Host>
<!--
Development servers--><
Host Include="DevServer1"><Domain>$(NSRoot)</Domain>
<Hostname>DevServer1</Hostname>
<Description>Development server</Description>
</Host>
---
<
ItemGroup><HTMLPublishFiles Include="$(SourceRel)Customer\Data\html\company\**\*.*" />
<HTMLPublishFiles Include="$(SourceRel)Customer\Data\html\shared\**\*.*" />
<HTMLPublishFiles Include="$(SourceRel)Customer\Data\html\company_$(AssemblyName)\**\*.*" />
</ItemGroup>
<
Copy SourceFiles="@(HTMLPublishFiles)"DestinationFolder="$(PublishDir)%(Host.Hostname)\%(RecursiveDir)\" />
Here's the problem, instead of copying to
$(PublishDir)localhost\%(RecursiveDir)\ and
$(PublishDir)DevServer1\%(RecursiveDir)\
it just copies to
$(PublishDir)\%(RecursiveDir)\, which is not what is needed. How can I do this

How can I use %(RecursiveDir) and task batching together
Exellon
Neil,
Sorry, I missed your post. Your solution works, and it's shorter!
--Scott
50322899
That didn't work either, but if I split the CreateItem into two, then it works, like so:
<CreateItem AdditionalMetadata="RealRecDir=%(FilesToCopy.RecursiveDir)" Include="@(FilesToCopy)">
<Output ItemName="TestEx1" TaskParameter ="Include"/>
</CreateItem>
<CreateItem AdditionalMetadata="Hostname=%(Host.HostName" Include="@(TextEx1)">
<Output ItemName="TestEx2" TaskParameter ="Include"/>
</CreateItem>
<Copy SourceFiles="@(TestEx2)" DestinationFiles="@(TestEx2->'$(PublishDir)%(HostName)%(RealRecDir)%(Filename)')"/>
Thanks for the help.
Joe Butler
Scott,
I think your case the problem is you have a $(Host.HostName) in there, and your %(RecursiveDir) isn't qualified. Try changing the %(RecursiveDir) to %(HTMLPublishFiles.RecursiveDir) and see if that does the trick. Also, I assume you have a <Target> around this that actually batches on the Host ItemGroup
Neil
tachikoma
Now that I'm thinking about it my use of the RelativeDir may not function properly. Here is a more correct technique I think.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="PrintFiles">
<ItemGroup>
<Host Include="localhost">
<Hostname>localhost</Hostname>
<Domain>$(NamDMZ)</Domain>
</Host>
<!-- Development servers-->
<Host Include="DevServer1">
<Domain>$(NSRoot)</Domain>
<Hostname>DevServer1</Hostname>
<Description>Development server</Description>
</Host>
</ItemGroup>
<ItemGroup>
<FilesToCopy Include="Source\Ibr\**\*"/>
</ItemGroup>
<PropertyGroup>
<PublishDir>C:\pub\</PublishDir>
</PropertyGroup>
<Target Name="PrintFiles">
<CreateItem AdditionalMetadata="Hostname=%(Host.HostName);RealRecDir=%(FilesToCopy.RecursiveDir)" Include="@(FilesToCopy)">
<Output ItemName="Test" TaskParameter ="Include"/>
</CreateItem>
<Copy SourceFiles="@(Test)" DestinationFiles="@(Test->'$(PublishDir)%(HostName)%(RealRecDir)%(FileName)')"/>
</Target>
</Project>
Sayed Ibrahim Hashimi
www.sedodream.com
Jon Limjap
Ok, I messed around with this some tonight and I'm as confused as you, Scott. I sent mail to our whole MSBuild team to see if they can shed some light. Faisal's up on IM right now scratching his head, too. When I hear back, I'll let you know :)
Neil
Mark Watkin
Scott,
Did the CreateProperty method work for you as well It's quite a bit simpler to parse for future maintainers of your build script.
Neil
Geze
Scott,
I have an even better one for you :) Rajeev, master of all things batching, gave this supremely simple version that doesn't require CreateProperty:
<Target Name="Build" Outputs="%(Host.Identity)">
<Message Text="$(PublishDir)@(Host)\%(HTMLPublishFiles.Identity) "/>
</Target>
(Obvously, you can replace the Message task with the Copy task and get the same result)
Neil
DaveKoch
What about this approach:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="CopyFiles">
<ItemGroup>
<Host Include="localhost">
<Hostname>localhost</Hostname>
<Domain>$(NamDMZ)</Domain>
</Host>
<!-- Development servers-->
<Host Include="DevServer1">
<Domain>$(NSRoot)</Domain>
<Hostname>DevServer1</Hostname>
<Description>Development server</Description>
</Host>
</ItemGroup>
<ItemGroup>
<FilesToCopy Include="**\*"/>
</ItemGroup>
<PropertyGroup>
<PublishDir>C:\pub\</PublishDir>
</PropertyGroup>
<Target Name="CopyFiles">
<CreateItem AdditionalMetadata="Hostname=%(Host.HostName)" Include="@(FilesToCopy)">
<Output ItemName="Test" TaskParameter ="Include"/>
</CreateItem>
<Copy SourceFiles="@(Test)" DestinationFiles="@(Test->'$(PublishDir)%(HostName)\%(RelativeDir)%(FileName)')"/>
</Target>
</Project>
Sayed Ibrahim Hashimi
www.sedodream.com
Jeff Schindler
You're close, Sayed, but there's an easier way without having to use a funky transform. Just do a CreateProperty (rather than CreateItem) to put the hostname into a property. Then when you do the Copy command it only has to batch on one thing:
<
Target Name="Build" Outputs="%(Host.Identity)"><CreateProperty Value="%(Host.HostName)">
<Output PropertyName="Hostname" TaskParameter="Value"/>
</CreateProperty>
<Copy SourceFiles="@(HTMLPublishFiles)" DestinationFolder="$(PublishDir)\$(Hostname)\%(HTMLPublishFiles.RecursiveDir)"/>
</Target>
Also note that you have to make sure that every Include that's used to create HTMLPublishFiles has a ** in it. In the original example, one of them didn't, which will make the recursion work incorrectly.
Neil