如何使用msbuild在构建过程中生成文件

19

有人知道如何修改 csproj 文件以生成代码文件,而不实际引用这些文件吗?

一个类似于以下过程:

  • 创建文件,
  • 在构建期间动态引用临时文件
  • 编译后的程序集包含额外成员,这取决于构建过程中创建的文件

这样做的目的是使用 Roslyn 创建一种生成代码文件的方式,而不是使用非常麻烦的 T4 模板,一旦您尝试根据属性做某些操作,就会发现 T4 模板很难使用。

因此,我计划提供一种使用特殊的 C# 文件(具有完整的语法支持)来基于该特殊文件的内容以编程方式生成文件的方法。

我已经花了几周时间在互联网上寻找资源(关于 msbuild 的主题),但到目前为止似乎我没有使用正确的关键字。

这篇文章对我来说是最有见地的: https://www.simple-talk.com/dotnet/.net-tools/extending-msbuild/

我的猜测是,为了在构建过程中填充自定义代码文件,对于我的目的来说,正确的构建目标应该是 "BeforeCompile"。

有没有人遇到过我这个问题,或者知道任何处理这个任务的特定资源?

我使用的解决方案:

<UsingTask TaskName="DynamicCodeGenerator.DynamicFileGeneratorTask" AssemblyFile="..\DynamicCodeGenerator\bin\Debug\DynamicCodeGenerator.dll" />
<Target Name="DynamicCodeGeneratorTarget" BeforeTargets="BeforeBuild;BeforeRebuild">
    <DynamicFileGeneratorTask>
        <Output ItemName="Generated" TaskParameter="GeneratedFilePaths" />
    </DynamicFileGeneratorTask>
    <ItemGroup>
        <Compile Include="@(Generated)" />
        <FileWrites Include="@(Generated)" />
        <!-- For clean to work properly -->
    </ItemGroup>
</Target>

很不幸,我没有像建议的那样使用propertygroup覆盖成功。

更新:这个链接也很有趣:https://github.com/firstfloorsoftware/xcc/blob/master/FirstFloor.Xcc/Targets/Xcc.targets


1
"BeforeTargets="BeforeBuild;BeforeRebuild"" - 这是改变游戏规则的部分。 - Chris W
2个回答

19
生成代码文件可以通过使用 msbuild 任务 或者 msbuild 内联任务 来实现。你需要生成正确的代码。你必须创建输出项参数以将其附加到 @(Compile) 项中。你可以使用 $(IntDir) 位置来定位你新生成的文件,并将它们添加到 @(FileWrites) 项组中,以便 Clean 目标正常工作。

当你完成编写任务后,你必须像这样在项目中使用它:

<UsingTask TaskName="TaskTypeFullName" AssemblyFile="YourAssembly.dll"/>
<PropertyGroup>
<!-- Here you need to experiment with [Build/Compile/SomeOther]DependsOn property -->
    <BuildDependsOn>
        MyCodeGenerator;
        $(BuildDependsOn)
    </BuildDependsOn>
</PropertyGroup>

<Target Name="MyCodeGenerator">
    <YourTaskName>
        <Output ItemName="Generated" TaskParameter="GeneratedFiles" />
    </YourTaskName>
    <ItemGroup>
        <Compile Include="@(Generated)" /> 
        <FileWrites Include="@(Generated)" /> <!-- For clean to work properly -->
    </ItemGroup>
</Target>


不错!我一段时间以前开始尝试UsingTask,但是没有将它与你的Output + Compile声明结合起来。这应该可以完成工作。一旦我验证了这一点,我会检查并点赞你的答案。它看起来非常有前途。感谢你的帮助。 - Dbl
请注意,为了使目标能够正常执行,必须在导入项目特定目标(即Microsoft.CSharp.targets)之后声明属性BuildDependsOn。 - stukselbax
我回家后会进行测试。根据我的阅读,我猜测应该是这样工作的,但还有一个确认问题:@(Generated) 是否支持类似于“subfolder1/file.cs;subfolder2/file.cs”的语法,并且取决于我根据相对路径生成文件到csproj(与编译包含通常一样)?如果我理解正确,那么文件必须被复制到那里,因为它们将在构建周期后移动。是这样吗? - Dbl
是的。@(Generated) 项中放置什么完全由您决定。一旦将其包含在编译项中,它们将作为源参数传递给 Csc 任务。此外,项支持通配符。 - stukselbax

15

我想使用一个Bash脚本在Linux上为一个使用.NET Core的项目生成代码。这是对我有效的方法。感谢@stukselbax,我是基于你的答案构建的。

  <Target Name="GenerateProtocolBuffers" BeforeTargets="BeforeBuild;BeforeRebuild">
    <Exec Command="./generatecode.sh" Outputs="proto/*.cs">
      <Output ItemName="Generated" TaskParameter="Outputs" />
    </Exec>
    <ItemGroup>
      <Compile Include="@(Generated)" />
      <FileWrites Include="@(Generated)" />
    </ItemGroup>
  </Target>
请注意,我用来生成代码的脚本名为generatecode.sh。请将其替换为您自己的脚本。

2
最终我使用 CoreCompile <Target Name="PreBuild" BeforeTargets="CoreCompile"> 代替尝试处理输出。 - Ashitakalax

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