本来我想说的是基于引用 msbuild 程序集来自己做一个编译器,但是想想好像本文做的,和造编译器没啥关系,咱自己调用 msbuild 的 API 而已。本文来告诉大家如何引用 msbuild 程序集,如何在自己的应用程序里面嵌入 msbuild 的构建代码,实现 dotnet build 的效果

大部分的代码都是采用命令行的方式去调用 dotnet build 或 msbuild 命令,然而通过命令行调用用的是跨进程的方式,如果期望做更多的定制化,最好还是放在相同的进程,此时可以更改构建的各个步骤

自己制作一个编译器最简单的方法就是引用现有的成熟的编译器作为组件,刚好 msbuild 最新版本也是使用 dotnet 框架编写的,咱的 dotnet 应用可以非常方便将 msbuild 引用进来。当然了,本文不讨论如何自己发布 msbuild 的问题,因为这又是另一个坑了。本文的方法是引用本机已安装好的 msbuild 程序集

在开始之前,请新建一个控制台项目。当然了,你要是新建一个 WPF 项目也没啥问题

编辑 csproj 文件,添加如下代码

  <ItemGroup><PackageReference Include="Microsoft.Build" Version="16.10.0" ExcludeAssets="runtime" /><PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.10.0" ExcludeAssets="runtime" /><PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" /></ItemGroup>

添加完成之后的 csproj 文件代码如下

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net6.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.Build" Version="16.10.0" ExcludeAssets="runtime" /><PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.10.0" ExcludeAssets="runtime" /><PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" /></ItemGroup>
</Project>

第一步是先获取本机已安装好的 msbuild 实例,如下代码

        static void Main(string[] args){var instances = MSBuildLocator.QueryVisualStudioInstances().ToList();}

以上代码要能运行,需要加上如下命名空间

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Locator;

