使用 Directory.Build 来消除项目文件中的重复配置

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 Directory.Build 来消除项目文件中的重复配置相关的知识,希望对你有一定的参考价值。

使用 Directory.Build 来消除项目文件中的重复配置

Intro

如果解决方案里的项目比较多的话,往往会有很多重复的项目属性,通常我们可以使用独立的 props 属性文件来配置公用的属性,而一般的属性文件都需要手动的 Import 到项目文件中,而 MSBuild 支持自动导入我们要介绍的 Directory.Build.props中的配置,所以通常我们可以使用 Directory.Build.props 来减少项目中重复的属性

DirectoryBuild

什么是 Directory.Build 呢?

Directory.Build 文件是放在某一个目录下,其中的配置针对这个目录下所有的项目都生效,并且 MSBuild 运行的时候会自动引入,不需要显式地在项目文件中引入,主要分成 Directory.Build.propsDirectory.Build.targets 两类,在很多开源项目包括微软的开源项目都有用到这个来简化项目配置,.props 文件是属性文件,通常定义公用的属性,导入时机较早,项目中的定义可以覆盖掉其中的配置,.targets 是目标文件,通常定义一些自定义的 MSBuild Task,导入时间稍后,可以用来覆盖项目文件中的定义

通常我只是用到 Directory.Build.props 来配置公用的属性

下面是一个项目文件中的一部分:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>

上面 TargetFramwork/Nullable/ImplicitUsings 是一个 MSBuild 属性,需要声明在 PropertyGroup

如果一个解决方案中有很多个项目,那么这些可能就都是重复项了,这时候使用 Directory.Build.props 就会比较方便,比如说 ImplicitUsing 这一属性,这个属性是用来启用 .NET 6 里的隐式命名空间引用的,但是从 .NET 6 RC1 默认是禁用的,需要显式声明 <ImplicitUsings>enable</ImplicitUsings> 来启用,如果项目里的项目都是需要启用的,那就可以直接声明在 Directory.Build.props 中,放在项目根目录下,如下所示:

<Project>
    <PropertyGroup>
        <LangVersion>latest</LangVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>
</Project>

除了 PropertyGroup 之外,我们也可以定义 <ItemGroup>,下面就是一个示例:

<Project>
    <PropertyGroup>
        <LangVersion>latest</LangVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>
    <ItemGroup>
        <Using Include="WeihanLi.Common"/>
        <Using Include="WeihanLi.Common.Helpers"/>
        <Using Include="WeihanLi.Extensions"/>
    </ItemGroup>
</Project>

这样我们可以把公用的命名空间定义在 Directory.Build.props 上, MSBuild 会按目录结构依次寻找上层的 Directory.Build.props,找到第一个之后就会停止再往上寻找,下面是一个示例

c:\\users\\username\\code\\test\\case1
c:\\users\\username\\code\\test
c:\\users\\username\\code
c:\\users\\username
c:\\users
c:\\

如果项目较多可以使用多层 Directory.Build.props,下面是一个示例:

\\
  MySolution.sln
  Directory.Build.props     (1)
  \\src
    Directory.Build.props   (2-src)
    \\Project1
    \\Project2
  \\test
    Directory.Build.props   (2-test)
    \\Project1Tests
    \\Project2Tests

所有项目 (1) 的通用属性、src 项目 (2-src) 的通用属性,以及 test 项目 (2-test) 的通用属性

前面我们提到过,MSBuild 对于指定项目找到第一个 Directory.Build.props 就会停止再向上寻找 Directory.Build.props 如果要同时使用多级 Directory.Build.props 则需要显式导入上层的 Directory.Build.props

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

Sample

下面我们来看一些使用示例吧

Sample1

在稍微复杂一些项目,往往会引用很多的 Nuget 包,而一些项目的版本可能是有关系的,有些包的版本是一致的,此时我们可以考虑定义一个属性,而在包版本的地方则引用这个属性

项目根目录下 Directory.Build.props 内容

<PropertyGroup>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <EFVersion>6.0.0-rc.1.*</EFVersion>
</PropertyGroup>

项目文件中内容:

<ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(EFVersion)" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(EFVersion)" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(EFVersion)" />
</ItemGroup>

这样我们如果需要更新 EF 的版本,我们只需要更新 Directory.Build.props 文件中的 EFVersion 这个属性就可以了,详细配置可以参考:https://github.com/WeihanLi/WeihanLi.EntityFramework

Sample2

我们再来看一个多个层级使用的示例,项目结构如下所示:

Directory.Build.props
--src
--tests
--Directory.Build.props
----UnitTest
----IntegrationTest

项目根目录下 Directory.Build.props,主要定义 package 相同的属性配置以及启用可控引用类型,隐式命名空间和公用的命名空间

<Project>
    <PropertyGroup>
        <LangVersion>latest</LangVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <PackageIcon>icon.png</PackageIcon>
        <PackageTags>HTTPie http https curl rest</PackageTags>
        <GenerateDocumentationFile>false</GenerateDocumentationFile>
        <PackageLicenseExpression>MIT</PackageLicenseExpression>
        <RepositoryType>git</RepositoryType>
        <RepositoryUrl>https://github.com/WeihanLi/dotnet-httpie</RepositoryUrl>
        <PackageProjectUrl>https://github.com/WeihanLi/dotnet-httpie</PackageProjectUrl>
        <Product>dotnet-HTTPie</Product>
        <Authors>WeihanLi</Authors>
        <PackageReleaseNotes>https://github.com/WeihanLi/dotnet-httpie/tree/main/docs/ReleaseNotes.md</PackageReleaseNotes>
        <Copyright>Copyright 2021 (c) WeihanLi</Copyright>
    </PropertyGroup>
    <ItemGroup>
        <Using Include="WeihanLi.Common"/>
        <Using Include="WeihanLi.Common.Helpers"/>
        <Using Include="WeihanLi.Extensions"/>
    </ItemGroup>
</Project>

tests 目录下 Directory.Build.props,定义了测试项目公用的命名空间,并引入了上一层 Directory.Build.props 中的属性

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

IntegrationTest 项目文件,引入自己需要的命名空间引用

<ItemGroup>
    <Using Include="HTTPie"/>
    <Using Include="HTTPie.Implement"/>
    <Using Include="HTTPie.Middleware"/>
    <Using Include="HTTPie.Models"/>
    <Using Include="HTTPie.Utilities"/>
</ItemGroup>

具体配置可以参考:https://github.com/WeihanLi/dotnet-httpie

More

使用好 Directory.Build.props 可以简化项目配置,使得项目更容易维护,如果还没用起来的可以尝试一下哈

References

  • https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?WT.mc_id=DT-MVP-5004222&view=vs-2019

  • https://github.com/WeihanLi/SparkTodo/blob/master/Directory.Build.props

  • https://github.com/WeihanLi/SamplesInPractice/tree/master/net6sample

  • https://github.com/WeihanLi/WeihanLi.EntityFramework/blob/dev/Directory.Build.props

  • https://github.com/WeihanLi/dotnet-httpie/blob/dev/Directory.Build.props

  • https://github.com/WeihanLi/dotnet-httpie/blob/dev/tests/Directory.Build.props

以上是关于使用 Directory.Build 来消除项目文件中的重复配置的主要内容,如果未能解决你的问题,请参考以下文章

当解决方案包含框架和核心项目时,Directory.Build.props 不起作用

将 .csproj 的宏导入 Directory.build.targets 文件?

如何在多个应用程序中共享日志配置

消除textarea的空格de长度值

中型WPF客户端开发项目总结(3.1) - 简述项目结构

在终端上使用 xcodebuild 命令导出失败