如何在 Rider 中触发文件更改的设计时构建?

Posted

技术标签:

【中文标题】如何在 Rider 中触发文件更改的设计时构建?【英文标题】:How to trigger a design-time build on file change in Rider? 【发布时间】:2019-02-18 10:31:56 【问题描述】:

我创建了一个NuGet package,它在设计和构建时从 DSL 文件生成 C# 代码。它在 Visual Studio 中运行良好,但在 Rider 中存在一些问题(我将在下面描述)。

在 Visual Studio 中

该包为 DSL 文件声明 custom items,并为它们分配一个 MSBuild:Compile Generator 元数据,每次文件更改时都会在 Visual Studio 中触发 design-time build。

然后,使用BeforeTargets="CoreCompile" 将自定义目标挂钩到构建。它生成 C# 代码并将Compile 项目添加到项目中。目标在正常和设计时构建中运行,并支持incremental builds 以避免不必要的工作。

这是相关的(简化和注释的)MSBuild 代码:

props 文件:

<Project>

  <!-- Define the item type with its default metadata -->
  <ItemDefinitionGroup>
    <ZebusMessages>
      <Generator>MSBuild:Compile</Generator>
    </ZebusMessages>
  </ItemDefinitionGroup>

  <!-- Make the item type user-visible in VS -->
  <ItemGroup>
    <AvailableItemName Include="ZebusMessages" />
    <PropertyPageSchema Include="$(MSBuildThisFileDirectory)ZebusMessages.xml" />
  </ItemGroup>

  <!-- Include all .msg files by default -->
  <ItemGroup>
    <ZebusMessages Include="**\*.msg" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
  </ItemGroup>

</Project>

targets 文件:

<Project>

  <ItemGroup>
    <!-- Add a GeneratorTargetPath metadata for the target file -->
    <ZebusMessages Update="@(ZebusMessages)" GeneratorTargetPath="$([MSBuild]::ValueOrDefault('$(IntermediateOutputPath)ZebusMessages/%(RecursiveDir)%(FileName)%(Extension).cs', '').Replace('\', '/'))" />

    <!-- Remove all None items for .msg files -->
    <None Remove="**\*.msg" />
  </ItemGroup>

  <Target Name="GenerateZebusMessages"
          BeforeTargets="CoreCompile"
          Condition="'@(ZebusMessages)' != ''"
          Inputs="@(ZebusMessages)"
          Outputs="@(ZebusMessages->'%(GeneratorTargetPath)')">

    <!-- Generate output files -->
    <GenerateZebusMessagesTask InputFiles="@(ZebusMessages)" />

    <ItemGroup>
      <!-- Add the output items -->
      <Compile Include="@(ZebusMessages->'%(GeneratorTargetPath)')" Visible="false" />
      <FileWrites Include="@(ZebusMessages->'%(GeneratorTargetPath)')" />
    </ItemGroup>
  </Target>

</Project>

完整文件为available here。

在骑士中

使用 Rider 2018.3.3 测试:

这在 Rider 中无法开箱即用,因为它似乎不会像 VS 那样触发设计时构建。

起初,Rider 完全不知道生成的 C# 文件,这导致它在引用生成的类的代码中显示错误。

我使用了 Rider 的 internal mode,它公开了对项目的“重新加载项目并显示日志”操作,这表明 Rider 在项目加载时调用 MSBuild,并具有以下目标:

GenerateAssemblyInfo;_CheckForInvalidConfigurationAndPlatform;GetFrameworkPaths;ResolvePackageDependenciesDesignTime;CollectPackageReferences;BeforeResolveReferences;ResolveAssemblyReferences;ResolveComReferences;ResolveSDKReferences;ResolveCodeAnalysisRuleSet

请注意该列表中缺少 CompileDesignTimeCompile

所以我添加了以下目标来支持 Rider:

  <Target Name="GenerateZebusMessagesRiderDesignTime"
          Condition="'$(BuildingByReSharper)' == 'true'"
          AfterTargets="PrepareForBuild"
          DependsOnTargets="GenerateZebusMessages" />

它挂钩到ResolveAssemblyReferences 所依赖的PrepareForBuild,并调用GenerateZebusMessages

这让 Rider 知道生成的文件,但我想解决一些问题:

如果我编辑 .msg 文件,生成的 C# 代码只会在项目构建时更新,而 Rider 的代码补全到那时才更新。 如果我将 .msg 文件添加到项目中,我需要重新加载项目以将其考虑在内。在这里构建项目没有帮助。

问题

我想让 Rider 调用 MSBuild 目标并在编辑或添加/删除 .msg 文件时重新评估项目。

我在添加文件时只在日志中发现了以下内容,但并没有太大帮助:

10:54:05.076 |I| ProjectModel                   | RequestBuilder thread:7        | Add item ZebusMessages = 'OtherFile.msg'...
10:54:05.076 |I| ProjectModel                   | RequestBuilder thread:7        | Item matches to a wildcard pattern, mark project as dirty
10:54:05.095 |I| ProjectModel                   | RequestBuilder thread:7        | Project file content requested: ZebusMessages.csproj
10:54:08.330 |I| ProjectModel                   | RequestBuilder thread:7        | Item with EvaluatedInclude 'OtherFile.msg' was already changed. Perform project reevaluation before processing.
10:54:08.361 |I| ProjectModel                   | RequestBuilder thread:7        | Project 'ZebusMessages.csproj' was reevaluated in 22 ms, EvaluationCounter: 9

在此时间范围内,日志中没有其他内容提及 MSBuild。

当给定的文件类型发生变化时,是否可以在 Rider 中触发类似于设计时构建的东西?

或者更一般地说,Rider 在什么情况下调用 MSBuild 来重新评估项目?

【问题讨论】:

【参考方案1】:

我为你准备了两个故事——短的和长的 =)

短篇小说

它应该在即将到来的 2019.1 EAP 1 中开箱即用

长篇大论

Rider(与 Visual Studio 不同)不会在项目加载阶段执行设计时构建。我们认为它太贵了(尤其是对于基于 .net sdk 的项目)。所以 Rider 会评估每个项目,然后构建一些预定义的目标来获取生成的文件和程序集引用。

有时它会导致问题(例如这个),因此在 2019.1 中,我们实施了额外的算法,扫描所有导入的目标并查找自定义的 item-factory 目标。据我所知,您的目标非常适合,因此 Rider 将能够找到它并与预定义的目标一起构建。

如果它以某种方式不起作用,您仍然有两个选择:

    我们重写了Build Tools 选项页面,因此您可以在加载过程中添加任何自定义目标 在这里提出问题:https://youtrack.jetbrains.com/newIssue?project=Rider,我会尽快解决您的问题。此类问题对我们来说具有最高优先级。

【讨论】:

我尝试了 2019.1 EAP 1,它好多了(基本支持不再需要解决方法),但一些问题仍然存在。我提交了issue here。 我会在那里回复你=)

以上是关于如何在 Rider 中触发文件更改的设计时构建?的主要内容,如果未能解决你的问题,请参考以下文章

仅当特定文件集发生更改时如何触发构建

如何在 Rider IDE 中更改 SDK 版本 asp netcore?

如何排除对管道 yaml 文件的更改以触发构建 i azure devops?

JetBrains Rider:无法更改项目配置文件中的环境变量

如何在 Rider 中支持多个应用程序配置

如何在 Rider 中更改这条线的颜色?