Directory.Build.props - Centralize suas compilações

Imagem de capa Directory.Build.props - Centralize suas compilações

Directory.Build.props - Centralize suas compilações

O Directory.Build.props funciona quase como um arquivo csproj "normal". Você pode definir dependências padrão ou até mesmo aplicar usos implícitos. Isso é, por exemplo, muito conveniente quando você tem um projeto de teste no qual sempre deseja importar o namespace Moq e XUnit devido à sua configuração.

Como funciona?

É simples. O MSBuild passará do seu caminho csproj atual para cima até a hierarquia de arquivos e selecionará o primeiro Mais para o Directory.Build.props posterior que puder encontrar e o incluirá. Dê uma olhada na seguinte estrutura de arquivos:

src/
?? Infrastructure/
?  ?? Infrastructure.csproj
?? Tools/
?  ?? Tools.csproj
?? Web/
?  ?? Web.csproj
?? Directoy.Build.props

Sob nosso src está diretamente o Directory.Build.props e então temos algumas pastas que contém os projetos "reais". Esses projetos "reais" importam automaticamente nosso Directory.Build.props.

Vejamos um exemplo:

<?xml version="1.0" encoding="utf-8"?>
<Project  xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
        <ItemGroup>
      <PackageReference  Include="SonarAnalyzer.CSharp"  Version="8.35.0.42613">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      </PackageReference>
      <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      </PackageReference>
    </ItemGroup>
   
    <ItemGroup>
        <AdditionalFiles  Include="$(SolutionDir)\stylecop.json"  Link="stylecop.json" />
    </ItemGroup>
    <PropertyGroup>
         <CodeAnalysisRuleSet>$(SolutionDir)\stylecop.analyzers.ruleset</CodeAnalysisRuleSet>
    </PropertyGroup>
    
    <PropertyGroup>
        <WarningLevel>5</WarningLevel>
        <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>

</Project>

Isso é tirado diretamente do repositório do meu blog no github: aqui

Portanto, em nosso caso, Infrastructure.csproj, Web.csproj e Tools.csproj incluirão automaticamente SonarAnalyzer.CSharp e StyleCop.Analyzers como pacote nuget. Se eu atualizar a versão no meu Directory.Build.props, todos eles serão atualizados automaticamente!

Outro exemplo: Digamos que você tenha a seguinte configuração de teste:

tests/
?? IntegrationTests/
?  ?? IntegrationTest.csproj
?? TestUtilities/
?  ?? TestUtilities.csproj
?? UnitTests/
?  ?? UnitTest.csproj
?? Directory.Build.props

Como a natureza dos testes, você provavelmente usará uma estrutura de teste em todos os seus testes, além da mesma estrutura de simulação. Eu pessoalmente amo o XUnit por várias razões e o Moq como biblioteca de simulação. Portanto, quero usá-los em todos os meus projetos de teste. Então meu Directory.Build.props ficaria assim:

<?xml version="1.0" encoding="utf-8"?>
<Project  xmlns="https://schemas.microsoft.com/developer/msbuild/2003">

   <ItemGroup>
      <PackageReference  Include="FluentAssertions"  Version="6.5.1" />
      <PackageReference  Include="Microsoft.NET.Test.Sdk"  Version="17.2.0-preview-20220131-20" />
      <PackageReference  Include="Moq"  Version="4.16.1" />
      <PackageReference  Include="xunit"  Version="2.4.2-pre.12" />
    </ItemGroup>
   
    <ItemGroup Label="Implicit usings">
      <Using  Include="FluentAssertions" />
      <Using  Include="Moq" />
      <Using  Include="Xunit" />
    </ItemGroup>

</Project>

Tirado daqui

Super conveniente, não é?

Agora vamos dizer que o projeto Tools não precisa de todos esses includes e outras coisas ou em geral: Como posso excluir um projeto?. Isso é bem fácil, pois estamos usando a sintaxe normal que usaríamos em um csproj: Condition. Tomando o exemplo acima, vamos excluir o TestUtilities.csproj:

<?xml version="1.0" encoding="utf-8"?>
<Project  xmlns="https://schemas.microsoft.com/developer/msbuild/2003">

   <ItemGroup  Condition="$(MSBuildProjectName) != 'TestUtilities'">
     <PackageReference  Include="FluentAssertions"  Version="6.5.1" />
     <PackageReference  Include="Microsoft.NET.Test.Sdk"  Version="17.2.0-preview-20220131-20" />
     <PackageReference  Include="Moq"  Version="4.16.1" />
     <PackageReference Include="xunit" Version="2.4.2-pre.12" />
   </ItemGroup>
   
   <ItemGroup  Label="Implicit usings"  Condition="$(MSBuildProjectName) != 'TestUtilities'">
     <Using Include="FluentAssertions" />
     <Using  Include="Moq" />
     <Using Include="Xunit" />
   </ItemGroup>

</Project>

Também muito simples. Basta adicionar o nome do seu projeto sem a extensão de arquivo "csproj".

Vários diretórios.Build.props

O que acontece se tivermos vários Directory.Build.props?

tests/
?? IntegrationTests/
?  ?? IntegrationTest.csproj
?? TestUtilities/
?  ?? TestUtilities.csproj
?? UnitTests/
?  ?? UnitTest.csproj
?? Directory.Build.props
Directory.Build.props

Agora temos outro Directory.Build.props um nível acima da pasta "tests". Bem, este não será incluído automaticamente. Você deve informar ao MSBuild que ele deve continuar a varredura. Você pode fazer isso adicionando isso ao Directory.Build.props interno (aquele em "testes").

<Import  Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

Conclusão

O Directory.Build.props oferece uma ferramenta útil para centralizar dependências e configurações comuns.

Leia mais aqui na documentação da Microsoft