如果共享的bin目录用于本地构建,则VS 2017不会执行快速最新检查

我们要使用共享bin目录进行本地开发,就像在CI服务器上使用共享目录一样。这样,我们应用程序的完全构建分支所占空间不到三分之一。这意味着可以在我们的小型SSD上容纳更多分支。

因此,我们做到了,并且一切正常,除了VS 2017 IDE不再使用快速的最新检查启发式。

因此,在更改之前,当我们第二次构建代码时,构建输出报告如下内容:

========== Build: 0 succeeded,0 failed,94 up-to-date,0 skipped ==========

意思是IDE采用了启发式方法,决定所有项目都是最新的(非常快),并完全跳过了msbuild。

更改后,这种情况不会发生,IDE实际上将所有项目移交给了msbuild,整个过程大约需要一分钟。它实际上并没有编译任何代码,但是现在整个过程花费了更长的时间。

就像我们已经设置了DisableFastUpToDateCheck msbuild属性一样,除了没有设置。

那么,我们如何解决它?我想要启发式返回。

编辑1

关于我们的工作方式,我要说几句话。每个项目都在导入标准目标(例如microsoft.CSharp.targets)之前,先导入一个特殊的目标文件。它是在MSBuild之前的15天内开发的,因此我们不使用Directory.Build.targets。在此文件中,我们将OutDir明确设置为工作区中的某些共享bin目录。因此,所有项目照常指定OutputPath,但是在此特殊目标文件中它被OutDir覆盖。我认为这是失去启发式方法的根本原因-它仅查看csproj,因此看到OutputPath,但看不到OutDir。而且,由于在OutputPath找不到二进制文件,因此无法满足启发式要求。

我将测试这个假设,如果事实成立,将重新发布此评论作为我的答案。

hellobaby12321 回答:如果共享的bin目录用于本地构建,则VS 2017不会执行快速最新检查

  

只需添加有关I want the heuristic back的一些详细信息即可。

1.Hmm,刚刚进行了一些测试,并确认您的Edit1是正确的。

我们可以简单地在本地计算机上重现相同的问题,而无需使用共享文件夹。如果我们在导入的目标中使用$(OutDir),则由于VS始终会寻找$(OutputPath),因此会影响正常的构建行为。 (同意您的Edit1,这应该是一个很好的答案!)

2。要解决此问题,我们必须避免使用$(OutDir)指定最终输出路径。因此,我们可以使用$(OutputPath)吗,很遗憾

项目文件中的$(OutputPath)将始终覆盖导入的$(OutputPath)文件中定义的同名xx.targets。请参见this如果项目包含与环境属性同名的属性定义,则项目中的属性将覆盖环境变量的值。

