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

如何在 MSBuild 中正确使用 % 来引用每一个项(Item)中的元数据

MSBuild 中写在 <ItemGroup /> 中的每一项是一个 ItemItem 除了可以使用 Include/Update/Remove 来增删之外,还可以定义其他的元数据(Metadata)。

使用 % 可以引用 Item 的元数据,本文将介绍如何正确使用 % 来引用每一个项中的元数据。


本文内容

    • 定义 Item 的元数据
    • 引用元数据
    • 使用元数据
    • 关于项元数据的其他信息

定义 Item 的元数据

就像下面这样,当引用一个 NuGet 包时,可以额外使用 Version 来指定应该使用哪个特定版本的 NuGet 包。这里的 VersionPrivateAssets 就是 PackageReference 的元数据。

<ItemGroup>
    <PackageReference Include="dotnetCampus.Configurations.Source" Version="1.0.0" PrivateAssets="All" />
    <PackageReference Include="dotnetCampus.CommandLine.Source" Version="1.2.1" PrivateAssets="All" />
    <PackageReference Include="Walterlv.Win32.Source" Version="0.12.2-alpha" PrivateAssets="All" />
    <PackageReference Include="Walterlv.IO.PackageManagement.Source" Version="0.13.2-alpha" PrivateAssets="All" />
</ItemGroup>

我们随便创建一个新的 Item,也可以定义自己的元数据。

<ItemGroup>
    <_WalterlvItem Include="欢迎访问" Url="https://" />
    <_WalterlvItem Include="吕毅的博客" Url="blog.walterlv.com" />
</ItemGroup>

引用元数据

引用元数据使用的是 % 符号。

<Target Name="_WalterlvDemo" AfterTargets="AfterBuild">
    <ItemGroup>
        <_WalterlvItem Include="欢迎访问" Url="https://" />
        <_WalterlvItem Include="吕毅的博客" Url="blog.walterlv.com" />
    </ItemGroup>
    <Message Text="@(_WalterlvItem):%(Url)" />
</Target>

虽然这里我们只写了一个 Message Task,但是最终我们会输出两次,每一个 _WalterlvItem 项都会输出一次。下面是这段代码的输出:

_WalterlvDemo:
  欢迎访问:https://
  吕毅的博客:blog.walterlv.com

当你使用 % 的时候,会为每一个项执行一次这行代码。当然,如果某个 Task 支持传入集合,那么则可以直接收到集合。

如果你不是用的 Message,而是定义一个其他的属性,使用 @(_WalterlvItem):%(Url) 作为属性的值,那么这个属性也会为每一个项都计算一次值。当然最终这个属性的值就是最后一项计算所得的值。

也许可以帮你回忆一下,如果我们不写 %(Url) 会输出什么。当只输出 @(WalterlvItem) 的时候,会以普通的分号分隔的文字。

<Target Name="_WalterlvDemo" AfterTargets="AfterBuild">
    <ItemGroup>
        <_WalterlvItem Include="欢迎访问" Url="https://" />
        <_WalterlvItem Include="吕毅的博客" Url="blog.walterlv.com" />
    </ItemGroup>
    <Message Text="@(_WalterlvItem)" />
</Target>

会输出:

_WalterlvDemo:
  欢迎访问;吕毅的博客

使用元数据

如果你希望自己处理编译过程,那么可能会对元数据做更多的处理。

为了简单说明 % 的用法,我将已收集到的所有的元数据和它的本体一起输出到一个文件中。这样,后续的编译过程可以直接使用这个文件来获得所有的项和你希望关心它的所有元数据。

<PropertyGroup>
    <_WalterlvContentArgsFilePath>$(IntermediateOutputPath)Args\Content.txt</_WalterlvContentArgsFilePath>
    <_WalterlvToolFile>$(MSBuildThisFileDirectory)..\bin\compile.exe</_WalterlvContentArgsFilePath>
</PropertyGroup>

<Target Name="_WalterlvDemo" AfterTargets="AfterBuild">
    <ItemGroup>
        <_WalterlvContentFileLine Include="@(Content)" Line="@(Content)|%(Content.PublishState)|%(Content.CopyToOutputDirectory)" />
    </ItemGroup>
    <WriteLinesToFile File="$(_WalterlvContentArgsFilePath)" Lines="%(_WalterlvContentFileLine.Line)" Overwrite="True" />
    <Exec ConsoleToMSBuild="True"
          Command="&quot;$(_WalterlvToolFile)&quot; PackContent --content-file &quot; $(_WalterlvContentArgsFilePath) &quot;" />
