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;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace CShapScriptWorkLibrary2015
{
//需要下载BuildTools for Visual Studio 2015 https://www.microsoft.com/zh-CN/download/details.aspx?id=48159
//解决办法就是在引用了第三方组件的项目中, 引用第三方组件中引用了的.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 >
//Microsoft.Build.15.4.8
//Microsoft.Build.Framework.15.4.8
//Microsoft.Build.Tasks.Core.15.4.8
//Microsoft.Build.Utilities.Core.15.4.8
//Microsoft.CodeAnalysis.Common.2.6.0
//Microsoft.CodeAnalysis.CSharp.2.6.0
//Microsoft.CodeAnalysis.CSharp.Workspaces.2.6.0
//Microsoft.CodeAnalysis.Workspaces.Common.2.6.0
//自己收集到的
//Microsoft.Net.Compilers
//Microsoft.CodeAnalysis.Compilers
//Microsoft.CodeAnalysis
//<? 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>
//Can you please add the following NuGet package references to your project and try again?
//NuGet.Frameworks
//NuGet.Packaging
//NuGet.ProjectModel
//NuGet.Versioning
//MsBuild is trying to do a restore (ResolvePackageAssets task) and it needs these packages.
//最重要的一步
//由于roslyn 使用反射调用 Microsoft.CodeAnalysis.CSharp.Workspaces.dll Microsoft.CodeAnalysis.CSharp.dll 因此需要手动将这两个dll 放到exe 目录
//
//
// netcoreapp2.1
//
//
public class CShapScriptWork
{
/// <summary>
/// 待写入文件的log列表
/// </summary>
static List<string> Logs = new List<string>();
/// <summary>
/// 输出文件,成功与否
/// </summary>
static Dictionary<string, bool> OutputFiles = new Dictionary<string, bool>();
public void CompileSolution(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[0], prop[1]);
}
}
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();
try
{
bool success = CompileSolution1(solutionUrl, outputDir, keyValues);
AddFormatPrint();
if (success)
{
AddFormatPrint("Compilation completed successfully.");
}
else
{
AddFormatPrint("Compilation failed.");
}
}
catch (Exception ex)
{
AddFormatPrint(ex.Message);
}
}
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
}
private void MSBuildWorkspaceFailed(object sender, WorkspaceDiagnosticEventArgs e)
{
Console.WriteLine(e.Diagnostic.Message);
//Console.WriteLine(e.Diagnostic.LoaderExceptions);
}
//private static void Compile(ISolution solution)
//{
// var workspaceServices = (IHaveWorkspaceServices)solution;
// var projectDependencyService = workspaceServices.WorkspaceServices.GetService<IProjectDependencyService>();
// foreach (var projectId in projectDependencyService.GetDependencyGraph(solution).GetTopologicallySortedProjects())
// {
// var currentDomain = AppDomain.CurrentDomain;
// var assemblyName = new AssemblyName();
// assemblyName.Name = "Most." + solution.GetProject(projectId).AssemblyName;
// var assemblyBuilder = currentDomain.DefineDynamicAssembly
// (assemblyName, AssemblyBuilderAccess.RunAndSave);
// var moduleBuilder = assemblyBuilder.
// DefineDynamicModule(assemblyName.Name);
// var types = moduleBuilder.GetTypes();
// types.Aggregate((ts, t) =>
// {
// var tb = t as TypeBuilder;
// return tb.CreateType();
// });
// solution.GetProject(projectId).GetCompilation().Emit(moduleBuilder);
// assemblyBuilder.Save(assemblyName.Name);
// }
//}
/// <summary>
/// 编译解决方案和输出项目bin文件
/// </summary>
/// <param name="solutionUrl"></param>
/// <param name="outputDir"></param>
/// <param name="keyValues"></param>
/// <returns></returns>
private bool CompileSolution1(string solutionUrl, string outputDir, Dictionary<string, string> keyValues = null)
{
bool success = true;
MSBuildWorkspace workspace;
if (keyValues == null)
keyValues = new Dictionary<string, string>();
//if (keyValues.ContainsKey("CheckForSystemRuntimeDependency") == false)
// keyValues.Add("CheckForSystemRuntimeDependency","true");
Solution solution = null;
try
{
workspace = MSBuildWorkspace.Create(keyValues);
workspace.WorkspaceFailed += MSBuildWorkspaceFailed;
workspace.LoadMetadataForReferencedProjects = true;
//solution = workspace.OpenSolutionAsync(solutionUrl).Result;
solution = workspace.OpenSolutionAsync(solutionUrl).Result;
var aaa = solution.Projects;
//Property MSBuildInstance As VisualStudioInstance
Get all instances of MSBuild
//Private ReadOnly visualStudioInstances() As VisualStudioInstance = MSBuildLocator.QueryVisualStudioInstances().ToArray()
Pick the instance of MSBuild you want
//MSBuildInstance = visualStudioInstances(???)
//MSBuildLocator.RegisterInstance(MSBuildInstance)
//Using Workspace As MSBuildWorkspace = MSBuildWorkspace.Create()
// AddHandler Workspace.WorkspaceFailed, AddressOf MSBuildWorkspaceFailed
// Dim currentProject As Project = Workspace.OpenProjectAsync(FileName).Result
Do something with the project
//End Using
Print message for WorkspaceFailed event to help diagnosing project load failures.
// Private Sub MSBuildWorkspaceFailed(_1 As Object, e1 As WorkspaceDiagnosticEventArgs)
// If MsgBox(e1.Diagnostic.Message, MsgBoxStyle.AbortRetryIgnore, "MSBuild Failed") = MsgBoxResult.Abort Then
// End
// End If
//End Sub
//var proj = sln.Projects.First(x => x.Name == "CodeAnalysis.Desktop");
foreach (var p in solution.Projects)
{
Console.WriteLine(p.FilePath);
var facadesDir = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\Facades\";
string libDir = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\";
var proj = p.AddMetadataReference(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(facadesDir + "System.Runtime.dll"));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(facadesDir + "System.Runtime.Extensions.dll"));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(facadesDir + "System.IO.dll"));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(facadesDir + "System.Threading.Tasks.dll"));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(facadesDir + "System.Text.Encoding.dll"));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(facadesDir + "System.Reflection.dll"));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location));
proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(libDir + "mscorlib.dll"));
//proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(libDir + "System.Collections.dll"));
//proj = proj.AddMetadataReference(MetadataReference.CreateFromFile(libDir + "System.IO.FileSystem.dll"));
}
}
catch (Exception ex)
{
Console.WriteLine("编绎失败!");
Console.WriteLine(ex.Message);
return false;
}
ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
IEnumerable<ProjectId> projectIds = projectGraph.GetTopologicallySortedProjects();
foreach (ProjectId projectId in projectIds)
{
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(0, 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 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 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" } };
}
//参数解析******************************************************************************************
//在项目中引入这两个类就可以在Main函数里对args做相应的解析和操作了。
//例如有控制台应用Example,在命令行中输入:
//Example.exe -u MrJson -p admin123
//在Example的Main函数里处理args:
//static void Main(string[] args)
//{
// var arguments = CommandLineArgumentParser.Parse(args);
// if (arguments.Has("-u"))
// {
// Console.WriteLine("用户名:{0}", arguments.Get("-u").Next);
// }
// if (arguments.Has("-p"))
// {
// Console.WriteLine("密码:{0}", arguments.Get("-p").Next);
// }
//}
//如果参数后面要传多个值,例如下面这个例子,-chpwd参数需要两个参数: Example.exe -chpwd admin888 admin999 那么,就可以这样处理:
// if(arguments.Has("-chpwd"))
//{
// var arg = arguments.Get("-chpwd");
// var oldPwd = arg.Take();
// var newPwd = arg.Take().Take();
// // 或者
// var pwds = arg.Take(2);
// oldPwd = pwds.First();
// newPwd = pwds.Last();
// Console.WriteLine("原密码:{0} 新密码:{1}", oldPwd, newPwd);
public class CommandLineArgument
{
List<CommandLineArgument> _arguments;
int _index;
string _argumentText;
public CommandLineArgument Next
{
get
{
if (_index < _arguments.Count - 1)
{
return _arguments[_index + 1];
}
return null;
}
}
public CommandLineArgument Previous
{
get
{
if (_index > 0)
{
return _arguments[_index - 1];
}
return null;
}
}
internal CommandLineArgument(List<CommandLineArgument> args, int index, string argument)
{
_arguments = args;
_index = index;
_argumentText = argument;
}
public CommandLineArgument Take()
{
return Next;
}
public IEnumerable<CommandLineArgument> Take(int count)
{
var list = new List<CommandLineArgument>();
var parent = this;
for (int i = 0; i < count; i++)
{
var next = parent.Next;
if (next == null)
break;
list.Add(next);
parent = next;
}
return list;
}
public static implicit operator string(CommandLineArgument argument)
{
return argument._argumentText;
}
public override string ToString()
{
return _argumentText;
}
}
public class CommandLineArgumentParser
{
List<CommandLineArgument> _arguments;
public static CommandLineArgumentParser Parse(string[] args)
{
return new CommandLineArgumentParser(args);
}
public CommandLineArgumentParser(string[] args)
{
_arguments = new List<CommandLineArgument>();
for (int i = 0; i < args.Length; i++)
{
_arguments.Add(new CommandLineArgument(_arguments, i, args[i]));
}
}
public CommandLineArgument Get(string argumentName)
{
return _arguments.FirstOrDefault(p => p == argumentName);
}
public bool Has(string argumentName)
{
return _arguments.Count(p => p == argumentName) > 0;
}
}
}