forked from ForNeVeR/Cesium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Compilation.cs
115 lines (97 loc) · 5 KB
/
Compilation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using System.Text;
using Cesium.CodeGen;
using Cesium.CodeGen.Contexts;
using Cesium.Core;
using Cesium.Parser;
using Cesium.Preprocessor;
using Mono.Cecil;
using Yoakke.Streams;
using Yoakke.SynKit.C.Syntax;
namespace Cesium.Compiler;
internal static class Compilation
{
public static async Task<int> Compile(
IEnumerable<string> inputFilePaths,
string outputFilePath,
CompilationOptions compilationOptions)
{
Console.WriteLine($"Generating assembly {outputFilePath}.");
var assemblyContext = CreateAssembly(outputFilePath, compilationOptions);
foreach (var inputFilePath in inputFilePaths)
{
Console.WriteLine($"Processing input file \"{inputFilePath}\".");
await GenerateCode(assemblyContext, inputFilePath);
}
SaveAssembly(assemblyContext, compilationOptions.TargetRuntime.Kind, outputFilePath, compilationOptions.CesiumRuntime);
return 0;
}
private static AssemblyContext CreateAssembly(string outputFilePath, CompilationOptions compilationOptions)
{
var assemblyName = Path.GetFileNameWithoutExtension(outputFilePath);
return AssemblyContext.Create(
new AssemblyNameDefinition(assemblyName, new Version()),
compilationOptions);
}
private static Task<string> Preprocess(string compilationFileDirectory, TextReader reader)
{
var currentProcessPath = Path.GetDirectoryName(Environment.ProcessPath)
?? throw new Exception("Cannot determine path to the compiler executable.");
var stdLibDirectory = Path.Combine(currentProcessPath, "stdlib");
var includeContext = new FileSystemIncludeContext(stdLibDirectory, compilationFileDirectory);
var preprocessorLexer = new CPreprocessorLexer(reader);
var definesContext = new InMemoryDefinesContext();
var preprocessor = new CPreprocessor(preprocessorLexer, includeContext, definesContext);
return preprocessor.ProcessSource();
}
private static async Task GenerateCode(AssemblyContext context, string inputFilePath)
{
var compilationFileDirectory = Path.GetDirectoryName(inputFilePath)!;
await using var input = new FileStream(inputFilePath, FileMode.Open);
using var reader = new StreamReader(input, Encoding.UTF8);
var content = await Preprocess(compilationFileDirectory, reader);
var lexer = new CLexer(content);
var parser = new CParser(lexer);
var translationUnitParseError = parser.ParseTranslationUnit();
if (translationUnitParseError.IsError)
{
switch (translationUnitParseError.Error.Got)
{
case CToken token:
throw new ParseException($"Error during parsing {inputFilePath}. Error at position {translationUnitParseError.Error.Position}. Got {token.LogicalText}.");
case char ch:
throw new ParseException($"Error during parsing {inputFilePath}. Error at position {translationUnitParseError.Error.Position}. Got {ch}.");
default:
throw new ParseException($"Error during parsing {inputFilePath}. Error at position {translationUnitParseError.Error.Position}.");
}
}
var translationUnit = translationUnitParseError.Ok.Value;
var lastUnprocessedToken = parser.TokenStream.Peek();
if (lastUnprocessedToken.Kind != CTokenType.End)
throw new ParseException($"Excessive output after the end of a translation unit {inputFilePath} at {lexer.Position}.");
var translationUnitName = Path.GetFileNameWithoutExtension(inputFilePath);
context.EmitTranslationUnit(translationUnitName, translationUnit);
}
private static void SaveAssembly(
AssemblyContext context,
SystemAssemblyKind targetFrameworkKind,
string outputFilePath,
string compilerRuntimeDll)
{
context.VerifyAndGetAssembly().Write(outputFilePath);
// This part should go to Cesium.SDK eventually together with
// runtimeconfig.json generation
var outputExecutablePath = Path.GetDirectoryName(outputFilePath) ?? Environment.CurrentDirectory;
var applicationRuntime = Path.Combine(outputExecutablePath, "Cesium.Runtime.dll");
// Prevent copying of the Cesium.Runtime if compile in same directory as compiler.
if (!string.Equals(Path.GetFullPath(compilerRuntimeDll), Path.GetFullPath(applicationRuntime), StringComparison.InvariantCultureIgnoreCase))
{
File.Copy(compilerRuntimeDll, applicationRuntime, true);
}
if (context.Module.Kind == ModuleKind.Console && targetFrameworkKind == SystemAssemblyKind.SystemRuntime)
{
var runtimeConfigFilePath = Path.ChangeExtension(outputFilePath, "runtimeconfig.json");
Console.WriteLine($"Generating a .NET 6 runtime config at {runtimeConfigFilePath}.");
File.WriteAllText(runtimeConfigFilePath, RuntimeConfig.EmitNet6());
}
}
}