</Target>

这段代码的含义是:

  1. 定义一个文件路径,这个路径即将用来存放所有 Content 项和它的元数据;
  2. 定义一个工具路径,我们即将运行这个路径下的命令行程序来执行自定义的编译;
  3. 收集所有的 Content 项,然后把所有项中的 PublishStateCopyToOutputDirectory 一起拼接成这个样子:
    • Content|PublishState|CopyToOutputDirectory
  4. 写文件,将以上拼接出来的每一项写入到文件中的每一行;
  5. 执行工具程序,这个程序将使用这个文件来执行自定义的编译。

关于使用 exe 进行自定义编译的部分可以参考我的另一篇博客:

  • 如何创建一个基于命令行工具的跨平台的 NuGet 工具包 - walterlv

关于写文件的部分可以参考我的另一篇博客:

  • 在 MSBuild 编译过程中操作文件和文件夹(检查存在/创建文件夹/读写文件/移动文件/复制文件/删除文件夹) - walterlv

关于项元数据的其他信息

一些已知的元数据:

  • MSBuild Well-known Item Metadata - Visual Studio - Microsoft Docs

参考资料

  • MSBuild Items - Visual Studio - Microsoft Docs

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

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

知识共享许可协议

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

相关文章:

  • 如何将一个 .NET 对象序列化为 HTTP GET 的请求字符串
  • 屏幕边缘上有趣的 1 个像素,看不见、摸不到
  • 在 MSBuild 编译过程中操作文件和文件夹(检查存在/创建文件夹/读写文件/移动文件/复制文件/删除文件夹)
  • 在 WPF 程序中应用 Windows 10 真•亚克力效果
  • 推荐 .NET/C# 开发者安装的几款代码分析插件或对应的代码分析 NuGet 包
  • 在 HTML 超链接上添加可交互的 ToolTip
  • 在移动端打开 Google 的网页快照
  • 为自己搭建的博客添加可切换的暗色和亮色主题
  • 如何编写基于 Microsoft.NET.Sdk 的跨平台的 MSBuild Target(附各种自带的 Task)
  • 让你编写的控件库在 XAML 中有一个统一的漂亮的命名空间(xmlns)和命名空间前缀
  • Sdk 风格的 csproj 对 WPF/UWP 支持不太好?有第三方 SDK 可以用!MSBuild.Sdk.Extras
  • 为博客或个人站点的 Markdown 添加 LaTeX 公式支持
  • 如何让 .NET Core 命令行程序接受密码的输入而不显示密码明文
  • 如何编写 WPF 的标记扩展 MarkupExtension,即便在 ControlTemplate/DataTemplate 中也能生效
  • PasswordVault —— 在 UWP 应用中安全地保存密码
  • ----------
  • ES学习笔记(12)--Symbol
  • EventListener原理
  • HashMap ConcurrentHashMap
  • learning koa2.x
  • magento 货币换算
  • passportjs 源码分析
  • SQLServer之创建数据库快照
  • STAR法则
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 手写双向链表LinkedList的几个常用功能
  • 应用生命周期终极 DevOps 工具包
  • const的用法,特别是用在函数前面与后面的区别
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​Linux·i2c驱动架构​
  • $.ajax,axios,fetch三种ajax请求的区别
  • (JS基础)String 类型
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET开源快速、强大、免费的电子表格组件
  • .net中的Queue和Stack
  • .考试倒计时43天!来提分啦!
  • @FeignClient 调用另一个服务的test环境,实际上却调用了另一个环境testone的接口,这其中牵扯到k8s容器外容器内的问题,注册到eureka上的是容器外的旧版本...
  • [.net] 如何在mail的加入正文显示图片
  • [.net]官方水晶报表的使用以演示下载
  • [2013][note]通过石墨烯调谐用于开关、传感的动态可重构Fano超——
  • [AIGC] Java 和 Kotlin 的区别
  • [asp.net core]project.json(2)
  • [BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)
  • [java进阶]——方法引用改写Lambda表达式
  • [JS7] 显示从0到99的100个数字
  • [Linux]进程创建➕进程终止
  • [Linux]----文件操作(复习C语言+文件描述符)
  • [NET].NET Framework 3.5 SP1 真正的离线安装(转)
  • [OGRE]看备注学编程(02):打地鼠01-布置场地九只地鼠
  • [Open3d]: 知识记录
  • [Rust] 使用vscode实现HelloWorld程序并进行debug
  • [Unity3D]深度相机 Depth Camera