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

时间:2018-03-02 18:31:54

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

描述

我无法让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): 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"错了。我在那里尝试了很多不同的值(抱歉不记得哪些)我总是遇到这样或者另一个问题。这只是猜测。

我做错了什么?

2 个答案:

答案 0 :(得分:1)

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

以下是一种hacky方法,因为它的表现不尽如人意 然而,它可能足以满足您的目的 - 这是由您决定的。我说它是hacky的原因是预构建文件删除确实执行了不止一次。 1

我拥有的csproj文件:

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

    rm

    这看起来确实比它应该更难......

    1 OP指出,为避免多次执行Condition命令,您可以将Exec添加到<Exec Command="rm $(ProjectDir)Generated/*$(DefaultLanguageSourceExtension)" Condition="Exists('$(ProjectDir)Generated/GeneratedClass$(DefaultLanguageSourceExtension)')" />

    Exists

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

答案 1 :(得分:0)

我能够通过包含生成的项目来使其工作:

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

    <Exec Command="mkdir Generated" Condition="!Exists('Generated')" />
    <Exec Command="echo class GeneratedClass { public static int MESSAGE = 1; } > Generated/GeneratedClass.cs" />

    <ItemGroup>
      <Compile Include="Generated/*$(DefaultLanguageSourceExtension)" Exclude="@(Compile)" />
    </ItemGroup>

  </Target>
</Project>

更新4/16/2019

SpecFlow 3使用巧妙的Exclude="@(Compile)"技巧来编译生成的文件(https://specflow.org/2019/updating-to-specflow-3/):

<ItemGroup>
  <Compile Include="Generated/*$(DefaultLanguageSourceExtension)" Exclude="@(Compile)" />
</ItemGroup>

更新9/20/2018

请在此处查看示例git存储库:https://github.com/altso/SO49075282

cmd中复制的步骤:

C:\Temp>dotnet --version
2.1.402

C:\Temp>git clone https://github.com/altso/SO49075282.git
Cloning into 'SO49075282'...
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 5 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), done.

C:\Temp>cd SO49075282

C:\Temp\SO49075282>dotnet build
Microsoft (R) Build Engine version 15.8.166+gd4e8d81a88 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restoring packages for C:\Temp\SO49075282\SO49075282.csproj...
  Generating MSBuild file C:\Temp\SO49075282\obj\SO49075282.csproj.nuget.g.props.
  Generating MSBuild file C:\Temp\SO49075282\obj\SO49075282.csproj.nuget.g.targets.
  Restore completed in 311.61 ms for C:\Temp\SO49075282\SO49075282.csproj.
  SO49075282 -> C:\Temp\SO49075282\bin\Debug\netcoreapp2.1\SO49075282.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.95

C:\Temp\SO49075282>dotnet build
Microsoft (R) Build Engine version 15.8.166+gd4e8d81a88 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 60.66 ms for C:\Temp\SO49075282\SO49075282.csproj.
  SO49075282 -> C:\Temp\SO49075282\bin\Debug\netcoreapp2.1\SO49075282.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.17

C:\Temp\SO49075282>