-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathCompilation.cs
202 lines (174 loc) · 7.83 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
using Meadow.Contract;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.Extensions.DependencyModel;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
namespace Meadow.SolCodeGen
{
public class Compilation
{
//public const string GENERATED_ASM_NAME = "GeneratedSol";
readonly SolCodeGenResults _codeGenResults;
readonly string _outputDirectory;
readonly string _namespace;
readonly string _generatedAssemblyFile;
readonly string _generatedPdbFile;
readonly string _generatedXmlDocFile;
public Compilation(SolCodeGenResults codeGenResults, string @namespace, string outputDirectory)
{
_codeGenResults = codeGenResults;
_outputDirectory = outputDirectory;
_namespace = @namespace;
_generatedAssemblyFile = Path.Combine(_outputDirectory, _namespace + ".dll");
_generatedPdbFile = Path.Combine(_outputDirectory, _namespace + ".pdb");
_generatedXmlDocFile = Path.Combine(_outputDirectory, _namespace + ".xml");
}
public void Compile()
{
CleanOutputDirectory();
// Convert the solc data into compiled resx form.
var generatedResxResourceDescription = CreateSolcResxResource(_namespace);
var manifestResources = new[] { generatedResxResourceDescription };
// Create compilation options (output library, debug mode, any CPU).
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithGeneralDiagnosticOption(ReportDiagnostic.Warn)
.WithReportSuppressedDiagnostics(true)
.WithPlatform(Platform.AnyCpu)
.WithOptimizationLevel(OptimizationLevel.Release);
// Add runtime, Meadow, and other dependency assembly references.
var metadataReferences = GetReferencedAssemblies(typeof(BaseContract).Assembly)
.Select(a => MetadataReference.CreateFromFile(a.Value.Location)).ToArray();
// Create compilation context with the code syntax trees and resx resources.
var compileContext = CSharpCompilation.Create(
assemblyName: _namespace,
syntaxTrees: _codeGenResults.GeneratedCSharpEntries.Select(g => g.SyntaxTree),
references: metadataReferences,
options: compilationOptions);
EmitResult emitResult;
using (var asmFileStream = new FileStream(_generatedAssemblyFile, FileMode.OpenOrCreate, FileAccess.ReadWrite))
using (var pdbFileStream = new FileStream(_generatedPdbFile, FileMode.OpenOrCreate, FileAccess.ReadWrite))
using (var xmlDocFileStream = new FileStream(_generatedXmlDocFile, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
var emitOptions = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb);
// Compile sources into assembly, pdb and xmldoc.
emitResult = compileContext.Emit(
options: emitOptions,
peStream: asmFileStream,
pdbStream: pdbFileStream,
xmlDocumentationStream: xmlDocFileStream,
manifestResources: manifestResources);
}
CheckEmitResult(emitResult);
_codeGenResults.CompilationResults = new SolCodeGenCompilationResults
{
AssemblyFilePath = _generatedAssemblyFile,
PdbFilePath = _generatedPdbFile,
XmlDocFilePath = _generatedXmlDocFile
};
}
// Deletes all the files (recursive) in the provided directory. Creates the directory if does not exist.
void CleanOutputDirectory()
{
if (Directory.Exists(_outputDirectory))
{
var existingFiles = Directory.GetFiles(_outputDirectory, "*.*", SearchOption.AllDirectories);
foreach (var file in existingFiles)
{
File.Delete(file);
}
}
else
{
Directory.CreateDirectory(_outputDirectory);
}
}
// Convert the solc data into compiled resx form.
ResourceDescription CreateSolcResxResource(string generatedAsmName)
{
Stream CreateResxDataStream()
{
var backingStream = new MemoryStream();
using (var resourceWriter = new ResourceWriter(backingStream))
{
foreach (var resxEntry in _codeGenResults.GeneratedResxResources)
{
resourceWriter.AddResource(resxEntry.Key, resxEntry.Value);
}
resourceWriter.Generate();
return new MemoryStream(backingStream.GetBuffer(), 0, (int)backingStream.Length);
}
}
var resxName = $"{generatedAsmName}.{CodebaseGenerator.SolcOutputDataResxFile}.sol.resources";
var generatedResxResourceDescription = new ResourceDescription(
resourceName: resxName,
dataProvider: CreateResxDataStream,
isPublic: true);
return generatedResxResourceDescription;
}
// Throws exception if emit failed.
void CheckEmitResult(EmitResult emitResult)
{
if (!emitResult.Success)
{
var errors = emitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToArray();
var exceptions = errors.Select(s => new Exception(s.GetMessage(CultureInfo.InvariantCulture))).ToArray();
if (exceptions.Length > 1)
{
throw new AggregateException("Errors compiling generated code", exceptions);
}
else
{
throw exceptions[0];
}
}
}
public static Dictionary<string, Assembly> GetReferencedAssemblies(Assembly assembly)
{
var context = DependencyContext.Load(assembly);
if (context == null)
{
return GetReferencedAssembliesManual(assembly);
}
var items = context.GetDefaultAssemblyNames();
return items.ToDictionary(t => t.FullName, t => Assembly.Load(t));
}
// Recursively gets the assembly dependencies of a given assembly.
public static Dictionary<string, Assembly> GetReferencedAssembliesManual(Assembly assembly)
{
void Iterate(Assembly asm, Dictionary<string, Assembly> assemblies)
{
if (!assemblies.ContainsKey(asm.FullName))
{
assemblies.Add(asm.FullName, asm);
var referenced = asm.GetReferencedAssemblies();
foreach (var child in referenced)
{
Assembly childAsm;
try
{
childAsm = Assembly.Load(child);
}
catch
{
var simpleAssemblyName = new AssemblyName { Name = child.Name };
childAsm = Assembly.Load(simpleAssemblyName);
}
Iterate(childAsm, assemblies);
}
}
}
var dict = new Dictionary<string, Assembly>();
Iterate(assembly, dict);
return dict;
}
}
}