简体   繁体   English

MSBuild - 如果文件不在 output 目录中,则将文件复制到 output 目录

[英]MSBuild - Copy file to output directory if the file isn't in the output directory

I have a nuget with a.targets file that tells the consuming project to copy all files within a "Dependencies" folder to the output directory.我有一个带有 .targets 文件的 nuget,它告诉消费项目将“依赖项”文件夹中的所有文件复制到 output 目录。

  <ItemGroup>
    <Files Include="$(MSBuildThisFileDirectory)/../contentFiles/Dependencies/*.*" />
  </ItemGroup>
  <Target Name="CopyDependencies" AfterTargets="Build">
    <Copy SourceFiles="@(Files)" 
          DestinationFolder="$(TargetDir)" />
  </Target>

This nuget is consumed by two projects: Project A and Project B .这个 nuget 由两个项目使用: Project AProject B For this question, let's say we have a System.Runtime.InteropServices.RuntimeInformation.dll that is one of the dependencies within this nuget. The output directory of Project A does not already have System.Runtime.InteropServices.RuntimeInformation.dll , so it gets copied to the output directory when the project is built.对于这个问题,假设我们有一个System.Runtime.InteropServices.RuntimeInformation.dll ,它是这个 nuget 中的依赖项之一。项目 A 的 output 目录还没有System.Runtime.InteropServices.RuntimeInformation.dll ,所以它在构建项目时被复制到 output 目录。 Project B however already contains System.Runtime.InteropServices.RuntimeInformation.dll in the output directory.但是,项目 B 已经在 output 目录中包含System.Runtime.InteropServices.RuntimeInformation.dll This causes a runtime issue at startup since the targets file is trying to overwrite the existing DLL of the same name with the System.Runtime.InteropServices.RuntimeInformation.dll file from within the nuget (which is a dependency of other files within the output directory).这会导致启动时出现运行时问题,因为目标文件试图用 nuget 中的System.Runtime.InteropServices.RuntimeInformation.dll文件覆盖同名的现有 DLL(这是 output 目录中其他文件的依赖项) ).

So, how can I adjust my.targets file to only copy in files that do not already exist within the output directory based on name , and not size or date modified ?那么,我如何调整 my.targets 文件以仅根据名称复制 output 目录中尚不存在的文件,而不是大小或修改日期

There are several ways but probably the most succinct change to your example code would be the following:有几种方法,但对您的示例代码最简洁的更改可能如下所示:

    <ItemGroup>
        <Files Include="$(MSBuildThisFileDirectory)/../contentFiles/Dependencies/*.*" />
    </ItemGroup>

    <Target Name="CopyDependencies" AfterTargets="Build">
        <Copy SourceFiles="@(Files)" DestinationFolder="$(TargetDir)" Condition="!Exists('$(TargetDir)/%(Filename)%(Extension)')" />
    </Target>

The change is adding a Condition on the Copy that is using the metadata of the @(Files) collection to test that the file does not exist in $(TargetDir) .更改是在Copy上添加一个Condition ,该条件使用@(Files)集合的元数据来测试$(TargetDir)中不存在该文件。

Because of the use of metadata, the Copy is a task batch .由于使用了元数据, Copy是一个任务批处理 Essentially the @(Files) collection is divided into batches by %(Filename)%(Extension) and there is a separate Copy task invoked for each batch.本质上, @(Files)集合按%(Filename)%(Extension)分为多个批次,每个批次调用一个单独的Copy任务。

If there is a large number of files in the Dependencies folder, the following variant may provide better performance.如果Dependencies文件夹中有大量文件,以下变体可能会提供更好的性能。

    <ItemGroup>
        <Files Include="$(MSBuildThisFileDirectory)/../contentFiles/Dependencies/*.*" />
    </ItemGroup>

    <Target Name="CopyDependencies" AfterTargets="Build">
        <ItemGroup>
            <FilesToCopy Include="@(Files)" Condition="!Exists('$(TargetDir)/%(Filename)%(Extension)')" />
        </ItemGroup>

        <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(TargetDir)" />
    </Target>

The task batching is moved to the definition of a new ItemGroup collection and the Copy task is invoked once for the set of files.任务批处理移动到新ItemGroup集合的定义中,并且为文件集调用一次Copy任务。 The potential performance improvement is that the implementation of the Copy task tries to parallelize copies, which it can't do when invoked per file.潜在的性能改进是Copy任务的实现尝试并行化副本,这在按文件调用时无法做到。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM