Настройка сборки для обработки созданных файлов

В любой сборке файлы, создаваемые во время сборки, ведут себя по-разному от статических файлов (например, исходных файлов). По этой причине важно понять, как MSBuild создает проекты. Два этапа — это этап оценки и этап выполнения. На этапе оценки MSBuild считывает проект, импортирует все, создает свойства, расширяет глобы для элементов и настраивает процесс сборки. На этапе выполнения MSBuild выполняет сборку, выполняя целевые объекты и задачи с данными, которые он анализирует во время этапа оценки.

Файлы, созданные во время выполнения, не существуют на этапе оценки, поэтому они не включаются в процесс сборки. Чтобы устранить эту проблему, необходимо вручную добавить созданные файлы в процесс сборки. Для этого рекомендуется добавить новый файл в Content целевой объект или None элементы BeforeBuild , как показано в следующем примере:

<Target Name="MyTarget" BeforeTargets="BeforeBuild">
  
  <!-- Some logic that generates your file goes here -->
  <!-- Generated files should be placed in $(IntermediateOutputPath) -->
  <WriteLinesToFile
      File="$(IntermediateOutputPath)GeneratedFile.cs"
      Lines='enum MyEnum { A, B }'
      Overwrite="true" />
    <ItemGroup>
      <Compile Include="$(IntermediateOutputPath)GeneratedFile.cs" />
    </ItemGroup>

  <ItemGroup>
    <!-- If your generated file was placed in `obj\` -->
    <None Include="$(IntermediateOutputPath)GeneratedFile.cs" TargetPath="GeneratedFile.cs" CopyToOutputDirectory="PreserveNewest"/>
    <!-- If you know exactly where that file is going to be, you can hard code the path. -->
    <None Include="some\specific\path\my-generatedfile" CopyToOutputDirectory="PreserveNewest"/>
    
    <!-- If you want to capture "all files of a certain type", you can glob like so. -->
    <None Include="some\specific\path\*.xyz" CopyToOutputDirectory="PreserveNewest"/>
    <None Include="some\specific\path\*.*" CopyToOutputDirectory="PreserveNewest"/>
  </ItemGroup>
</Target>

<Target Name="CleanGeneratedCode" AfterTargets="CoreClean">
  <Delete Files="$(IntermediateOutputPath)GeneratedFile.cs" />
</Target>

Добавление созданного файла None в процесс сборки или Content достаточно для его просмотра. Вы также хотите убедиться, что он добавляется в нужное время. В идеале целевой объект выполняется раньше BeforeBuild. AssignTargetPaths является еще одним возможным целевым объектом, так как это окончательная возможность изменять None и Content элементы (среди других), прежде чем они преобразуются в новые элементы. См. общие типы элементов.

Скопируйте приведенный выше файл, вставьте его в файл и вызовите его buildcodegen.targets. Затем запустите, импортируйте dotnet new consoleфайл и создайте его, чтобы узнать, как это работает.

<Project Sdk="Microsoft.NET.Sdk">
  <Import Project="buildcodegen.targets"/>
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

Запустите msbuild.exe и просмотрите выходные данные, чтобы убедиться, что файл был создан и скопирован в выходную папку. Вы можете использовать ildasm.exe для подтверждения того, что выходные двоичные файлы включают созданный код MyEnum:

ildasm CodeGen.dll

Дальнейшие шаги

Этот пример можно улучшить для поддержки более реалистичных вариантов использования. Например, для поддержки добавочных сборок , когда созданный код зависит от входного файла и InputsOutputs должен быть предоставлен целевому объекту. Такой целевой объект будет повторно создавать файл, только если дата входного файла или файлов является более последней, чем выходной файл. Часто при настройке для создания кода рекомендуется создать настраиваемую задачу. См. статью "Создание настраиваемой задачи" для создания кода.