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.Linq;
10 using System.Text;
11 using System.Text.Json;
12 using System.Reflection;
13 using System.Collections.Generic;
14 using System.Runtime.InteropServices;
15 using Microsoft.CodeAnalysis;
16 using Microsoft.CodeAnalysis.CSharp;
17 using Microsoft.CodeAnalysis.Text;
18 using Antmicro.Renode.Utilities;
19 using Antmicro.Renode.Exceptions;
20 
21 namespace Antmicro.Renode.TAPHelper
22 {
23     public class DynamicModuleSpawner
24     {
GetTAPHelper()25         public static string GetTAPHelper()
26         {
27             var generatedFilePath = TemporaryFilesManager.Instance.GetTemporaryFile();
28             var outputFilePath = Path.ChangeExtension(generatedFilePath, ".dll");
29             var outputFileName = Path.GetFileName(outputFilePath);
30 
31             GenerateTAPHelper(outputFilePath, outputFileName);
32 
33             // Generate runtimeconfig.json file necessary to run standalone application with dotnet on .NETCore and above
34             File.WriteAllText(
35                 Path.ChangeExtension(generatedFilePath, "runtimeconfig.json"),
36                 GenerateRuntimeConfig()
37             );
38 
39             // Copy Infrastructure.dll to temp directory
40             var currentAssemblyPath = Assembly.GetExecutingAssembly().Location;
41             var targetPath = Path.Combine(TemporaryFilesManager.Instance.EmulatorTemporaryPath, "Infrastructure.dll");
42             File.Copy(currentAssemblyPath, targetPath, true);
43 
44             return outputFilePath;
45         }
46 
GenerateTAPHelper(string path, string filename)47         private static void GenerateTAPHelper(string path, string filename)
48         {
49             var sourceCode = @"
50                 using System.Runtime.InteropServices;
51                 using Antmicro.Renode.TAPHelper;
52                 public class TAP
53                 {
54                     public static int Main(string[] args)
55                     {
56                         var deviceName = args[0];
57                         var persistent = bool.Parse(args[1]);
58                         var dev = Marshal.StringToCoTaskMemAuto(deviceName);
59                         var err = TAPTools.OpenTAP(dev, persistent);
60                         Marshal.FreeCoTaskMem(dev);
61                         if (err < 0)
62                         {
63                             return 1;
64                         }
65                         return 0;
66                     }
67                 }";
68             var codeString = SourceText.From(sourceCode);
69             var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9);
70 
71             var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
72 
73             var references = new List<MetadataReference>
74             {
75                 MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
76             };
77 
78             AssemblyHelper.GetAssembliesLocations().ToList()
79                 .ForEach(location => references.Add(MetadataReference.CreateFromFile(location)));
80 
81             var result = CSharpCompilation.Create(filename,
82                 new[] { parsedSyntaxTree },
83                 references: references,
84                 options: new CSharpCompilationOptions(
85                     OutputKind.ConsoleApplication,
86                     optimizationLevel: OptimizationLevel.Release,
87                     assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default)).Emit(path);
88 
89             if (!result.Success)
90             {
91                 var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
92                 var diagnosticString = string.Join(Environment.NewLine, failures.Select(x => x.ToString()));
93                 throw new RecoverableException("Could not compile TAP assembly. \n" + diagnosticString);
94             }
95         }
96 
GenerateRuntimeConfig()97         private static string GenerateRuntimeConfig()
98         {
99             // It writes JSON of the following form:
100             // {
101             //   "runtimeOptions": {
102             //     "framework": {
103             //       "name": "Microsoft.NETCore.App",
104             //       "version": "5.0.5"
105             //     }
106             //   }
107             // }
108             using (var stream = new MemoryStream())
109             {
110                 using (var writer = new Utf8JsonWriter(
111                     stream,
112                     new JsonWriterOptions() { Indented = true }
113                 ))
114                 {
115                     writer.WriteStartObject();
116                     writer.WriteStartObject("runtimeOptions");
117                     writer.WriteStartObject("framework");
118                     writer.WriteString("name", "Microsoft.NETCore.App");
119                     writer.WriteString(
120                         "version",
121                         RuntimeInformation.FrameworkDescription.Replace(".NET ", "")
122                     );
123                     writer.WriteEndObject();
124                     writer.WriteEndObject();
125                     writer.WriteEndObject();
126                 }
127 
128                 return Encoding.UTF8.GetString(stream.ToArray());
129             }
130         }
131 
DynamicModuleSpawner()132         private DynamicModuleSpawner()
133         {
134         }
135     }
136 }