我能想象的唯一可能的方法是在$(CustomPath)文件中定义xx.targets(代表共享文件夹),然后将OutputPath中的所有xx.csproj属性修改为<OutputPath>$(CustomPath)\$(Configuration)</OutputPath>。那可能是一件无聊的工作,因为您有将近100个项目文件:(

我认为除非我们放弃共享文件夹方式,否则很难避免无聊的工作。这个答案有点否定,但希望能对您有所帮助...

,

这是二进制日志无用的罕见情况之一,因为它是由msbuild生成的,即为时已晚。必须先在Visual Studio中打开诊断构建,因为诊断的第一行来自启发式。它指定了构建项目的原因。所有其他诊断日志都可以丢弃,但是第一行是无价的。它已将我引导至以下解决方案:

启用启发式

我在从每个项目导入的目标文件中添加了以下目标。那些不关心与MSBuild 14向后兼容的人可以将其添加到Directory.Build.Targets:

*我们最终选择了另一种方式-请参阅编辑2 *

<Target Name="SatisfyVisualStudioFastUpToDateCheckHeuristic" AfterTargets="AfterBuild" Condition="'$(OutDir)' != '' And '$(GatedCheckIn)' != True">
    <ItemGroup>
        <Files Include="$(TargetFileName).config" Condition="'$(AppConfig)' != '' And Exists('$(AppConfig)')" />
        <Files Include="$(TargetName).pdb" />
        <Files Include="$(TargetName)$(TargetExt)" />
        <Files Include="@(IntermediateAssembly->'%(Filename)%(Extension)')" />
        <Files Include="$(_SGenDllName)" Condition="'$(_SGenDllCreated)'=='true'" />
        <Files Include="@(ReferenceCopyLocalPaths->'%(DestinationSubDirectory)%(Filename)%(Extension)')" />
        <Files Include="@(_SourceItemsToCopyToOutputDirectory->'%(TargetPath)')" />
    </ItemGroup>
    <MakeDir Directories="$(OutputPath)\%(Files.RelativeDir)" />
    <Touch Files="@(Files->'$(OutputPath)%(Identity)')" AlwaysCreate="true" ContinueOnError="true"/>
</Target>

<Target Name="CleanFakeOutputFiles" AfterTargets="AfterClean" Condition="'$(OutDir)' != '' And '$(GatedCheckIn)' != True">
    <RemoveDir Directories="$(OutputPath)" />
</Target>

此目标在启发式程序预期的位置创建零长度的伪代理输出文件。

后续操作1

如果某人的某个项目将其他项目引用为DLL而不是项目引用,则这样做实际上可能会使构建失败。例如,如果项目Alice像这样引用项目Bob:

<Reference Include="Bob,Version=1.0.0.0,Culture=neutral,PublicKeyToken=...,processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\Bob\bin\$(Configuration)\Bob.dll</HintPath>
</Reference>

问题出在HintPath上。当我们使用共享的bin进行构建且没有伪造的代理输出时,HintPath将被忽略,因为它不会导致任何现有文件。因此,将在共享bin目录中找到Bob.dll并从那里加载它。但是,当bin\$(Configuration)中存在伪造文件以满足启发式要求时,HintPath返回一个现有文件,因此对Bob.dll的搜索以零长度伪造结束。显然,编译将失败。解决方法是将其更改为相应的项目参考,或者消除所有元数据的HintPath

<Reference Include="Bob" />

必须引用Bob.dll,就像我们引用GAC中的系统依赖项一样。

后续操作2

在这一点上,代码应该构建并且启发式工作。除非有时会告诉它找不到某种依赖性,例如System.IO.dll。在这种情况下,必须检查以下两个条件是否成立:

  1. 项目从某个目录(例如NuGet包)引用System.IO.dll
  2. System.IO.dll在GAC中找到,更适合目标框架。

在我的情况下,我有多个项目引用了NuGet的System.IO*System.Runtime*System.Security*System.ValueTuple,而这些dll由于具有.NET 4.7而存在于GAC中.2系统上的运行时。启发式方法会在HintPath中查找这些引用,并期望在bin\$(Configuration)文件夹中找到它们。

但是,该构建实际上是从GAC中获取它们的,并且从GAC引用的dll默认情况下不会复制到目标bin。因此,没有伪造的替代品可以满足启发式方法。

我的解决方案是用GAC的相应引用替换所有这些NuGet引用。

编辑1

问题1

后续操作1 的副作用是,不再容易返回本地bin文件夹,因为对项目dll引用的更改不向后兼容。

可以保留HintPath元数据,但是所有元数据都必须指向共享bin目录,在这种情况下,它仍然与旧方法不兼容。

问题2

每次加载Web应用程序项目时,Visual Studio都会覆盖.vs\config\applicationhost.config。意思是:

  • 打开解决方案时
  • 卸载项目后,重新加载

这是一个大麻烦,因为共享bin方法的一部分是通过脚本更新.vs\config\applicationhost.config中的路径。但是VS的这种行为使其成为一个主要的麻烦。

编辑2

我们已经更改了启用启发式的方式。我们不会执行VS期望的伪造文件,而是执行以下操作:

  1. 在构建的早期生成一个结点 bin.link-> bin
  2. 在启发式隐藏的目标文件中设置 OutDir = bin.link
  3. 使用 OutputPath = bin
  4. 修改了所有项目

通过这种方式,启发式方法可以看到正确的位置(OutputPath),但同时 OutputPath!= OutDir ,因此发布代码可以正常工作。

因此,不再需要零长度的垃圾文件。

对于VS覆盖.vs \ config \ applicationhost.config的情况-我们为此创建了一个自定义VS扩展名。

本文链接:https://www.f2er.com/3154699.html

大家都在问