1 //
2 // Copyright (c) 2010-2022 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.IO;
9 using System.CodeDom.Compiler;
10 using System.Collections.Generic;
11 using System.Linq;
12 using Antmicro.Renode.Exceptions;
13 using System.Reflection;
14 using Microsoft.CodeAnalysis;
15 using Microsoft.CodeAnalysis.CSharp;
16 using Microsoft.CodeAnalysis.Text;
17 
18 namespace Antmicro.Renode.Utilities
19 {
20     public class AdHocCompiler
21     {
Compile(string sourcePath)22         public string Compile(string sourcePath)
23         {
24             var tempFilePath = TemporaryFilesManager.Instance.GetTemporaryFile();
25             // With .NET Core and above, one must explicitly specify a .dll extension for output assembly
26             var outputFilePath = Path.ChangeExtension(tempFilePath, ".dll");
27             var outputFileName = Path.GetFileName(outputFilePath);
28 
29             var sourceCode = File.ReadAllText(sourcePath);
30             var codeString = SourceText.From(sourceCode);
31             var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9);
32 
33             var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
34 
35             var references = new List<MetadataReference>
36             {
37                 MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
38             };
39 
40             AssemblyHelper.GetAssembliesLocations().ToList()
41                 .ForEach(location => references.Add(MetadataReference.CreateFromFile(location)));
42 
43             var result = CSharpCompilation.Create(outputFileName,
44                 new[] { parsedSyntaxTree },
45                 references: references,
46                 options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
47                     optimizationLevel: OptimizationLevel.Release,
48                     assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default)).Emit(outputFilePath);
49 
50             if (!result.Success)
51             {
52                 // Access diagnostic informations
53                 var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
54                 var diagnosticString = string.Join(Environment.NewLine, failures.Select(x => x.ToString()));
55                 throw new RecoverableException($"Could not compile assembly from: {sourcePath}\n{diagnosticString}");
56             }
57 
58             return outputFilePath;
59         }
60     }
61 }