包含编译文件(增量构建)

Max*_*oro 6 msbuild csproj visual-studio

我有一个增量构建设置,如下所示:

  <ItemGroup>
    <MyInput Include="$(MyInput)" />
    <UpToDateCheckInput Include="@(MyInput)" />
    <UpToDateCheckBuilt Include="$(MyOutput)" />
  </ItemGroup>

  <Target Name="MyCodeGen"
          BeforeTargets="PreBuildEvent"
          DependsOnTargets="ResolveReferences"
          Inputs="@(MyInput);$(MSBuildThisFileFullPath);$(MyCodegenExe)"
          Outputs="$(MyOutput)">
    <Exec Command="$(MyCodegenExe) $(MyCodegenParams)" />
    <ItemGroup>
      <Compile Remove="$(MyOutput)" />
      <Compile Include="$(MyOutput)" />
    </ItemGroup>
  </Target>
Run Code Online (Sandbox Code Playgroud)

目标生成一个$(MyOutput)需要在当前项目中包含和编译的单个 .cs 文件。请注意,在目标中我首先删除然后包含此文件。

  1. 在第一次构建时,目标按预期运行。
  2. 在第二次构建时,目标不会按预期运行。但 MSBuild 仍然可以编译,因为$(MyOutput)“自上次最新检查以来已被修改”。
  3. 在第三次构建时,一切都是最新的。

如何在第二次构建中获得“一切都是最新的”结果?

sti*_*ijn 1

无条件包含该文件应该可行。例如,在 Microsoft.CSharp.targets 导入之后的默认 C# 项目模板中使用以下行:

<ItemGroup>
  <MyInput Include="in.txt" />
</ItemGroup>
<PropertyGroup>
  <MyOutput>Generated.cs</MyOutput>
</PropertyGroup>
<Target Name="MyCodeGen" BeforeTargets="PreBuildEvent" DependsOnTargets="ResolveReferences"
        Inputs="@(MyInput);$(MSBuildThisFileFullPath)"
        Outputs="$(MyOutput)">
  <ReadLinesFromFile File="@(MyInput)">
    <Output TaskParameter="Lines" ItemName="MyInputlines"/>
  </ReadLinesFromFile>
  <Message Text="CODEGEN lines @(MyInputlines)" />
  <WriteLinesToFile File="$(MyOutput)" Lines="@(MyInputlines)" Overwrite="True" />
</Target>
<ItemGroup>
  <Compile Include="$(MyOutput)"/>
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)

在命令行上运行 msbuild /v:d 的结果是(仅显示 MyCodeGen Target 的输出):

第一次运行的输出(Generate.cs 尚不存在):

Target "MyCodeGen" in project "my.csproj" (target "PreBuildEvent" depends on it):
Building target "MyCodeGen" completely.
Output file "Generated.cs" does not exist.
Using "ReadLinesFromFile" task from assembly "Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "ReadLinesFromFile"
Done executing task "ReadLinesFromFile".
Task "Message"
  CODEGEN lines using System;
Done executing task "Message".
Using "WriteLinesToFile" task from assembly "Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "WriteLinesToFile"
Done executing task "WriteLinesToFile".
Done building target "MyCodeGen" in project "my.csproj".
Run Code Online (Sandbox Code Playgroud)

第二次/第三次/...运行的输出:

Target "MyCodeGen" in project "my.csproj" (target "PreBuildEvent" depends on it):
Skipping target "MyCodeGen" because all output files are up-to-date with respect to the input files.
Input files: in.txt;my.csproj
Output files: Generated.cs
Done building target "MyCodeGen" in project "my.csproj".
Run Code Online (Sandbox Code Playgroud)

修改in.txt后构建的输出:

Target "MyCodeGen" in project "my.csproj" (target "PreBuildEvent" depends on it):
Building target "MyCodeGen" completely.
Input file "in.txt" is newer than output file "Generated.cs".
Using "ReadLinesFromFile" task from assembly "Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "ReadLinesFromFile"
Done executing task "ReadLinesFromFile".
Task "Message"
  CODEGEN lines using System.Linq;
Done executing task "Message".
Using "WriteLinesToFile" task from assembly "Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "WriteLinesToFile"
Done executing task "WriteLinesToFile".
Done building target "MyCodeGen" in project "my.csproj".
Run Code Online (Sandbox Code Playgroud)

此后运行的输出:

Target "MyCodeGen" in project "my.csproj" (target "PreBuildEvent" depends on it):
Skipping target "MyCodeGen" because all output files are up-to-date with respect to the input files.
Input files: in.txt;my.csproj
Output files: Generated.cs
Done building target "MyCodeGen" in project "my.csproj".
Run Code Online (Sandbox Code Playgroud)

如果这对您不起作用,则可能是您用来生成文件的进程无法与 msbuild 配合良好,因为它无法正确更新时间戳。例如,仅复制比项目文件更旧的现有文件将导致 generated.cs 的时间戳早于 $(MSBuildThisFileFullPath) 输入,因此目标将始终运行。

  • 建议将“MyOutput”添加到“DefaultItemExcludes”,这样就不会出现重复的项目 - 当有人使用属性页手动编辑项目时,在某些奇怪的情况下,这可能会混淆项目系统。 (2认同)
  • 我在第二次构建时跳过 MyCodeGen 没有问题。问题是 CoreCompile 没有被跳过。但我发现这个问题只出现在 VS 中,而不是在从命令行调用 MSBuild 时出现。 (2认同)