Condividi tramite


Personalizzare una compilazione per gestire i file generati

In una determinata compilazione, i file generati durante la compilazione si comportano in modo diverso dai file statici ,ad esempio i file di origine. Per questo motivo, è importante comprendere in che modo MSBuild compila progetti. Le due fasi sono la fase di valutazione e la fase di esecuzione. Durante la fase di valutazione, MSBuild legge il progetto, importa tutto, crea proprietà, espande i glob per gli elementi e configura il processo di compilazione. Durante la fase di esecuzione, MSBuild esegue la compilazione eseguendo destinazioni e attività con i dati analizzati durante la fase di valutazione.

I file generati durante l'esecuzione non esistono durante la fase di valutazione, pertanto non sono inclusi nel processo di compilazione. Per risolvere questo problema, è necessario aggiungere manualmente i file generati nel processo di compilazione. Il modo consigliato per eseguire questa operazione è aggiungere il nuovo file agli elementi Content o None prima del target BeforeBuild, come nell'esempio seguente:

<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>

L'aggiunta del file generato a None o Content è sufficiente per visualizzare il processo di compilazione. Si vuole anche assicurarsi che venga aggiunto al momento giusto. Idealmente, il tuo obiettivo viene eseguito prima di BeforeBuild. AssignTargetPaths è un'altra possibile destinazione, poiché è l'opportunità finale di modificare None e Content elementi (tra gli altri) prima che vengano trasformati in nuovi elementi. Vedere Tipi di elementi comuni.

Copiare l'oggetto precedente, incollarlo in un file e chiamarlo buildcodegen.targets. Eseguire quindi dotnet new console, importare il file e compilarlo per vedere come funziona.

<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>

Eseguire msbuild.exe ed esaminare l'output per verificare che il file sia stato generato e copiato nella cartella di output. È possibile usare ildasm.exe per verificare che i file binari di output includano il codice MyEnumgenerato:

ildasm CodeGen.dll

Passaggi successivi

Questo esempio potrebbe essere migliorato per supportare casi d'uso più realistici. Ad esempio, per supportare compilazioni incrementali quando il codice generato dipende da un file Inputs di input e Outputs deve essere fornito alla destinazione. Tale destinazione rigenera il file solo se la data del file di input o dei file è più recente del file di output. Spesso, quando si personalizza per la generazione di codice, è consigliabile creare un compito personalizzato. Vedere Creare un'attività personalizzata per la generazione di codice.