从MSBuild部署而不覆盖特定文件

时间:2013-12-18 21:25:18

标签: msbuild

所以这就是我想要做的事情:

我想要一个构建脚本,它将xcopy将遗留winform应用程序的构建输出部署到给定目录。我想指定一个不覆盖的文件列表(一些配置文件)。

我宁愿让不覆盖的文件列表作为参数传递,而不是硬编码。

这似乎真的很难。这是我到目前为止所做的:


 <!-- A property that is passed a semicolon delimited list of file names -->
 <PropertyGroup>
 <ProtectedFiles/>
 </PropertyGroup>

 <--! An ItemGroup to pick up the files>
 <ItemGroup>
    <FilesToDelete Include=$(DeploymentTargetFolder)\*.* Exclude="@(ProtectedFiles->'$(DeployTargetFolder)\%(identity)')"
 <ItemGroup/>
 <--! the delete isn't working, so I will stop just with that to keep the code brief -->
 <Delete Files="@(FilesToDelete)"/>

删除只会忽略排除文件并删除所有内容

有更好的方法吗?它似乎并不太疯狂 - 我只想

  • 删除目标目录中的所有文件(配置文件除外)
  • 将构建输出中的所有文件复制到目标目录,而不覆盖配置文件。

2 个答案:

答案 0 :(得分:1)

您的特定标记的第一个问题似乎会使MsBuild $(properties)MsBuild %(items)MsBuild @(itemgroups)混淆。

ProtectedFiles是一个属性:

 <!-- A property that is passed a semicolon delimited list of file names -->
 <PropertyGroup>
 <ProtectedFiles/>
 </PropertyGroup>

但它被视为一个项目,并且没有任何%item.metadata:

<--! An ItemGroup to pick up the files>
 <ItemGroup>
    <FilesToDelete Include=$(DeploymentTargetFolder)\*.* Exclude="@(ProtectedFiles->'$(DeployTargetFolder)\%(identity)')"
 <ItemGroup/>

将以下标记保存为“foo.xml”,然后调用“ msbuild.exe foo.xml ”并观察输出:

<Project ToolsVersion="4.0" DefaultTargets="foo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <FilesProp>FileA.txt;FileB.txt</FilesProp>
    </PropertyGroup>
    <ItemGroup>
      <ProtectedFiles Include="FileA.txt" />
      <ProtectedFiles Include="FileA.txt" />
    </ItemGroup>

    <Target Name="foo">
        <Message Importance="high" Text="ProtectedFiles ItemGroup: @(ProtectedFiles)" />
        <Message Importance="high" Text="ProtectedFiles ItemGroup transform: @(ProtectedFiles->'%(Identity)')" />
        <Message Importance="high" Text="FilesProp Property: $(FilesProp)" />
        <Message Importance="high" Text="FilesProp Property: @(FilesProp->'%(FilesProp.Identity)')" />
    </Target>
</Project>

将产生以下输出:

foo:
  ProtectedFiles ItemGroup: FileA.txt;FileA.txt
  ProtectedFiles ItemGroup transform: FileA.txt;FileA.txt
  FilesProp Property: FileA.txt;FileB.txt
  FilesProp Property:

如果您无法更改设计并需要转换包含以分号分隔的文件路径列表的Property,请使用MsBuild <CreateItem /> task

将此标记添加到Foo目标之后发生的foo.xml,然后再次调用msbuild,但使用“bar”目标(例如 msbuild.exe foo.xml / t:bar

<Target Name="bar">

    <CreateItem Include="$(FilesProp)">
       <Output TaskParameter="Include" ItemName="TheFiles"/>
    </CreateItem>

    <Message Text="TheFiles ItemGroup: @(TheFiles)" Importance="high" />
    <Message Text="Output each item: %(TheFiles.Identity)" Importance="high" />

</Target>

将产生以下输出:

bar:
  TheFiles ItemGroup: FileA.txt;FileB.txt
  Output each item: FileA.txt
  Output each item: FileB.txt

接下来你应该重新考虑你的一些假设。我不认为文件扩展名应该是决定更新哪些文件的决定因素,而是你应该依赖于MsBuild能够逐步构建任务,只有当输入比输出更新时才能执行任务。您可以使用配置为跳过未更改文件的MsBuild <Copy /> task来执行此操作。

将此标记添加到上面的Xml文件中,然后修改$(SourceFolder)和$(TargetFolder)以指向要递归复制的源文件夹,以及放置文件的目标文件夹。使用“ msbuild.exe foo.xml / t:部署”构建并观察输出。

<Target Name="Deployment">
    <PropertyGroup>
        <SourceFolder>c:\sourcefolder\</SourceFolder>
        <TargetFolder>c:\destinationfolder\</TargetFolder>
    </PropertyGroup>
    <CreateItem Include="$(SourceFolder)\**\*.*">
        <Output TaskParameter="Include" ItemName="FilesToCopy" />
    </CreateItem>

    <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(TargetFolder)%(RecursiveDir)" SkipUnchangedFiles="true" />

</Target>

如果不修改任何源文件,请再次运行该命令并注意没有复制文件。

修改源文件夹中的文件,然后再次运行该命令。请注意,只复制了更新的文件?

我希望这能让你走上正轨。

答案 1 :(得分:0)

似乎有一个已经存在的帖子,类似于此。请查看此Trying to exclude certain extensions doing a recursive copy (MSBuild)