当前位置: 首页 > news >正文

在项目文件 / MSBuild / NuGet 包中编写扩展编译的时候,正确使用 props 文件和 targets 文件

.NET 扩展编译用的文件有 .props 文件和 .targets 文件。不给我选择还好,给了我选择之后我应该使用哪个文件来编写扩展编译的代码呢?


如果你不了解 .props 文件或者 .targets 文件,可以阅读下面的博客:

  • 理解 C# 项目 csproj 文件格式的本质和编译流程 - walterlv

具体的例子有下面这些博客。不过大概阅读一下就好,这只是 .props 和 .targets 文件的一些应用。文章比较长,你可以考虑稍后阅读。

  • 从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目) - walterlv
  • 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包 - walterlv
  • 如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - walterlv

当我们创建的 NuGet 包中包含 .props 和 .targets 文件的时候,我们相当于在项目文件 csproj 的两个地方添加了 Import 这些文件的代码。

<Project Sdk="Microsoft.NET.Sdk">

  <!-- 本来是没有下面这一行的,我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 -->
  <Import Project="$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.props" Condition="Exists('$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.props')" />

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0</TargetFrameworks>
  </PropertyGroup>

  <!-- 本来是没有下面这一行的,我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 -->
  <Import Project="$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.targets" Condition="Exists('$(NuGetPackageRoot)walterlv.samplepackage\0.8.3-alpha\build\Walterlv.SamplePackage.targets')" />

</Project>

如果你安装的多份 NuGet 包都带有 .props 和 .targets 文件,那么就相当于帮助你 Import 了多个:

<Project Sdk="Microsoft.NET.Sdk">

  <!-- 本来是没有下面这一行的,我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 -->
  <Import Project="$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.props" Condition="Exists('$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.props')" />
  <Import Project="$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.props" Condition="Exists('$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.props')" />

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>netcoreapp3.0</TargetFrameworks>
  </PropertyGroup>

  <!-- 本来是没有下面这一行的,我只是为了说明 NuGet 相当于帮我们添加了这一行才假装写到了这里。 -->
  <Import Project="$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.targets" Condition="Exists('$(NuGetPackageRoot)walterlv.samplepackage1\0.8.3-alpha\build\Walterlv.SamplePackage1.targets')" />
  <Import Project="$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.targets" Condition="Exists('$(NuGetPackageRoot)walterlv.samplepackage2\0.5.1-beta\build\Walterlv.SamplePackage2.targets')" />

</Project>

于是,什么代码写到 .props 里而什么代码写到 .targets 里就一目了然了:

  1. 如果你是定义属性或者为属性设置初值,那么请写到 .props 里面
    • 这样,所有的 NuGet 包或者扩展的编译流程都将可以访问到你设置的属性的值
  2. 如果你是使用属性,或者按条件设置属性,那么请写到 .targets 里面
    • 因为这个时候多数的属性已经初始化完毕,你可以使用到属性的值了
  3. 如果你写的是编译目标(Target),那么请写到 .targets 里面
    • 编译目标是扩展编译的,通常都是使用属性
    • 也会有一些产生属性的,但那都是需要在编译期间产生的属性,其他依赖需要使用 DependsOn 等属性来获取

例如下面的属性适合写到 .props 里面。这是一个设置属性初始值的地方:

<Project>

  <PropertyGroup>
    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>

    <!-- 当生成 WPF 临时项目时,不会自动 Import NuGet 中的 props 和 targets 文件,这使得在临时项目中你现在看到的整个文件都不会参与编译。
       然而,我们可以通过欺骗的方式在主项目中通过 _GeneratedCodeFiles 集合将需要编译的文件传递到临时项目中以间接参与编译。
       WPF 临时项目不会 Import NuGet 中的 props 和 targets 可能是 WPF 的 Bug,也可能是刻意如此。
       所以我们通过一个属性开关 `ShouldFixNuGetImportingBugForWpfProjects` 来决定是否修复这个错误。-->
    <ShouldFixNuGetImportingBugForWpfProjects Condition=" '$(ShouldFixNuGetImportingBugForWpfProjects)' == '' ">True</ShouldFixNuGetImportingBugForWpfProjects>

  </PropertyGroup>

</Project>

而下面的属性适合写到 .targets 里面,因为这里使用到了其他的属性:


<Project>

  <PropertyGroup>
    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>

    <!-- 因为这里使用到了 `Configuration` 属性,需要先等到此属性已经初始化完成再使用,否则我们会拿到非预期的值。 -->
    <ShouldOptimizeDebugging> Condition=" '$(Configuration)' == 'Debug' ">True</ShouldOptimizeDebugging>

  </PropertyGroup>

</Project>

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • 如何给 Windows Terminal 增加一个新的终端(以 Bash 为例)
  • 在 Visual Studio 中设置当发生某个特定异常或所有异常时中断
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • 如何在 Windows 10 中安装 WSL2 的 Linux 子系统
  • 如何安装和准备 Visual Studio 扩展/插件开发环境
  • 基于 Roslyn 同时为 Visual Studio 插件和 NuGet 包开发 .NET/C# 源代码分析器 Analyzer 和修改器 CodeFixProvider
  • 软件界面中一些易混淆/易用错的界面文案,以及一些约定俗成的文案约定
  • WPF 的 VisualBrush 只刷新显示的视觉效果,不刷新布局范围
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • 使用 Roslyn 分析代码注释,给 TODO 类型的注释添加负责人、截止日期和 issue 链接跟踪
  • 为 NuGet 指定检测的 MSBuild 路径或版本,解决 MSBuild auto-detection: using msbuild version 自动查找路径不合适的问题
  • 解决方案文件 sln 中的项目类型 GUID
  • 两种方法设置 .NET/C# 项目的编译顺序,而不影响项目之间的引用
  • 【刷算法】求1+2+3+...+n
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • 【译】理解JavaScript:new 关键字
  • 3.7、@ResponseBody 和 @RestController
  • AHK 中 = 和 == 等比较运算符的用法
  • Android 架构优化~MVP 架构改造
  • Android系统模拟器绘制实现概述
  • CAP理论的例子讲解
  • create-react-app项目添加less配置
  • ES6语法详解(一)
  • es的写入过程
  • ES学习笔记(12)--Symbol
  • Hexo+码云+git快速搭建免费的静态Blog
  • MYSQL 的 IF 函数
  • SOFAMosn配置模型
  • 成为一名优秀的Developer的书单
  • 大型网站性能监测、分析与优化常见问题QA
  • 解决iview多表头动态更改列元素发生的错误
  • 前嗅ForeSpider采集配置界面介绍
  • 如何借助 NoSQL 提高 JPA 应用性能
  • 使用agvtool更改app version/build
  • 思维导图—你不知道的JavaScript中卷
  • 微信小程序设置上一页数据
  • 学习HTTP相关知识笔记
  • 翻译 | The Principles of OOD 面向对象设计原则
  • ###C语言程序设计-----C语言学习(3)#
  • (11)MATLAB PCA+SVM 人脸识别
  • (层次遍历)104. 二叉树的最大深度
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (一)基于IDEA的JAVA基础12
  • (译)2019年前端性能优化清单 — 下篇
  • ***测试-HTTP方法
  • .gitignore
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET 发展历程
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • /usr/bin/python: can't decompress data; zlib not available 的异常处理
  • @configuration注解_2w字长文给你讲透了配置类为什么要添加 @Configuration注解