如何在编译开始之前运行MSBuild任务,但是在生成中间文件之后?

时间:2016-12-25 03:46:44

标签: c# .net visual-studio visual-studio-2015 msbuild

背景:StyleCop抱怨自动生成的文件格式不佳,当我尝试构建项目时会导致许多警告。自动生成的文件位于我项目的obj/目录中,我想创建一个MSBuild任务,在编译之前将// <auto-generated/>添加到此文件中(但是在生成之后),以便StyleCop不会抱怨。

问题:我有以下MSBuild代码

<!-- StyleCop complains about a file that's auto-generated by the designer,
    so we need to prepend 'auto-generated' to it beforehand. -->
<Target Name="BeforeCompile" DependsOnTargets="MarkGeneratedFiles" />

<Target Name="MarkGeneratedFiles">
  <PropertyGroup>
    <GeneratedFilePath>$(MSBuildThisFileDirectory)obj\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).Program.cs</GeneratedFilePath>
  </PropertyGroup>

  <InsertIntoFile FilePath="$(GeneratedFilePath)" LineNumber="1" Text="// &lt;auto-generated/&gt;" />
</Target>

<!-- Code taken from http://stackoverflow.com/a/21500030/4077294 -->
<UsingTask
  TaskName="InsertIntoFile"
  TaskFactory="CodeTaskFactory"
  AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
  <ParameterGroup>
    <FilePath ParameterType="System.String" Required="true" />
    <LineNumber ParameterType="System.Int32" Required="true" />
    <Text ParameterType="System.String" Required="true" />
  </ParameterGroup>
  <Task>
    <Using Namespace="System" />
    <Using Namespace="System.IO" />
    <Code Type="Fragment" Language="cs">
      <![CDATA[
        // By tradition, text file line numbering is 1-based
        var lines = File.Exists(FilePath) 
                              ? File.ReadAllLines(FilePath).ToList() 
                              : new List<String>(1);
        lines.Insert(Math.Min(LineNumber - 1, lines.Count), Text);
        File.WriteAllLines(FilePath, lines);
        return true;
      ]]>
    </Code>
  </Task>
</UsingTask>

我要修改的文件的文件名为obj/Debug/netcoreapp1.0/BasicCompiler.Tests.Program.cs。在上面的代码段中,我有一个BeforeCompile目标,该目标取决于MarkGeneratedFiles,它继续尝试在该文件的第一行之前插入// <auto-generated/>

我已经测试过,如果生成的文件已经存在,这似乎工作正常。但是,如果我删除obj/目录或从其他计算机构建,我会收到此错误:

"C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj" (default target) (1) ->
(MarkGeneratedFiles target) ->
  C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj(68,5): error MSB4018: The "InsertIntoFile" task failed unexpectedly.\r
C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj(68,5): error MSB4018: System.IO.DirectoryNotFoundException: Could not find
a part of the path 'C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\obj\Debug\netcoreapp1.0\BasicCompiler.Tests.Program.cs'.\r

基本上似乎目标是在生成文件之前运行,因此没有任何内容可以在文本前添加。有没有办法在生成此文件后运行,但编译之前

附加说明:到目前为止,我查看了所有特殊目标名称here,并尝试同时使用BeforeBuildBeforeCompile

另外,由于我使用的是“new”StyleCop,因此我无法将<ExcludeFromStyleCop>放入项目文件中。见https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1145

1 个答案:

答案 0 :(得分:1)

我设法解决了这个问题;请参阅@ stijn的超级有用的评论here

  • 首先,我从命令行运行msbuild /v:detailed。这增加了MSBuild的详细程度,因此它可以让您更详细地了解正在进行的操作,例如:您可以看到正在运行的每个目标的名称。

  • 我在日志中搜索了生成该文件的目标的名称。就我而言,结果是GenerateProgramFiles

  • 我使用AfterTargets="GenerateProgramFiles"标记了我的自定义目标,因此它在生成文件后运行。