使用roslyn代替MSBuild完成解决方案编译
原本我是使用批处理调用 MSBuild 完成解决方案编译的,新版的 MSBuild 在 Visual Studio 2015 会自带安装.
当然在Visual Studio 2015 中,MSBuild 是一个独立的安装包,可以单独安装,而无须安装 Visual Studio 2015.
刚开始,我在 Windows Server 2008 R2 上使用 MSBuild 编译使用 .NET Framework 4.5.2 版本 开发的项目,也不是那么顺利的.
期间,遇到并且解决了很多问题,依次顺序为:
1. Windows Server 2008 R2 没有安装 .NET Framework 4.5.2 ,这个安装 .NET Framework 4.5.2 就解决了.
2. Windows Server 2008 R2 上没有可以编译 .NET Framework 4.5.2 版本项目的 MSBuild .
原因是 MSBuild 的版本问题,因为 .NET Framework 4.0 自带的 MSBuild 不能识别 C# 6.0 语法特性.
对于这个问题,当时很纠结,因为那时我还不知道 MSBuild 有了独立安装包,以为想要用新版的 MSBuild 必须在服务器上安装 Visual Studio 2015.
后来我在visualstudio.com上找到了 MSBuild 独立安装包, 名为 Microsoft Build Tools 2015, 所以这个问题也算是解决了.
3. 在Windows Server 2008 R2 上用 MSBuild 2015 居然要安装 .NET Framework 4.5.2 SDK ?
这个也是安装 .NET Framework 4.5.2 Developer Pack 就可以解决了, 不过我没安装, 而是直接从本机上拷贝一份到服务器上, 存放位置和本机的路径一样.
只要安装了 Visual Studio 2015 ,那么 SDK 的位置一般在(x86系统)C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2 ,
(x64系统)C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2.
4. 由于在项目中引用了第三方组件,而第三方组件又引用了一些 .NET Framework 4.0 的 dll, 而在引用第三方组件的项目中没有引用第三方组件中引用了的 .NET Framework 4.0 的 dll.
一般情况下,用 Visual Studio 2015 进行编译是没有问题的. 当使用批处理进行编译的时候, 问题就来了, 抛出了一对错误, 诸如 System.Object, Object 之类的错误, 比如:
error CS0012: The type 'Object' is defined in an assembly that is not referenced.
You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a'.
The type 'System.Object' is defined in an assembly
that is not referenced. You must add a reference to assembly 'System.Runtime,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
发现同一依赖程序集的不同版本之间存在冲突
解决办法就是在引用了第三方组件的项目中, 引用第三方组件中引用了的 .NET Framework 4.0 的 dll. 这样批处理是可以成功执行完成编译的了. 但 Visual Studio 2015 编译却报错了.
于是折腾了一番, 敲定解决办法是:
1.拷贝System.Runtime.dll到解决方案目录(随意, 我的是Library目录)下.
2.直接打开需要引用的csproj文件,向其中添加:
<Reference Include="System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<HintPath>..\..\Library\System.Runtime.dll</HintPath>
<Private>True</Private>
</Reference>
3.向 Web.Config 的 runtime --> assemblyBinding 节添加:
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
到这里, VS 编译 和 批处理编译都没问题了.
MSBuild 批处理编译 .NET Framework 4.5.2 项目的命令行:
ECHO 初始化变量
SET ProgramPath="C:\Program Files"
IF EXIST %windir%\SysWOW64 SET ProgramPath="C:\Program Files (x86)"
SET MsBuildExe="%ProgramPath%\MSBuild\14.0\Bin\MSBuild.exe" /t:rebuild /verbosity:q /p:Configuration=Release;FrameworkPathOverride="%ProgramPath%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2" /l:FileLogger,Microsoft.Build.Engine;verbosity=normal;encoding=utf-8;append=true;logfile=Build.log ECHO 编译解决方案
%MsBuildExe% "C:\work\CRM\CRM.VS2015.sln"
大功告成, 这样就可以编译 .NET Framework 4.5.2 项目了.
可是后来我为什么要换成 roslyn 编译呢? 这也是有原因的!
经过上面的折腾,我成功用批处理编译 .NET Framework 4.5.2 项目后, 我并未满足, 我想要更方便的, 无须安装那么多操蛋的东西, 只需要有运行时环境就可以了, 行不行?
答案当然是可以的, 那便是近年渐火的 roslyn 开源项目.
接下来, 我尝试使用 roslyn 帮助我完成编译 .NET Framework 4.5.2 项目.
首先, 用 Visual Studio 2015 Update 1 新建一个目标框架为 .NET Framework 4.5.2 的控制台 C# 项目, 为什么一定要 Update 1, 不解释, 照做就对了.
然后对这项目右键属性, 更改程序集名称为 RBuild .
其次, 在 Visual Studio 菜单栏中 工具 --> NuGet 包管理器 --> 程序包管理器控制台.
在控制台输入指令: Install-Package Microsoft.CodeAnalysis 以及 Install-Package Microsoft.Net.Compilers.
安装成功后,在项目中会有个包管理文件 packages.config
内容如下:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.CodeAnalysis" version="1.1.1" targetFramework="net452" />
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net452" />
<package id="Microsoft.CodeAnalysis.Common" version="1.1.1" targetFramework="net452" />
<package id="Microsoft.CodeAnalysis.CSharp" version="1.1.1" targetFramework="net452" />
<package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="1.1.1" targetFramework="net452" />
<package id="Microsoft.CodeAnalysis.VisualBasic" version="1.1.1" targetFramework="net452" />
<package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="1.1.1" targetFramework="net452" />
<package id="Microsoft.CodeAnalysis.Workspaces.Common" version="1.1.1" targetFramework="net452" />
<package id="Microsoft.Composition" version="1.0.27" targetFramework="net452" />
<package id="Microsoft.Net.Compilers" version="1.1.1" targetFramework="net452" developmentDependency="true" />
<package id="System.Collections" version="4.0.0" targetFramework="net452" />
<package id="System.Collections.Immutable" version="1.1.37" targetFramework="net452" />
<package id="System.Diagnostics.Debug" version="4.0.0" targetFramework="net452" />
<package id="System.Globalization" version="4.0.0" targetFramework="net452" />
<package id="System.IO" version="4.0.0" targetFramework="net452" />
<package id="System.Linq" version="4.0.0" targetFramework="net452" />
<package id="System.Reflection" version="4.0.0" targetFramework="net452" />
<package id="System.Reflection.Extensions" version="4.0.0" targetFramework="net452" />
<package id="System.Reflection.Metadata" version="1.1.0" targetFramework="net452" />
<package id="System.Reflection.Primitives" version="4.0.0" targetFramework="net452" />
<package id="System.Resources.ResourceManager" version="4.0.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
<package id="System.Runtime.Extensions" version="4.0.0" targetFramework="net452" />
<package id="System.Runtime.InteropServices" version="4.0.0" targetFramework="net452" />
<package id="System.Text.Encoding" version="4.0.0" targetFramework="net452" />
<package id="System.Text.Encoding.Extensions" version="4.0.0" targetFramework="net452" />
<package id="System.Threading" version="4.0.0" targetFramework="net452" />
</packages>
接着, 在 Program类 敲代码:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.MSBuild;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq; namespace WebAPI.Build.Roslyn
{
class Program
{
/// <summary>
/// 待写入文件的log列表
/// </summary>
static List<string> Logs = new List<string>(); /// <summary>
/// 输出文件,成功与否
/// </summary>
static Dictionary<string, bool> OutputFiles = new Dictionary<string, bool>(); static void Main(string[] args)
{
//命令行参数解析器
CommandLineArgumentParser arguments = CommandLineArgumentParser.Parse(args); if (arguments.Has(ConfigInfo.Help) || arguments.Has(ConfigInfo.Question))
{
string helpFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "help.txt");
string[] contents = File.ReadAllLines(helpFile);
foreach (string content in contents)
{
Console.WriteLine(content);
} return;
} //解决方案路径
string solutionUrl;
if (arguments.Has(ConfigInfo.SolutionUrl))
{
solutionUrl = arguments.Get(ConfigInfo.SolutionUrl).Next;
}
else
{
solutionUrl = GetAppSetting(ConfigInfo.SolutionUrl);
} //输出目录
string outputDir;
if (arguments.Has(ConfigInfo.OutputDir))
{
outputDir = arguments.Get(ConfigInfo.OutputDir).Next;
}
else
{
outputDir = GetAppSetting(ConfigInfo.OutputDir);
} //编译属性
string properties;
if (arguments.Has(ConfigInfo.Properties))
{
properties = arguments.Get(ConfigInfo.Properties).Next;
}
else
{
properties = GetAppSetting(ConfigInfo.Properties);
} Dictionary<string, string> keyValues;
if (!string.IsNullOrEmpty(properties))
{
keyValues = new Dictionary<string, string>();
IEnumerable<string> props = properties.Split(';').Where(t => !string.IsNullOrWhiteSpace(t));
foreach (var item in props)
{
string[] prop = item.Split('=');
keyValues.Add(prop[], prop[]);
}
}
else
{
keyValues = null;
} string logFile;
if (arguments.Has(ConfigInfo.LogFile))
{
logFile = arguments.Get(ConfigInfo.LogFile).Next;
}
else
{
logFile = GetAppSetting(ConfigInfo.LogFile);
} if (!File.Exists(solutionUrl))
{
AddFormatPrint("The file specified does not exist.");
AddFormatPrint("FileName:" + solutionUrl);
}
else
{
AddFormatPrint("Start building solutions");
AddFormatPrint(); AddFormatPrint("Check output directory exists");
if (!Directory.Exists(outputDir))
{
AddFormatPrint("Create output directory:");
AddFormatPrint(outputDir);
Directory.CreateDirectory(outputDir);
AddFormatPrint("Output directory has been created successfully");
}
else
{
AddFormatPrint("Output directory already exists");
}
AddFormatPrint(); AddFormatPrint("Start compilation solution");
AddFormatPrint();
bool success = CompileSolution(solutionUrl, outputDir, keyValues);
AddFormatPrint(); if (success)
{
AddFormatPrint("Compilation completed successfully.");
}
else
{
AddFormatPrint("Compilation failed.");
}
} foreach (string fullPathName in OutputFiles.Where(t => t.Value == false).Select(t => t.Key))
{
try
{
File.Delete(fullPathName);
}
catch
{
}
}
File.WriteAllLines(logFile, Logs); #if DEBUG
AddFormatPrint("Press the any key to exit.");
Console.ReadKey();
#endif
} /// <summary>
/// 编译解决方案和输出项目bin文件
/// </summary>
/// <param name="solutionUrl"></param>
/// <param name="outputDir"></param>
/// <param name="keyValues"></param>
/// <returns></returns>
private static bool CompileSolution(string solutionUrl, string outputDir, Dictionary<string, string> keyValues = null)
{
bool success = true; MSBuildWorkspace workspace;
if (keyValues != null && keyValues.Any())
{
workspace = MSBuildWorkspace.Create(keyValues);
}
else
{
workspace = MSBuildWorkspace.Create();
} Solution solution = workspace.OpenSolutionAsync(solutionUrl).Result; ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
foreach (ProjectId projectId in projectGraph.GetTopologicallySortedProjects())
{
Project project = solution.GetProject(projectId);
AddFormatPrint("Building: {0}", project.FilePath);
try
{
Compilation projectCompilation = project.GetCompilationAsync().Result;
if (null != projectCompilation && !string.IsNullOrEmpty(projectCompilation.AssemblyName))
{
string fileName = string.Format("{0}.dll", projectCompilation.AssemblyName);
string fullPathName = string.Format("{0}\\{1}", outputDir, fileName);
if (!OutputFiles.ContainsKey(fullPathName))
{
OutputFiles.Add(fullPathName, true);
} var diagnostics = projectCompilation.GetDiagnostics();
var warnDiagnostics = diagnostics.Where(x => x.Severity == DiagnosticSeverity.Warning).ToArray();
var errorDiagnostics = diagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).ToArray(); foreach (var e in errorDiagnostics.Concat(warnDiagnostics).ToArray())
{
AddFormatPrint("{0}: {1}", e.Severity.ToString(), e.ToString());
} if (errorDiagnostics.Any())
{
OutputFiles[fullPathName] = false;
AddFormatPrint("Build failed.");
success = false;
}
else
{
AddFormatPrint("Build successfully."); using (var stream = new MemoryStream())
{
EmitResult result = projectCompilation.Emit(stream);
AddFormatPrint("{0} --> {1}", project.Name, fullPathName);
if (result.Success)
{
using (FileStream file = File.Create(fullPathName))
{
stream.Seek(, SeekOrigin.Begin);
stream.CopyTo(file);
}
AddFormatPrint("Output successfully.");
}
else
{
OutputFiles[fullPathName] = false;
AddFormatPrint("Output failed.");
success = false;
}
}
}
AddFormatPrint();
}
else
{
AddFormatPrint("Build failed. {0}", project.FilePath);
success = false;
}
}
catch (AggregateException ex)
{
foreach (var ie in ex.InnerExceptions)
{
AddFormatPrint(ie.Message);
}
success = false;
}
catch (Exception ex)
{
AddFormatPrint(ex.Message);
success = false;
}
AddFormatPrint();
} return success;
} /// <summary>
/// 添加消息记录和打印消息
/// </summary>
/// <param name="format"></param>
/// <param name="args"></param>
private static void AddFormatPrint(string format = "", params object[] args)
{
if (format == string.Empty)
{
Logs.Add(string.Empty);
Console.WriteLine();
}
else
{
string log = string.Format(format, args);
Logs.Add(log);
Console.WriteLine(log);
}
} /// <summary>
/// 获取配置值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
private static string GetAppSetting(string key)
{
return ConfigurationManager.AppSettings[key] ?? ConfigurationManager.AppSettings[ConfigInfo.KeyValues[key]];
}
} public struct ConfigInfo
{
/// <summary>
/// 解决方案路径
/// </summary>
public const string SolutionUrl = "-s"; /// <summary>
/// 输出目录
/// </summary>
public const string OutputDir = "-o"; /// <summary>
/// 编译属性
/// </summary>
public const string Properties = "-p"; /// <summary>
/// 日志文件名称
/// </summary>
public const string LogFile = "-l"; /// <summary>
/// 帮助
/// </summary>
public const string Help = "-h"; /// <summary>
/// 提问
/// </summary>
public const string Question = "-?"; /// <summary>
/// 全称键值对
/// </summary>
public static readonly Dictionary<string, string> KeyValues = new Dictionary<string, string> { { SolutionUrl, "solutionUrl" }, { OutputDir, "outputDir" }, { Properties, "properties" }, { LogFile, "logFile" } };
}
}
编译完成后, 就可以在 CMD 命令行提示符用了.
这是 help.txt 内容以及用法:
Provide help information for commands. RBuild [-s] [-o] [-p] -s —— Solution Path, required.
Use: -s "C:\work\CRM\CRM.VS2015.sln"
-o —— Output Directory, required.
Use: -o "E:\work\CRM\Build\WebAPI\bin"
-p —— Build Properties, required.
Use: -p Configuration=Release;FrameworkPathOverride=C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2;key=value
-l —— Output Log Path, not required.
Use: -l "log.txt"
目前我只知道 roslyn 可以编译项目, 但编译后拷贝相关的非代码文件到相应目录这个功能却没有发现哪里有, 只能通过批处理自己拷贝了.
我的用法:
ECHO 开始编译解决方案
BuildTools\RBuild.exe -s "..\..\CRM\src\CRM.VS2015.sln" -o "CRM\bin" -p Configuration=Release -l Build.log
安装包下载:
.NET Framework 4.5.2(运行时环境):https://www.microsoft.com/zh-cn/download/details.aspx?id=42642
.NET Framework 4.5.2 Language Pack(可选):https://www.microsoft.com/zh-cn/download/details.aspx?id=42641
.NET Framework 4.5.2 Developer Pack(SDK):https://www.microsoft.com/zh-CN/download/details.aspx?id=42637
Microsoft Build Tools 2015(MSBuild):https://www.microsoft.com/zh-cn/download/details.aspx?id=48159
引用资料:
http://www.cnblogs.com/walkerwang/p/3368986.html
http://yangpei.appsp0t.com/post/aglzfnlhbmdwZWlyDAsSBUVudHJ5GKEfDA
http://stackoverflow.com/questions/13280008/how-do-i-compile-a-c-sharp-solution-with-roslyn
http://www.cnblogs.com/linxuanchen/p/c-sharp-command-line-argument-parser.html
https://msdn.microsoft.com/zh-cn/library/ms164311.aspx
https://github.com/dotnet/roslyn
使用roslyn代替MSBuild完成解决方案编译的更多相关文章
- win10 uwp 使用 msbuild 命令行编译 UWP 程序
原文:win10 uwp 使用 msbuild 命令行编译 UWP 程序 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http:// ...
- 利用批处理结合Msbuild实现快速编译
我们经常在用vs2005做项目的时候会把一个项目分成几个模块(不管是对于功能上,还是系统构架上面),为的是以后部署,还有修改维护时候的方便.这样就会带来一个问题,随着模块的增加(这里所说得每个模块就是 ...
- 每日踩坑 2019-04-08 VS2015未能找到路径“…\bin\roslyn\csc.exe”的解决方案
使用 Nuget 安装 Microsoft.CodeDom.Providers.DotNetCompilerPlatform 包即可. VS2017都是用 roslyn 编译, VS2015原本的编译 ...
- [2015-11-10]分享一个调用msbuild生成解决方案并打包发布的批处理脚本
最近工作成果之一,特此记录. 用于打包的批处理脚本 注意设置 path/to/your/solutionfile.sln 指向vs的解决方案文件. setlocal enabledelayedexpa ...
- VS2017 Pro未能找到路径“……\bin\roslyn\csc.exe”的解决方案
VS2017改用roslyn编译的,新的roslyn编译器,支持c# 6.0语法.它放到bin里面去是为了支持asp.net应用的动态编译. 它是通过nuget的包Microsoft.CodeDom. ...
- CMake生成OpenCV解决方案&&编译OpenCV源码
生成OpenCV工程需要用到CMake,所以第一步需要下载CMake软件,下载链接:CMake下载 目前最新的版本是3.7.1,这里选择下载Platform下的Windows win32-x86 ZI ...
- Win10 OpenCV3.3.0+VS2013配置大坑,OpenCV解决方案编译报错“找不到python36_d.lib”错误
今天因为想要用OpenCV做图像识别,小白一个,在网上找到一个教程,但是需要配置OpenCV3.3.0的环境,于是又在网上找OpenCV3.3.0+VS2013(因为我之前已经安过了VS2013),前 ...
- asyncio NetMQ 解决方案编译问题
程序集代码 (原) <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <VersionPrefix& ...
- GitLab CI持续集成配置方案(补)
上篇文章介绍了GitLab CI的持续集成配置方法,本篇文章将主要介绍NUnit的持续集成和遇到的一些坑 1.NUnit单元测试持续集成 下载NUnit.3.4.1.msi,https://githu ...
随机推荐
- C#多线程之线程同步篇2
在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...
- AspNetPager分页控件样式的使用
分页是Web应用程序中最常用到的功能之一,AspNetPager 简单实用,应用到项目后台中,棒极了! 自定义样式: <style type="text/css"> ...
- 编译器开发系列--Ocelot语言5.表达式的有效性检查
本篇将对"1=3""&5"这样无法求值的不正确的表达式进行检查. 将检查如下这些问题.●为无法赋值的表达式赋值(例:1 = 2 + 2)●使用非法的函数 ...
- Configure a bridge interface over a VLAN tagged bonded interface
SOLUTION VERIFIED February 5 2014 KB340153 Environment Red Hat Enterprise Linux 6 (All Versions) Red ...
- ASP.NET跨平台最佳实践
前言 八年的坚持敌不过领导的固执,最终还是不得不阔别已经成为我第二语言的C#,转战Java阵营.有过短暂的失落和迷茫,但技术转型真的没有想象中那么难.回头审视,其实单从语言本身来看,C#确实比Java ...
- Dubbo 备注
Dubbo是阿里开源的一款服务治理中间件,主要包含如下节点: Provider: 暴露服务的服务提供方. Consumer: 调用远程服务的服务消费方. Registry: 服务注册与发现的注册中心. ...
- 【AI开发第一步】微软认知服务API应用
目录 介绍 API分类 使用‘视觉’API完成的Demo 点击直接看干货 介绍 从3月份Google家的阿尔法狗打败韩国围棋冠军选手李世石,到之后微软Build2016大会宣布的“智能机器人”战略.种 ...
- 在开发中到底要不要用var?
var是.net的一个语法糖,在Resharper中推荐都使用这个关键字,平常我也是经常用:但是在跟其他程序员推广使用时,他的一些考虑引发了我的深思,到底该不该使用这个关键字呢? 我使用的理由 我使用 ...
- 全局唯一ID设计
在分布式系统中,经常需要使用全局唯一ID查找对应的数据.产生这种ID需要保证系统全局唯一,而且要高性能以及占用相对较少的空间. 全局唯一ID在数据库中一般会被设成主键,这样为了保证数据插入时索引的快速 ...
- 如何用Dockerfile创建镜像
本文原创,原文地址为:http://www.cnblogs.com/fengzheng/p/5181222.html 创建镜像的目的 首先说DockerHub或其它一些镜像仓库已经提供了够多的镜像,有 ...