WIP on C# compiler support for .NET core build

This commit is contained in:
UnknownShadow200 2022-06-11 13:31:33 +10:00
parent 088bcebb8a
commit e1a521e37b
7 changed files with 227 additions and 14 deletions

View File

@ -62,7 +62,7 @@ THE SOFTWARE.
----------------------------------------------------------------------------------
MCGalaxy includes some of a noise library, which license is as follows:
MCGalaxy includes some of a noise library, whose license is as follows:
(source https://libnoisedotnet.codeplex.com/)
----------------------------------------------------------------------------------
@ -87,3 +87,17 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------------------
MCGalaxy includes some code based on RoslynCodeDomProvider, whose license is as follows:
(source https://github.com/aspnet/RoslynCodeDomProvider)
----------------------------------------------------------------------------------
Copyright (c) Microsoft Corporation All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -589,6 +589,7 @@
<Compile Include="Modules\Compiling\CmdCompload.cs" />
<Compile Include="Modules\Compiling\Compiler.cs" />
<Compile Include="Modules\Compiling\CompilerOperations.cs" />
<Compile Include="Modules\Compiling\Roslyn.cs" />
<Compile Include="Modules\Moderation\Notes\CmdNotes.cs" />
<Compile Include="Modules\Moderation\Notes\NotesPlugin.cs" />
<Compile Include="Modules\Relay\BotControllersCmd.cs" />
@ -725,9 +726,7 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Modules\Compiling" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -8,7 +8,6 @@
<PackageReference Include="System.CodeDom" Version="4.5.0" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="4.5.0" />
<PackageReference Include="System.Drawing.Common" Version="4.5.0" />
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="3.6.0" />
<PackageReference Include="MySql.Data" Version="6.7.9" />
</ItemGroup>
</Project>

View File

@ -19,10 +19,6 @@
*/
#if !DISABLE_COMPILING
using System.CodeDom.Compiler;
using System;
#if NETSTANDARD
using Microsoft.CodeDom.Providers.DotNetCompilerPlatform;
#endif
namespace MCGalaxy.Modules.Compiling
{
@ -34,7 +30,7 @@ namespace MCGalaxy.Modules.Compiling
protected override CodeDomProvider CreateProvider() {
#if NETSTANDARD
return new CSharpCodeProvider();
return new RoslynCSharpCodeProvider();
#else
return CodeDomProvider.CreateProvider("CSharp");
#endif

View File

@ -18,11 +18,9 @@
permissions and limitations under the Licenses.
*/
#if !DISABLE_COMPILING
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace MCGalaxy.Modules.Compiling

View File

@ -0,0 +1,208 @@
// Based on https://github.com/aspnet/RoslynCodeDomProvider
// Copyright(c) Microsoft Corporation All rights reserved.
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#if !DISABLE_COMPILING
#if NETSTANDARD
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
namespace Microsoft.CodeDom.Providers.DotNetCompilerPlatform
{
public class RoslynCSharpCodeProvider : Microsoft.CSharp.CSharpCodeProvider
{
[Obsolete("Callers should not use the ICodeCompiler interface and should instead use the methods directly on the CodeDomProvider class.")]
public override ICodeCompiler CreateCompiler() { return new CSharpCompiler(); }
}
class CSharpCompiler : ICodeCompiler
{
static Regex outputRegWithFileAndLine;
static Regex outputRegSimple;
public CompilerResults CompileAssemblyFromDom(CompilerParameters options, CodeCompileUnit compilationUnit) { return null; }
public CompilerResults CompileAssemblyFromDomBatch(CompilerParameters options, CodeCompileUnit[] compilationUnits) { return null; }
public CompilerResults CompileAssemblyFromSource(CompilerParameters options, string source) { return null; }
public CompilerResults CompileAssemblyFromSourceBatch(CompilerParameters options, string[] sources) { return null; }
public CompilerResults CompileAssemblyFromFile(CompilerParameters options, string fileName) {
return CompileAssemblyFromFileBatch(options, new string[] { fileName });
}
public CompilerResults CompileAssemblyFromFileBatch(CompilerParameters options, string[] fileNames) {
try {
return FromFileBatch(options, fileNames);
} finally {
options.TempFiles.Delete();
}
}
static CompilerResults FromFileBatch(CompilerParameters options, string[] fileNames) {
string outputFile = null;
var results = new CompilerResults(options.TempFiles);
results.TempFiles.AddExtension("pdb"); // for .pdb debug files
string args = GetCommandLineArguments(options, fileNames);
// Use a response file if the compiler supports it
string responseFileArgs = GetResponseFileCmdArgs(options, args);
if (responseFileArgs != null) args = responseFileArgs;
string compilerFullPath = GetCSharpCompilerPath();
// Try opening the file to make sure the compiler exist. This will throw an exception
// if it doesn't
using (Stream tmp = File.OpenRead(compilerFullPath)) { }
int retValue = Compile(options, compilerFullPath, args, ref outputFile);
results.NativeCompilerReturnValue = retValue;
// only look for errors/warnings if the compile failed or the caller set the warning level
if (retValue != 0 || options.WarningLevel > 0) {
// The output of the compiler is in UTF8
string[] lines = File.ReadAllLines(outputFile, Encoding.UTF8);
foreach (string line in lines)
{
ProcessCompilerOutputLine(results, line);
}
}
results.PathToAssembly = options.OutputAssembly;
return results;
}
static string Quote(string value) { return "\"" + value + "\""; }
static string GetResponseFileCmdArgs(CompilerParameters options, string cmdArgs) {
string responseFileName = options.TempFiles.AddExtension("cmdline");
File.WriteAllText(responseFileName, cmdArgs, Encoding.UTF8);
// Always specify the /noconfig flag (outside of the response file)
return "/noconfig /fullpaths " + "@\"" + responseFileName + "\"";
}
static int Compile(CompilerParameters options, string compilerFullPath, string arguments, ref string outputFile) {
string errorFile = null;
string cmdLine = "\"" + compilerFullPath + "\" " + arguments;
outputFile = options.TempFiles.AddExtension("out");
return Executor.ExecWaitWithCapture(
options.UserToken, cmdLine,
Environment.CurrentDirectory,
options.TempFiles,
ref outputFile, ref errorFile);
}
static void ProcessCompilerOutputLine(CompilerResults results, string line) {
if (outputRegSimple == null) {
outputRegWithFileAndLine =
new Regex(@"(^(.*)(\(([0-9]+),([0-9]+)\)): )(error|warning) ([A-Z]+[0-9]+) ?: (.*)");
outputRegSimple =
new Regex(@"(error|warning) ([A-Z]+[0-9]+) ?: (.*)");
}
//First look for full file info
Match m = outputRegWithFileAndLine.Match(line);
bool full;
if (m.Success) {
full = true;
} else {
m = outputRegSimple.Match(line);
full = false;
}
if (!m.Success) return;
CompilerError ce = new CompilerError();
if (full) {
ce.FileName = m.Groups[2].Value;
ce.Line = int.Parse(m.Groups[4].Value, CultureInfo.InvariantCulture);
ce.Column = int.Parse(m.Groups[5].Value, CultureInfo.InvariantCulture);
}
if (string.Compare(m.Groups[full ? 6 : 1].Value, "warning", StringComparison.OrdinalIgnoreCase) == 0) {
ce.IsWarning = true;
}
ce.ErrorNumber = m.Groups[full ? 7 : 2].Value;
ce.ErrorText = m.Groups[full ? 8 : 3].Value;
results.Errors.Add(ce);
}
static string GetCommandLineArguments(CompilerParameters parameters, string[] fileNames) {
StringBuilder sb = new StringBuilder();
sb.Append("/t:library ");
// Get UTF8 output from the compiler
sb.Append("/utf8output ");
string coreAssemblyFileName = typeof(object).Assembly.Location;
Console.WriteLine("CORE FILE: " + coreAssemblyFileName);
if (!string.IsNullOrWhiteSpace(coreAssemblyFileName)) {
sb.Append("/nostdlib+ ");
sb.AppendFormat("/R:{0} ", Quote(coreAssemblyFileName.Trim()));
}
// Bug 913691: Explicitly add System.Runtime as a reference.
string systemRuntimeAssemblyPath = null;
try {
var systemRuntimeAssembly = Assembly.Load("System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
systemRuntimeAssemblyPath = systemRuntimeAssembly.Location;
}
catch {
// swallow any exceptions if we cannot find the assembly
}
if (systemRuntimeAssemblyPath != null) {
sb.AppendFormat("/R:{0} ", Quote(systemRuntimeAssemblyPath));
}
foreach (string path in parameters.ReferencedAssemblies) {
sb.AppendFormat("/R:{0} ", Quote(path));
}
sb.AppendFormat("/out:{0} ", Quote(parameters.OutputAssembly));
// debug information
sb.Append("/D:DEBUG ");
sb.Append("/debug+ ");
sb.Append("/optimize- ");
sb.Append("/warnaserror- ");
if (parameters.CompilerOptions != null) {
sb.Append(parameters.CompilerOptions + " ");
}
foreach (string path in fileNames) {
sb.AppendFormat("{0} ", Quote(path));
}
return sb.ToString();
}
static string GetCSharpCompilerPath()
{
string fullPath = Environment.GetEnvironmentVariable("ROSLYN_COMPILER_LOCATION");
if (!string.IsNullOrEmpty(fullPath)) return fullPath;
// try current directory as a fallback.. probably won't work though
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"bin\roslyn\csc.exe");
}
}
}
#endif
#endif

View File

@ -79,7 +79,6 @@ Compiling - mono and .NET framework
Compiling - .NET 6 / .NET 5 / .NET Core
-----------------
**Command line:**
* Compiling for .NET 6: No changes necessary
* Compiling for .NET 5: Change `TargetFramework` in CLI/MCGalaxyCLI_Core.csproj to `net5.0`