MSBuild入门(续)

2023-11-08

MSBuild基本概念(续)

在上一篇简单的介绍了下MSBuild中的四个基本块,每块介绍比较单薄,在这里对在大多数的项目模版生成的*.*proj文件中比较常见一些用法和概念做些补充。主要有一下几方面:

MSBuild特殊字符:MSBuild保留的一些字符,以及XML中的特殊字符处理。
MSBuild条件: Condition特性,作用类似于C#的if。
MSBuild属性: 使用环境变量、保留属性、全局属性。
MSBuild项: 元数据、项转换。
MSBuild任务: ITask接口、UsingTask[自定义任务]、ContinueOnError。
MSBuild目标: 初始目标、默认目标、目标依赖项。
Import元素: 导入项目文件到当前项目文件。

MSBuild特殊字符

一些字符在MSBuild中代表着特殊的上下文含义,如下:

MSBuild的特殊字符[%引用元数据]、[$引用属性]、[@引用项]、['条件或其他表达式]、[;列表分隔符]、[?文件名通配符]、[*文件名通配符];
XML的保留字符:<、>、&、"、'

针对MSBuild的特殊字符转义需要用[%xx]这种方式,xx代表字符的ASCII十六进制值([%=%25][$=%24][@=%40]['=%27][;=%3B][?=%3F][*=%2A])。针对XML保留字符则使用&lt这种方式。 一般用到这些特殊字符的情况不多,见到时能知道是转义就可以了。

MSBuild条件

条件在*.*proj项目文件中非常常见,用Condition特性来表示一个布尔表达式,类似于if条件,几乎所有的元素都可以具有Conditon特性。一个简单的例子如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!--condition.xml文件-->
 3 <Project DefaultTargets="show" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 4   <PropertyGroup>
 5     <!--Condition在属性、项、任务、目标生都有使用-->
 6     <!--如果Configuration为空(''),则其值为Debug-->
 7     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
 8     <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
 9   </PropertyGroup>
10   <ItemGroup>
11     <!--如果csfile1.cs文件存在就包含在CSFile项中-->
12     <CSFile Include="csfile1.cs" Condition="Exists('csfile1.cs')"></CSFile>
13   </ItemGroup>
14   <Target Name="show">
15     <!--输出Debug|x86-->
16     <Message Text="$(Configuration)|$(Platform)"/>
17     <!--输出空,因为csfile1.cs并不存在-->
18     <Message Text="@(CSFile)"/>
19   </Target>
20 </Project>

还有一些常用的表达式如!=、!、And、Or等。

MSBuild属性

上篇介绍到可以用$可以引用自定义的属性,除此之外亦可以引用系统的环境变量,如$(Path),以及 MSBuild保留属性(MSDN)

属性除了可以在项目文件中声明是赋值外,在MSBuild命令行也允许设置属性的值(语法:/p:propertyName=value)。称作全局属性,这类属性会重写在项目文件中设置的属性值,保留属性除外的任何属性都可被这种方式覆盖其原值。 以上面示例为基础:[MSBuild condition.xml /p:Platform=x64],则最终输出结果就为Debug|x64了。

属性还有一种叫做任务发出属性,在上篇用到了,由Output元素的PropertyName特性指定了属性名,这类属性不像一般的声明式属性那样赋值,而是动态得到的值。是在项目文件中很常见的用法。

MSBuild项

项大都是用来引入文件用的,而文件会有一些附加信息,比如版本,语言等,而这些附加信息在项目文件中是以项的子元素的出现的,称为项的元数据。元数据是键/值的形式存储的,声明方式和属性相同。

1   <ItemGroup>
2     <!--如果csfile1.cs文件存在就包含在CSFile项中-->
3     <CSFile Include="csfile1.cs">
4       <!--声明元数据,必须为项的一级子元素-->
5       <!--引用方式:%(CSFile.Culture)-->
6       <Culture>zh-cn</Culture>
7     </CSFile>
8   </ItemGroup>

除了自定义的一些元数据外,系统还提供一些隐式存在的元数据,即不用声明即可使用,具体可参见MSBuild常见的已知元数据。引用这类元数据的语法和自定义的完全相同。

项转换允许把一个项的列表与另一个列表一一变换。比如下面的例子:

 1 <?>
 5     <CSFile Include="1.cs;2.cs"/>
 6     <!--%(Filename)为项的元数据,由系统提供-->
 7     <VBFile Include="@(CSFile->'%(Filename).vb')"/>
 8   </ItemGroup>
 9   <Target Name="show">
10     <!--输出1.cs;2.cs-->
11     <Message Text="@(CSFile)"/>
12     <!--输出1.vb;2.vb-->
13     <Message Text="@(VBFile)"/>
14   </Target>
15 </Project>

MSBuild任务

从上篇中我们对任务的认识是它是一个原子操作,用来执行某一项逻辑处理,但是xml格式的项目文件是没有这个处理能力的,所以这些任务都是映射到.NET类库中的一些类,由这些类来处理操作中的逻辑。具体来说都是实现ITask接口的类,ITask接口位于Microsoft.Build.Framework命名空间。当然我们也可以实现自己的任务类,直接实现ITask接口或者继承自Task(此抽象类实现了ITask接口的部分功能,可简化自定义任务类的编写,留出一个Execute抽象方法供子类重写自己的任务逻辑)。然后通过UsingTask元素映射到出一个任务元素。我就继承Task写一个简单的示例:

 1 //AddTwoNumberTask.cs,需编译为dll
 2 using System;
 3 using Microsoft.Build.Utilities;
 4 using Microsoft.Build.Framework;
 5  
 6 /// <summary>
 7 /// 继承Task,任务逻辑是处理加法
 8 /// </summary>
 9 public class AddTwoNumberTask : Task
10 {
11     /// <summary>
12     /// 定义一个加数,
13     /// 如果一个输入属性被要求必须输入,则用[Required]特性标识该属性
14     /// </summary>
15     public String Number1 { get; set; }
16     /// <summary>
17     /// 定义另一个加数
18     /// </summary>
19     public String Number2 { get; set; }
20     public override bool Execute()
21     {
22         this.Sum = (Int32.Parse(this.Number1) + Int32.Parse(this.Number2)).ToString();
23         return true;
24  
25     }
26     /// <summary>
27     /// 定义一个输出参数,使用Output特性修饰该属性
28     /// </summary>
29     [Output]
30     public String Sum { get; set; }
31 }
 然后编写如下项目文件,放于AddTwoNumberTask.cs同目录下:
 1 <!--buildAddTaskDll.csproj-->
 2 <?xml version="1.0" encoding="utf-8"?>
 3 <!--从AddTwoNumberTask.cs源文件到编译成dll-->
 4 <Project DefaultTargets="buildAddTaskDll"  ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 5 <PropertyGroup>
 6     <OutputType>Library</OutputType>
 7 </PropertyGroup>
 8 <ItemGroup>
 9     <Reference Include="Microsoft.Build.Framework" />
10     <Reference Include="Microsoft.Build.Utilities.v4.0" />
11     <Reference Include="System" />
12 </ItemGroup>
13 <ItemGroup>
14     <Compile Include="AddTwoNumberTask.cs" />
15 </ItemGroup>
16 <Target Name="buildAddTaskDll">
17     <!--@(Reference->'$(MSBuildBinPath)\%(Identity).dll')表示项转换-->
18     <Csc Sources="@(Compile)"
19     References="@(Reference->'$(MSBuildBinPath)\%(Identity).dll')"
20         TargetType="$(OutputType)">
21     </Csc>
22 </Target>
23 </Project>

用MSBuild编译buildAddTaskDll.csproj项目文件。得到AddTwoNumberTask.dll程序集。再编写一个项目文件usingtask如下:

<?xml version="1.0" encoding="utf-8"?>
<!--使用自定义的任务做加法-->
<Project DefaultTargets="show"  ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="AddTwoNumberTask" AssemblyFile = "AddTwoNumberTask.dll"/>
  <Target Name="show">
    <AddTwoNumberTask Number1="1" Number2="2" >
      <Output TaskParameter="Sum" PropertyName="SumValue"/>
    </AddTwoNumberTask>
    <!--输出3-->
    <Message Text="$(SumValue)"/>
  </Target>
</Project>

如果仔细看AddTwoNumberTask.cs文件就会发现

//如果Number1或者2不是数字,则此任务就会抛异常了 
this.Sum = (Int32.Parse(this.Number1) + Int32.Parse(this.Number2)).ToString();

那么如果<AddTwoNumberTask Number1="1" Number2="2" >在这里加一个ContinueOnError=“true”,则表示会忽略掉逻辑处理中的错误,继续运行,否则会终止执行后续任务。如果任务有输出参数的话,Output元素总是作为任务的子元素出现,作为一个中间桥梁把任务的输出传输到属性或者项中。

MSBuild目标

Project根元素代表者一个项目文件,上面的例子我都会写一个DefaultTargets特性来指定该项目文件要执行的默认目标是哪一个。其实此特性是可选的,也是可以用分号分割写多个的,执行顺序依据书写顺序来判定,也可通过MSBuild命令行参数来传递:

msbuild /target:Build1;Build2

除此之外,Project元素还有一个可选特性InitialTargets,也支持多个目标。如果这两个特性都没有,则MSBuild先执行它遇到的第一个Target。Target有一个DependsOnTargets特性表示当前目标依赖另一个目标,效果就是DependsOnTargets特性指定的目标先于当前目标执行。这绕来绕去好多先后顺序关系,写一个示例看看吧。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!--目标执行顺序-->
 3 <!--如果InitialTargets特性存在,则首先执行此目标列表-->
 4 <!--如果DefaultTargets特性存在,则继续执行此目标列表-->
 5 <Project InitialTargets="B1;B2" DefaultTargets="B3;B4"  
 6          ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 7   <!--如果发现Target具有DependsOnTargets特性-->
 8   <!--则先执行DependsOnTargets指定的目标-->
 9   <!--MSBuild4新加入了RunBeforeTargets和RunAfterTargets特性-->
10   <!--其作用和DependsOnTargets类似,一前一后,不做演示了-->
11   <Target Name="B1" DependsOnTargets="B5">
12     <Message Text="B1"/>
13   </Target>
14   <Target Name="B2">
15     <Message Text="B2"/>
16   </Target>
17   <Target Name="B3">
18     <Message Text="B3"/>
19   </Target>
20   <Target Name="B4">
21     <Message Text="B4"/>
22   </Target>
23   <Target Name="B5">
24     <Message Text="B5"/>
25   </Target>
26   <!--结果为:B5 B1 B2 B3 B4-->
27 </Project> 

Import元素

项目模版产生的*.*proj项目文件大量的使用这个元素,用来导入可重用的项目文件,其中最常见的一个应该是这个吧,如果你用C#开发的话。

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

MSBuildToolsPath或者是MSBuildBinPath,Project特性指定要导入的项目文件。Import元素像是一个占位元素,MSBuild在执行到此时会用*.targets替换掉此元素,就像本来就声明在这里一样,所以和*.targets文件有关的所有保留属性会被重置。 Import元素对导入文件的扩展名无要求,文件是正确的项目文件就行,但一般约定为*.targets。

总结和备注

了解了以上知识点后,阅读一般的项目模版生成的项目文件(*.*proj)应该是可以的了,下篇文章先认识几个重要的*.targets,为剖析项目文件做准备。

备注:针对项目文件中所指的“特性”是表示一个xml元素的“属性”。由于属性在MSBuild中有特殊含义,则MSDN文档一律把项目文件中的xml属性称作是特性,比如Message任务的Text特性。如有错误之处,欢迎指正!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MSBuild入门(续) 的相关文章

  • UTF8/UTF16 和 Base64 在编码方面有什么区别

    In c 我们可以使用下面的类来进行编码 System Text Encoding UTF8 System Text Encoding UTF16 System Text Encoding ASCII 为什么没有System Text En
  • ROWNUM 的 OracleType 是什么

    我试图参数化所有现有的 sql 但以下代码给了我一个问题 command CommandText String Format SELECT FROM 0 WHERE ROWNUM lt maxRecords command CommandT
  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • 在 Xamarin Android 中将图像从 URL 异步加载到 ImageView 中

    我有一个包含多个项目的 ListView 列表中的每个项目都应该有一个与之关联的图像 我创建了一个数组适配器来保存每个列表项并具有我希望加载的图像的 url 我正在尝试使用 Web 请求异步加载图像 并设置图像并在加载后在视图中更新它 但视
  • 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

    最近 我们遇到了一些旧版 WinForms 应用程序 我们需要更新一些新功能 在专家测试该应用程序时 发现一些旧功能被破坏 无效的跨线程操作 现在 在您认为我是新手之前 我确实有一些 Windows 窗体应用程序的经验 我不是专家 但我认为
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • A* 之间的差异 pA = 新 A;和 A* pA = 新 A();

    在 C 中 以下两个动态对象创建之间的确切区别是什么 A pA new A A pA new A 我做了一些测试 但似乎在这两种情况下 都调用了默认构造函数 并且仅调用了它 我正在寻找性能方面的任何差异 Thanks If A是 POD 类
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 我的 strlcpy 版本

    海湾合作委员会 4 4 4 c89 我的程序做了很多字符串处理 我不想使用 strncpy 因为它不会终止 我不能使用 strlcpy 因为它不可移植 只是几个问题 我怎样才能让我的函数正常运行 以确保它完全安全稳定 单元测试 这对于生产来
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反
  • 如何连接字符串和常量字符?

    我需要将 hello world 放入c中 我怎样才能做到这一点 string a hello const char b world const char C string a hello const char b world a b co

随机推荐

  • linux 网卡队列深度,linux 磁盘队列深度nr_requests 和 queue_depth

    nr requests 和 queue depth 修改配置值 nr requests 和 queue depth 区别 iostat 的avgqu sz lsscsi l 的队列大小 iostat nr requests 和 queue
  • MOEA/D 算法详解

    MOEA D 笔记 1 聚合方法 1 1 权重求和法 Weighted Sum Approach 1 2 切比雪夫聚合法 Tchebycheff Approach 1 3 边界交叉法 Boundary Intersection Approa
  • JS:MessageChannel

    MessageChannel API MessageChannel 为通信管道对象 使用 MessageChannel 构造函数将返回一个 MessageChannel 对象 返回的对象中包含两个 MessagePort 对象 可以实现双端
  • 关于numpy中seed随机数种子的使用

    numpy random seed 随机种子生成器 使下一次生成的随机数为由种子数决定的 特定 的随机数 如果seed中参数为空 则生成的随机数 完全 随机 gt gt gt import numpy as np gt gt gt np r
  • 电脑查询域名对应IP的过程

    1 浏览器缓存 当用户通过浏览器访问某域名时 浏览器首先会在自己的缓存中查找是否有该域名对应的IP地址 若曾经访问过该域名且没有清空缓存 便存在 2 系统缓存 当浏览器缓存中无域名对应IP则会自动检查用户计算机系统Hosts文件DNS缓存是
  • 4键电子手表说明书_电子手表怎么调(电子手表的四个键的功能各是什么)

    展开全部 四个功能键分别是 左上角按键e68a84e8a2ad62616964757a686964616f31333433643062LIGHT 右上角按键ST SP 左下角按键MODE 右下角按键RESET 具体操作步骤如下 1 首先 在
  • (干货)微信小程序组件封装

    概述 自己封装的一个比较简单微信弹窗小组件 主要就是教会大家对微信小组件的用法和理解 因为微信小程序对组件介绍特别少 所以我就把自己的理解分享给大家 一前言 相信大家在开发小程序时会遇到某个功能多次使用的情况 比如弹出框 这个时候大家首先想
  • 自动化测试之RobotFramework框架

    自动化测试之RobotFramework框架 很久没更新 因为我跳槽了 之前学习了入门级别性能测试的Jmeter 发现自由度真的差 又想着找一个上限比较高的python测开的工作 所以换到了现在的工作 我们公司使用的是wxpython基于r
  • 学习日记——基于MDK的智慧物流案例开发(2020.2.19)

    准备阶段 开发板 小熊派开发板 提前组装 将 NB卡 NB35 A通信扩展板 E53 ST1GPS模块 IDE LiteOS Stiudio 小熊派的编译环境 平台 华为云账号 开通开发中心的权限 若使用软件开发服务进行应用开发 还需要开通
  • QT drawPixmap和drawImage处理图片模糊问题

    drawPixmap和drawImage显示图片时 如果图片存在缩放时 会出现模糊现象 例如将一个100x100 的图片显示到30x30的区域 这个时候就会出现模糊 如下 实际图片 这个问题就是大图显示成小图造成的像素失真 当我们在1080
  • ctf.show_web12

    f12提示 传参 cmd hightlight file index php 得到源码
  • 【抽五分钟】使用VuePress创建在线文档中心

    文章目录 安装初始化 核心配置 导航栏配置 侧边栏配置 静态资源配置 nginx部署 typora编写 安装初始化 全局安装 npm install g vuepress 创建目录 mkdir vurepress blog 项目初始化 cd
  • 使用 pair 做 unordered_map 的键值

    背景 标准库中 unordered map 是使用哈希表实现的 对于内置类型标准库通过 std hash 提供了哈希函数的实现 因此若采用非内置类型做键值 则需要程序员自己提供其哈希函数的实现 用 pair 做键值 自定义哈希函数 stru
  • Spring-Boot-Admin--快速学习--按应用实例添加标签--08

    代码地址 https gitee com DanShenGuiZu learnDemo tree master springboot admin learn 一 按应用实例添加标签 Tags 是我们区别同一应用的不同实例的方法 1 1 举例
  • 如何用电路区分 OC门与TTL

    这是两个概念 oc门是输出驱动方式 指集电极开路驱动 在电路中如果输出有一个电源到输出端的上拉电阻通常就是OC门 OC门只能灌电流 你说的TTL可能是指没注明的图腾拄驱动 即用不同极性的两个管子推拉驱动 不需要外接电源 高电平时可给负载提供
  • Vue第二篇:概念深度剖析

    参考链接 https www bilibili com video BV1oj411D7jk spm id from 333 788 recommend more video 0 vd source 3969f30b089463e19db0
  • 人生如一趟旅行

    http www putclub com html download life prose 2011 0311 27595 html Life is like a train ride We get on We ride We get of
  • HikariPool一直报连接不可用

    前言 一开始发现测试环境报错 原先配置6现在配置20依然还是很频繁的报错 想看下底层到底如何处理的导致这个问题 到底什么情况 排查 看了下日志连接数大量的空闲 看日志活跃的却是满的疑惑 2023 07 18 13 17 15 258 xxl
  • Java EnumMap putAll()方法具有什么功能呢?

    转自 Java EnumMap putAll 方法具有什么功能呢 下文笔者讲述EnumMap中putAll 方法的功能简介说明 如下所示 EnumMap中putAll 方法的功能 向map中批量添加一个map元素 EnumMap中putAl
  • MSBuild入门(续)

    MSBuild基本概念 续 在上一篇简单的介绍了下MSBuild中的四个基本块 每块介绍比较单薄 在这里对在大多数的项目模版生成的 proj文件中比较常见一些用法和概念做些补充 主要有一下几方面 MSBuild特殊字符 MSBuild保留的