如何在MSBUILD中覆盖.NET引用的CopyLocal(私有)设置?

28

我已经苦苦挣扎了几天,大量的搜索后我找不到正确的解决方法。

我想要做的是设置一个MSBUILD项目来构建我们整个.NET应用程序(由8个解决方案和大约250个项目组成),并覆盖一些个别项目文件中的设置,以便为自己创建一个调试版本。

具体来说,我想使用我们的发布配置来生成没有优化和完整调试信息/pdb的版本。此外,为了缩短我们目前超长的构建时间,我想在每个项目的每个引用上将“复制本地”(或实际的proj文件xml中的私有)设置为false。

前面的事情相当容易,我可以相当容易地覆盖项目级属性(甚至可以使用MSBuild命令行中的/p来完成此操作),但我无法弄清楚如何覆盖引用上的属性。我尝试了StackOverflow和网络上看到的几种方法,但还没有找到解决方案。

如果我更改Microsoft.Common.targets中的代码,注释掉_CopyFilesMarkedCopyLocal目标中的代码将完全跳过这些文件的复制。但我不希望改变我的全局配置并在所有情况下都这样做。我创建了自己的备用目标文件——如果我更改单个项目文件以指向它,则会起作用——但我无法弄清楚如何在顶级(即构建所有8个解决方案的.proj文件)指定这一点。如果我能够覆盖顶层的_CopyFilesMarkedCopyLocal目标,那就太好了,这样如果MS更改了默认的目标文件,我的构建就不会出问题,但是我无法弄清楚如何使其工作。

最理想的情况是,如果有一种方法可以像项目级属性一样覆盖引用级属性,而无需重写/覆盖构建目标——但我没有找到任何表明这是可能的信息。

提前感谢您的帮助。

P.S. 我不可能只是逐个处理所有项目文件并对它们进行更改;我需要一个可以保留现有代码的解决方案。


将CopyLocal设置为false可能会在部署时引起不同的问题。请参阅我的博客文章“除非理解后果,否则不要将项目引用的“复制本地”更改为false。”(http://geekswithblogs.net/mnf/archive/2012/12/09/do-not-change-copy-local-project-references-to-false-unless.aspx) - Michael Freidgeim
不是回答你的问题,但希望微软可以原生地解决这个问题:https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/13280400-improve-visual-studio-build-performance-for-large - Mark
从MSBuild 15开始,您可以轻松完成此操作,请参见我下面的答案 https://dev59.com/uHI-5IYBdhLWcg3wxbjv#50755445 - gReX
3个回答

32

尝试这个:

CustomAfterMicrosoftCommonTargets 设置为 C:\foo\filter.targets (或者您命名的其他文件),然后将 filter.targets 设置为:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemDefinitionGroup>
      <Reference>
        <Private>False</Private>
      </Reference>
    </ItemDefinitionGroup>
</Project>

6
这个方法很有效,我可以像这样简单地添加另一个节点来实现相同的行为: <ProjectReference> <Private>False</Private> </ProjectReference>以在我的项目引用中得到相同的行为(而不仅是文件引用)。 - David Hay
4
唯一需要注意的是,这将设置<Private>的默认值。如果它在任何单独的<Reference>上设置了,那么该值将覆盖此处指定的值。 - Tom Mayfield
2
如果在.csproj中配置了设置,是否有任何方法可以覆盖该设置? - lysergic-acid
请注意,如果您将<Reference>替换为<ProjectReference>,此方法也适用于项目引用。 - ya23
1
有一个技巧:将引用的“复制本地”设置为false,然后再设置为true,Visual Studio会自动为该引用添加Private元数据。至少VS 2010是这样的。 - JustAMartin
@Martin,在这种情况下,您不希望将其明确添加到项目本身。如果是这种情况,那么上面的内容就没有帮助了。它为未指定的情况提供了默认值。 - Sayed Ibrahim Hashimi

8
在类似filter.targets的文件中添加一个目标:
<Target Name="BeforeBuild">
  <ItemGroup>
        <ReferenceNew Include="@(Reference)">
           <Private>False</Private>
        </ReferenceNew>
        <Reference Remove="@(Reference)"/>
        <Reference Include="@(ReferenceNew)"/>
  </ItemGroup>
</Target>

这将重新创建@(Reference)列表,将元数据“Private”设置为false,以确保引用不会被复制。 然后使用属性$(CustomAfterMicrosoftCommonTargets)设置为filter.targets文件的路径来构建您的解决方案或项目。
msbuild foo.sln /p:CustomAfterMicrosoftCommonTargets=c:\foo\filter.targets

这样,BeforeBuild目标将在构建项目之前被调用,并且所有引用都将相应设置。如果您想更好地控制Private是false还是true,可以修改BeforeBuild目标并通过属性进行控制。

编辑:在BeforeBuild目标中设置元数据可能有更好的方法。


这对我似乎不起作用。它肯定在运行BeforeBuild目标(我放了一个消息任务来检查它是否出现)每个项目,但它似乎没有对引用做任何事情。这感觉是正确的方向,但上面呈现的目标仍然有些问题。 - David Hay
在 BeforeBuild 目标中尝试在更改引用之前和之后打印引用。像这样打印: “ref: %(Reference.Identity) : %(Reference.Private)” - radical
我最终成功让这个方法针对引用工作(我在过滤文件中有其他问题),但我认为上面Sayed的解决方案更加简洁。 - David Hay
请注意,如果您将<Reference>替换为<ProjectReference>,则此方法也适用于项目引用。 - ya23
这似乎可以工作,但它也会复制GAC dll或者除了我自己的Dll之外的Dll。我该怎么过滤呢?或者排除System.dll等等? - Zenwalker
将“CopyLocalDependenciesWhenParentReferenceInGac”设置为true,以抑制该行为。 - Michael Baker

2

从msbuild v 15开始,您可以在包含源代码的根目录中复制一个名为Directory.Build.props的单个文件。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
  <Reference>
    <Private>False</Private>
  </Reference>
</ItemDefinitionGroup>
</Project>

这在使用Visual Studio 2017和vNext Build时非常有效。可能需要关闭Visual Studio,然后重新打开解决方案以使文件生效。

https://learn.microsoft.com/zh-cn/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接