使用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 ...
随机推荐
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(65)-MVC WebApi 用户验证 (1)
系列目录 前言: WebAPI主要开放数据给手机APP,其他需要得知数据的系统,或者软件应用,所以移动端与系统的数据源往往是相通的. Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能, ...
- Android数据加密之SHA安全散列算法
前言: 对于SHA安全散列算法,以前没怎么使用过,仅仅是停留在听说过的阶段,今天在看图片缓存框架Glide源码时发现其缓存的Key采用的不是MD5加密算法,而是SHA-256加密算法,这才勾起了我的好 ...
- [C#] 简单的 Helper 封装 -- CookieHelper
using System; using System.Web; namespace ConsoleApplication5 { /// <summary> /// Cookie 助手 // ...
- Struts的文件上传下载
Struts的文件上传下载 1.文件上传 Struts2的文件上传也是使用fileUpload的组件,这个组默认是集合在框架里面的.且是使用拦截器:<interceptor name=" ...
- 关于Java中进程和线程的详解
一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...
- 机器指令翻译成 JavaScript —— 终极目标
上一篇,我们顺利将 6502 指令翻译成 C 代码,并演示了一个案例. 现在,我们来完成最后的目标 -- 转换成 JavaScript. 中间码输出 我们之所以选择 C,就是为了使用 LLVM.现在来 ...
- Xamarin.Android之SQLiteOpenHelper
一.前言 在手机中进行网络连接不仅是耗时也是耗电的,而耗电却是致命的.所以我们就需要数据库帮助我们存储离线数据,以便在用户未使用网络的情况下也可以能够使用应用的部分功能,而在需要网络连接的功能上采用提 ...
- 【腾讯Bugly干货分享】Android Patch 方案与持续交付
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57a31921ac3a1fb613dd40f3 Android 不仅系统版本众多 ...
- Redola.Rpc 的一个小目标
Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Complete requests: 20000 ...
- 感悟 GNU C 以及将 Vim 打造成 C/C++ 的半自动化 IDE
C 语言在 Linux 系统中的重要性自然是无与伦比.不可替代,所以我写 Linux 江湖系列不可能不提 C 语言.C 语言是我的启蒙语言,感谢 C 语言带领我进入了程序世界.虽然现在不靠它吃饭,但是 ...