以上拿到的 instances 就是本机安装的 msbuild 实例,也就是 dotnet sdk 的各个版本,可以使用如下代码输出

            for (var i = 1; i <= instances.Count; i++){var instance = instances[i - 1];var recommended = string.Empty;// The dev console is probably always the right choice because the user explicitly opened// one associated with a Visual Studio install. It will always be first in the list.if (instance.DiscoveryType == DiscoveryType.DeveloperConsole)recommended = " (Recommended!)";Console.WriteLine($"{i}) {instance.Name} - {instance.Version}{recommended}");}

下一步是有点黑科技的部分,也就是为什么我会写本文的原因。使用下面代码注册 msbuild 实例,如果没有使用下面这句代码注册,那么在后续调用 msbuild 相关类型时,将会因为找不到 msbuild 的程序集而失败

            // 必须调用 RegisterInstance 方法,否则将提示找不到 msbuild 文件MSBuildLocator.RegisterInstance(instances.First());

注册完成之后,将可以使用 msbuild 提供的各个类来实现构建,请新建一个方法用来编写调用 msbuild 各个类的构建代码。如以下代码

        private static void Build(){var projectFile = new FileInfo(@"..\..\..\RalboleaballNeeqaqiku.csproj");var projectRootElement = ProjectRootElement.Open(projectFile.FullName);var project = new Project(projectRootElement);project.Build(new Logger());}

为什么需要这部分构建代码放在另一个方法里面?原因是在碰到了 ProjectRootElement 类型的时候,就需要开始加载程序集,然而在调用 MSBuildLocator.RegisterInstance 之前,还是找不到程序集的哦。因此为了让 MSBuildLocator.RegisterInstance 能被执行,就需要让包含 MSBuildLocator.RegisterInstance 代码的方法不会在执行之前碰到还没有存在的程序集,因此就需要将碰到构建相关逻辑的代码放在独立的方法或者独立的类型里面,这样就能让包含 MSBuildLocator.RegisterInstance 的代码不会因为找不到程序集而不执行

以上代码是通过调用 ProjectRootElement.Open 方法加载了 csproj 文件,此步骤是反序列化过程。接着新建 Project 实例,在新建方法里面将会进行初始化,可以拿到输入的 csproj 将有哪些导入等信息

最后一步是通过调用 Project 的 Build 方法进行构建,此时将会执行一次构建,构建的信息通过传入的 Logger 进行输出,以下是 Logger 的代码

        private class Logger : ILogger{public void Initialize(IEventSource eventSource){eventSource.AnyEventRaised += (_, args) => { Console.WriteLine(args.Message); };}public void Shutdown(){}public LoggerVerbosity Verbosity { get; set; }public string Parameters { get; set; }}

全部代码如下

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Locator;namespace RalboleaballNeeqaqiku
{class Program{static void Main(string[] args){var instances = MSBuildLocator.QueryVisualStudioInstances().ToList();for (var i = 1; i <= instances.Count; i++){var instance = instances[i - 1];var recommended = string.Empty;// The dev console is probably always the right choice because the user explicitly opened// one associated with a Visual Studio install. It will always be first in the list.if (instance.DiscoveryType == DiscoveryType.DeveloperConsole)recommended = " (Recommended!)";Console.WriteLine($"{i}) {instance.Name} - {instance.Version}{recommended}");}// 必须调用 RegisterInstance 方法,否则将提示找不到 msbuild 文件MSBuildLocator.RegisterInstance(instances.First());// 需要将构建的代码放在另一个方法里面,否则将会因为放在相同的方法,没有加上程序集Build();}private static void Build(){var projectFile = new FileInfo(@"..\..\..\RalboleaballNeeqaqiku.csproj");var projectRootElement = ProjectRootElement.Open(projectFile.FullName);var project = new Project(projectRootElement);project.Build(new Logger());}private class Logger : ILogger{public void Initialize(IEventSource eventSource){eventSource.AnyEventRaised += (_, args) => { Console.WriteLine(args.Message); };}public void Shutdown(){}public LoggerVerbosity Verbosity { get; set; }public string Parameters { get; set; }}}
}

本文所有代码放在 github 和 gitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin b6171297d4200586d135a8c5c0d7376df7ee7c6a

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 RalboleaballNeeqaqiku 文件夹

更多关于 Roslyn 请看 手把手教你写 Roslyn 修改编译


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/dotnet-%E9%80%9A%E8%BF%87%E5%BC%95%E7%94%A8-msbuild-%E7%A8%8B%E5%BA%8F%E9%9B%86%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%B7%B1%E5%AE%9A%E5%88%B6%E7%BC%96%E8%AF%91%E5%99%A8.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

dotnet 通过引用 msbuild 程序集实现自己定制编译器相关推荐

  1. 未能解析引用的程序集……因为它对不在当前目标框架……

    新建了一个项目,引用了一下自己写的一个应用程序,结果遇见两个警告: 警告 1 未能解析引用的程序集"E:\Develop\ShaftCAD\bin\ShaftCAD.exe",因为 ...

  2. 所引用的程序集没有强命名解决方法

    所引用的程序集没有强命名 1.打开SDK 命令提示窗口; 也就是VS2012 开发人员命令提示,一般所在目录C:\ProgramData\Microsoft \Windows\Start Menu\P ...

  3. 编译报错程序集版本高于所引用的程序集的版本

    今天修改MVC的项目,编译的时候出现错误提示,以前没有遇到过,错误提示内容: 编译报错程序集版本高于所引用的程序集的版本 于是网上找了下相关的内容,发现是项目引用出现问题,只要根据错误提示,把相关的项 ...

  4. 关于程序集生成失败 -- 引用的程序集没有强名称的解决办法

    关于程序集生成失败 -- 引用的程序集没有强名称的解决办法 我在PetShop4程序中加入了一个业务外观层,然后在业务逻辑层(PetShop.BLL)引用这个程序集,点"生成",出 ...

  5. [ASP.NET Core 2.0 前方速报].NET Core 2.0.3 已经支持引用第三方程序集了

    发现问题 在将 FineUIMvc(支持ASP.NET MVC 5.2.3)升级到 ASP.NET Core 2.0 的过程中,我们发现一个奇怪的现象: 通过项目引用 FineUICore 工程一切正 ...

  6. 未能解析引用的程序集......因为它对不在当前目标框架“.NETFramework,Version=v4.0,Profile=Client”中的 (转)...

    解决方法:资源管理器下点击项目名(右键)属性--将.NET Framework 4 Client Profile改成.NET Framework 4 . 传送门:http://bbs.csdn.net ...

  7. 引用程序集没有强名称解决办法

    引用程序集没有强名称解决办法(http://www.cnblogs.com/tearer/archive/2010/09/01/1814655.html) 为项目添加强名称方法: 1.右键单击项目,打 ...

  8. 【转】[程序集清单定义与程序集引用不匹配]分析及解决

    转自:https://www.cnblogs.com/shuangzimuchangzhu/p/8572817.html 什么是程序集清单(Assembly Manifest)? 我们知道,在.net ...

  9. 探究.NET的bin引用程序集运行机制看.NET程序集部署原理

    探究.NET的bin引用程序集运行机制 看.NET程序集部署原理 新建一个最简单的网站,并引用使用程序集Nhibernate.dll,页面代码为       运行后输出的结果 .NET 程序集部署程序 ...

最新文章

  1. 从volatile解读ConcurrentHashMap(jdk1.6.0)无锁读
  2. 因“薪水太高”被欠薪3个月、后又遭解雇?程序员愤怒反击!
  3. 关于使用REST API
  4. php select user 验证,php 用户验证的简单示例
  5. 五个数字从小到大排序java,五个数冒泡排序 用c语言数组定义5个数使用冒泡排序 从小到大...
  6. 复制文本朗读_原创:昭明文选配乐朗读 卷第五十一 论一 东方曼倩 非有先生论 王子渊 四子讲德论 并序...
  7. 情人节福利,撩妹神器恋爱话术库它来了~
  8. memcached 与 redis 的区别和具体应用场景
  9. oracle常用函数详解
  10. python 马赛克拼图_用几十万张图片来拼图!Open CV牛逼不是没有道理的!马赛克拼图...
  11. GANs(生成对抗网络)浅析
  12. 国内和国际反垃圾邮件组织
  13. 树状数组(Binary Indexed Tree),看这一篇就够了
  14. 网络安全证书已过期或不可信怎么办
  15. python朋友圈可见_女神说不能每张照片P的同样,因此朋友圈开三天可见,用Python一步解决...
  16. 吕文翰 php,自己动手写一个 iOS 网络请求库(三)——降低耦合
  17. 怎么把静态图片做成动态图?简单三步让图片动起来
  18. 平面设计师经常去的网站——设计灵感类
  19. RE0:从零开始的服务器生活(一):双硬盘双系统+无线网卡驱动+最简单的Ubuntu16.04 Nvidia显卡驱动设置
  20. 百度地图的驾车路线规划

热门文章

  1. 每天写出好代码的5个建议
  2. php如何生成公钥私钥,php如何生成公钥私钥(代码)
  3. 绑定dictionary 给定关键字不再字典中_VBA代码集锦-利用字典做两列数据的对比并对齐...
  4. 数据库_7_SQL基本操作——表操作
  5. dot-- 资源汇总
  6. C#构造函数、操作符重载以及自定义类型转换
  7. oracle 基本异常的练习及各个错误码
  8. Linux操作系统备份之二:通过tar拷贝分区实现Linux操作数据的在线备份
  9. Teams App 如何使用设备的能力
  10. libreoffice_如何更改您在LibreOffice中可以撤消的操作数