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

描述

我无法让C#代码生成在我的.NET项目中可靠地工作.我可以让它在事先存在源文件时构建EITHER(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”的空目录.

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

我在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):错误MSB3105:项目“生成/ GeneratedClass.cs“在”Sources“参数中被多次指定. “Sources”参数不支持重复项. [/home/user/tmp/CodeGenExample.csproj]

“不存在”错误的完整错误文本:Program.cs(5,34):错误CS0103:当前上下文中不存在名称“GeneratedClass”[/home/user/tmp/CodeGenExample.csproj]

请求帮助

我最好的猜测是我的BeforeTargets =“CoreCompile”是错误的.我在那里尝试了很多不同的值(抱歉不记得哪些)我总是遇到这样或者另一个问题.这只是猜测.

我究竟做错了什么?

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

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

我拥有的csproj文件执行此操作:

>删除Generated目录中的所有文件.这是通过CleanGen目标完成的,并作为Project节点中的初始目标启动.
> GeneratedCode目标附加到输出文件,以证明它只发生一次.
>启用ItemGroup节点以允许编译生成的文件.
>回显变量$(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命令,可以向Exec添加一个Condition:

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

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

点赞