为什么MSBuild不像我期望的那样复制

时间:2008-11-05 22:49:29

标签: .net msbuild

我需要编写我的构建脚本。我正在使用MSBUILD,因为它与VS.net集成。我试图将一些文件从构建环境复制到部署文件夹。我正在使用MSBuild的复制任务。但不是像我期望的那样复制目录树。它将所有内容复制到一个文件夹中。我重复目录树中的所有文件,最终在一个文件夹中。我需要它将文件夹和目录树复制到目标文件夹中。有什么我想念的吗?

以下是构建脚本的相关部分:

<PropertyGroup>
    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
    <Source>outputfolder</Source>
    <DestEnv>x</DestEnv>
    <DeployPath>\\networkpath\$(DestEnv)</DeployPath>
</PropertyGroup>
<ItemGroup>
    <TargetDir Include="$(DeployPath)\**\*" Exclude="**\web.config"></TargetDir>
    <SourceDir Include="$(Source)\**\*" />
</ItemGroup>    
<Target Name="Clean" >
    <!-- clean detail ... -->
</Target>
<Target Name="migrate" DependsOnTargets="Clean">
    <Copy DestinationFolder="$(DeployPath)" SourceFiles="@(SourceDir)" />
</Target>

4 个答案:

答案 0 :(得分:13)

我们在调试MSBuild问题时发现了一个宝石:

http://blog.scrappydog.com/2008/06/subtle-msbuild-bug-feature.html

在Targets之前解析ItemGroups,因此当沿着脚本进一步引用ItemGroup时,将不会拾取任何创建新文件的目标(例如编译!)。

Eric Bowen还介绍了这个“功能”的解决方法, CreateItem 任务:

<Target Name="Copy" >
    <CreateItem Include="..\Source\**\bin\**\*.exe"
        Exclude="..\Source\**\bin\**\*.vshost.exe">
        <Output TaskParameter="Include" ItemName="CompileOutput" />
    </CreateItem>
    <Copy SourceFiles="@(CompileOutput)" 
        DestinationFolder="$(OutputDirectory)"></Copy>
</Target>

很多人对他赞不绝口!

答案 1 :(得分:12)

为Copy任务指定DestinationFolder时,它会从SourceFiles集合中获取所有项目并将它们复制到DestinationFolder。这是预期的,因为复制任务无法确定每个项目的路径的哪个部分需要用DestinationFolder替换以保持树结构。例如,如果您的SourceDir集合定义如下:

<ItemGroup>
    <SourceDir Include="$(Source)\**\*" />
    <SourceDir Include="E:\ExternalDependencies\**\*" />
    <SourceDir Include="\\sharedlibraries\gdiplus\*.h" />
</ItemGroup>

您希望目标文件夹树看起来像什么?

要保留树,您需要进行身份转换,并为SourceFiles集合中的每个项生成一个目标项。这是一个例子:

<Copy SourceFiles="@(Compile)" DestinationFiles="@(Compile->'$(DropPath)%(Identity)')" />

复制任务将获取SourceFiles集合中的每个项目,并通过使用$(DropPath)替换源项目规范中的**之前的部分来转换其路径。

有人可能会说DestinationFolder属性应该被写为以下转换的快捷方式:

<Copy SourceFiles="@(Compile)" DestinationFiles="@(Compile->'$(DestinationFolder)%(Identity)')" />

唉,这会阻止你试图避免的深拷贝到平面文件夹场景,但其他人可能会在他们的构建过程中使用。

答案 2 :(得分:5)

以递归方式复制目录内容和结构的非常简单的示例:

<Copy SourceFiles="@(Compile)" DestinationFolder="c:\foocopy\%(Compile.RecursiveDir)"></Copy>

@(编译)是要复制的所有文件的ItemGroup。可能是这样的:

   <ItemGroup>
      <Compile Include=".\**\*.dll" /> 
   </ItemGroup>

复制任务会将所有文件复制到c:\ foocopy中,就像xcopy一样。

答案 3 :(得分:4)

我在MSDN中的一个例子中找到了这个例子。我不明白它,但会在stackoverflow中为我的其他travlers留下一个例子。以下是上述迁移目标的固定版本:

<Target Name="migrate" DependsOnTargets="Clean">
    <Copy DestinationFiles="@(SourceDir->'$(DeployPath)\%(RecursiveDir)%(Filename)%(Extension)')" SourceFiles="@(SourceDir)" />
</Target>

如果有人真正了解这个例子,请解释。祝你好运!