c# - 在 .NET Core 2.x csproj 项目中可靠地生成 C# 代码?

标签 c# .net-core code-generation csproj

描述

我一直无法让 C# 代码生成在我的 .NET 项目中可靠地工作。我可以让它构建(a)当源文件预先存在时或(b)当源文件预先不存在时。我无法在两种情况下使用相同的设置。

为什么这很重要:如果我在我的开发机器上构建,我可能以前构建过代码,所以我需要它来重新生成现有的源代码。但是,在构建机器上构建时,这些文件不存在,因此在这种情况下我需要它从头开始生成代码。

设置

复制它只需要一个 csproj 和一个源文件。

这是一个引用示例的简单程序 GeneratedClass :

class Program
{
    public static void Main(string[] args)
    {
        System.Console.WriteLine(GeneratedClass.MESSAGE);
    }
}

这是我能想到的最简单的 csproj 文件。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <Target Name="GenerateCode" BeforeTargets="CoreCompile">

    <!-- Removing the source code beforehand makes no difference
    <Exec Command="rm $(ProjectDir)Generated/*.cs" IgnoreExitCode="true" />
    -->

    <Exec Command="echo 'class GeneratedClass { public static int MESSAGE = 1; }' > Generated/GeneratedClass.cs" />

    <!-- Toggling this setting will cause failures in some scenarios and success in others
    <ItemGroup>
      <Compile Include="Generated/*$(DefaultLanguageSourceExtension)" />
    </ItemGroup> -->

  </Target>
</Project>

创建一个名为“Generated”的空目录。

要构建,请运行 dotnet build从 csproj 和 Program.cs 文件所在的目录。

我在 Linux 上运行 .NET Core 2.0.3。我的 Docker 构建容器使用 microsoft/dotnet:2.0-sdk图片;我可以在 Docker 内部和外部复制该问题。

症状

请注意,在上面的 csproj 文件中有一个 <Compile Include被注释掉的设置。另请注意,多次运行构建将生成代码。 (可手动删除代码,复制构建开始时代码不存在的情况。)

这是我发现错误和未发现错误的矩阵:

+----------------------+----------------------+-----------------------------------+
| Compile Include=...? | Code Already Exists? |              Result               |
+----------------------+----------------------+-----------------------------------+
| Present              | YES                  | ERROR! "specified more than once" |
| Present              | NO                   | SUCCESS!                          |
| Commented Out        | YES                  | SUCCESS!                          |
| Commented Out        | NO                   | ERROR! "does not exist"           |
+----------------------+----------------------+-----------------------------------+

“多次指定”错误的完整错误文本:/usr/share/dotnet/sdk/2.0.3/Roslyn/Microsoft.CSharp.Core.targets(84,5): error MSB3105: The item "Generated/GeneratedClass.cs" was specified more than once in the "Sources" parameter. Duplicate items are not supported by the "Sources" parameter. [/home/user/tmp/CodeGenExample.csproj]

“不存在”错误的完整错误文本:Program.cs(5,34): error CS0103: The name 'GeneratedClass' does not exist in the current context [/home/user/tmp/CodeGenExample.csproj]

请求帮助

我最好的猜测是我的 BeforeTargets="CoreCompile"是错的。我在那里尝试了很多不同的值(抱歉不记得是哪些),而且我总是遇到这样或那样的问题。这只是一个猜测。

我做错了什么?

最佳答案

免责声明:您的实际项目中似乎有上述内容中没有的内容,因此我不确定此解决方案是否有效。

以下是一个 hacky 方法,因为它的行为并不像它应该的那样。
然而,它可能足以满足您的目的 - 这由您决定。我说它有问题的原因是预构建文件删除似乎执行了不止一次。1

我的 csproj 文件是这样做的:

  1. 删除生成目录中的所有文件。这是通过 CleanGen 目标完成的,并作为 Project 节点中的初始目标启动。
  2. GeneratedCode 目标附加到输出文件,以证明它只发生一次。
  3. 启用 ItemGroup 节点以允许编译生成的文件。
  4. 回显变量 $(NuGetPackageRoot) 以表明它已设置。

在此处完成 csproj 文件:

<Project InitialTargets="CleanGen" Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <Target Name="CleanGen">
    <Exec Command="echo 'Cleaning files...'" />
    <Exec Command="rm $(ProjectDir)Generated/*$(DefaultLanguageSourceExtension)" IgnoreExitCode="true" />
  </Target>
  <Target Name="GenerateCode" BeforeTargets="CoreCompile">
    <Exec Command="echo 'Generating files... $(NuGetPackageRoot)'" />
    <Exec Command="echo 'class GeneratedClass { public static int MESSAGE = 1; }' >> Generated/GeneratedClass.cs" />

    <ItemGroup>
      <Compile Include="Generated/*$(DefaultLanguageSourceExtension)" />
    </ItemGroup>
  </Target>
</Project>

这看起来确实比它应该做的要难...


1 OP 指出,为避免多次执行 rm 命令,您可以将 Condition 添加到 Exec:

<Exec 
    Command="rm $(ProjectDir)Generated/*$(DefaultLanguageSourceExtension)"
    Condition="Exists('$(ProjectDir)Generated/GeneratedClass$(DefaultLanguageSourceExtension)')" />

不幸的是,Exists 不接受 glob,因此您必须至少指定一个您知道将在该文件夹中生成的特定文件。通过这种妥协,您还可以摆脱 IgnoreExitCode="true",因为它只应在有要删除的文件时执行。

关于c# - 在 .NET Core 2.x csproj 项目中可靠地生成 C# 代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49075282/

相关文章:

c# - 交换单个链表上的节点

.net - 如何将多个 .NET Core 程序集合并为一个(.dll/.exe)?

php - 生成 PHP 代码(来自 Parser Tokens)

api - Docker:Docker运行后无法在浏览器上启动.Net Core 3.1 Web Api

linux - 运行单元测试时出现 DlibDotNet 异常 : System. DllNotFoundException:无法加载共享库 'DlibDotNetNative'

java - 如何实例化一个类 "annotation processed"?

java - 是否可以配置 maven 来编译生成的源代码而不使用插件?

c# - 如何在 Xamarin.Forms 中用引号将字符串括起来

c# - 如何在 ASP.NET Core MVC 中使用依赖注入(inject)设计存储库模式?

c# - 如何在可执行文件中存储字符串?