| | | 1 | | using Microsoft.CodeAnalysis; |
| | | 2 | | using Microsoft.CodeAnalysis.CSharp; |
| | | 3 | | using pva.Helpers.Extensions; |
| | | 4 | | using pva.SuperV.Engine.Exceptions; |
| | | 5 | | using System.Dynamic; |
| | | 6 | | using System.Text; |
| | | 7 | | |
| | | 8 | | namespace pva.SuperV.Engine |
| | | 9 | | { |
| | | 10 | | /// <summary> |
| | | 11 | | /// Class for building a <see cref="WipProject"/> and convert it to a <see cref="RunnableProject"/> by generating an |
| | | 12 | | /// </summary> |
| | | 13 | | public static class ProjectBuilder |
| | | 14 | | { |
| | | 15 | | /// <summary> |
| | | 16 | | /// Builds the specified <see cref="WipProject"/>. |
| | | 17 | | /// </summary> |
| | | 18 | | /// <param name="wipProject">The WIP project.</param> |
| | | 19 | | /// <returns>a <see cref="RunnableProject"/></returns> |
| | | 20 | | /// <exception cref="pva.SuperV.Engine.Exceptions.ProjectBuildException"></exception> |
| | | 21 | | public static async Task<RunnableProject> BuildAsync(WipProject wipProject) |
| | 134 | 22 | | { |
| | 134 | 23 | | RunnableProject runnableProject = wipProject.CloneAsRunnable(); |
| | 132 | 24 | | wipProject.Dispose(); |
| | 132 | 25 | | await BuildAsync(runnableProject); |
| | 132 | 26 | | return runnableProject; |
| | 132 | 27 | | } |
| | | 28 | | |
| | | 29 | | public static async ValueTask BuildAsync(RunnableProject runnableProject) |
| | 276 | 30 | | { |
| | 276 | 31 | | if (!File.Exists(runnableProject.GetAssemblyFileName())) |
| | 132 | 32 | | { |
| | 132 | 33 | | string projectAssemblyFileName = runnableProject.GetAssemblyFileName(); |
| | 132 | 34 | | string projectCode = runnableProject.GetCode(); |
| | 132 | 35 | | var compilation = CreateCompilation(CSharpSyntaxTree.ParseText(projectCode), $"{runnableProject.Name}-V{ |
| | 132 | 36 | | await using MemoryStream dllStream = new(); |
| | 132 | 37 | | await using MemoryStream pdbStream = new(); |
| | 132 | 38 | | await using Stream win32ResStream = compilation.CreateDefaultWin32Resources( |
| | 132 | 39 | | versionResource: true, // Important! |
| | 132 | 40 | | noManifest: false, |
| | 132 | 41 | | manifestContents: null, |
| | 132 | 42 | | iconInIcoFormat: null); |
| | 132 | 43 | | var compilationResult = compilation.Emit( |
| | 132 | 44 | | peStream: dllStream, |
| | 132 | 45 | | pdbStream: pdbStream, |
| | 132 | 46 | | win32Resources: win32ResStream); |
| | | 47 | | |
| | 132 | 48 | | if (!compilationResult.Success) |
| | 0 | 49 | | { |
| | 0 | 50 | | StringBuilder diagnostics = new(); |
| | 0 | 51 | | compilationResult.Diagnostics |
| | 0 | 52 | | .ForEach(diagnostic => diagnostics.AppendLine(diagnostic.ToString())); |
| | 0 | 53 | | throw new ProjectBuildException(runnableProject, diagnostics.ToString()); |
| | | 54 | | } |
| | 132 | 55 | | await File.WriteAllBytesAsync(projectAssemblyFileName, dllStream.ToArray()); |
| | 132 | 56 | | } |
| | 276 | 57 | | } |
| | | 58 | | |
| | | 59 | | /// <summary> |
| | | 60 | | /// Creates a <see cref="CSharpCompilation"/> to generate an assembly for the project. |
| | | 61 | | /// </summary> |
| | | 62 | | /// <param name="tree">The tree.</param> |
| | | 63 | | /// <param name="name">The name.</param> |
| | | 64 | | /// <returns>A <see cref="CSharpCompilation"/></returns> |
| | | 65 | | private static CSharpCompilation CreateCompilation(SyntaxTree tree, string name) |
| | 132 | 66 | | { |
| | 132 | 67 | | var systemAssembliesPath = Path.GetDirectoryName(typeof(object).Assembly.Location); |
| | 132 | 68 | | List<MetadataReference> refs = |
| | 132 | 69 | | [ |
| | 132 | 70 | | /* |
| | 132 | 71 | | * Adding some necessary .NET assemblies |
| | 132 | 72 | | * These assemblies couldn't be loaded correctly via the same construction as above, |
| | 132 | 73 | | * in specific the System.Runtime. |
| | 132 | 74 | | */ |
| | 132 | 75 | | MetadataReference.CreateFromFile(Path.Combine(systemAssembliesPath!, "mscorlib.dll")), |
| | 132 | 76 | | MetadataReference.CreateFromFile(Path.Combine(systemAssembliesPath!, "System.dll")), |
| | 132 | 77 | | MetadataReference.CreateFromFile(Path.Combine(systemAssembliesPath!, "System.Core.dll")), |
| | 132 | 78 | | MetadataReference.CreateFromFile(Path.Combine(systemAssembliesPath!, "System.Runtime.dll")), |
| | 132 | 79 | | MetadataReference.CreateFromFile(Path.Combine(systemAssembliesPath!, "System.Collections.dll")), |
| | 132 | 80 | | MetadataReference.CreateFromFile(Path.Combine(systemAssembliesPath!, "System.Dynamic.Runtime.dll")), |
| | 132 | 81 | | MetadataReference.CreateFromFile(Path.Combine(systemAssembliesPath!, "Microsoft.CSharp.dll")), |
| | 132 | 82 | | MetadataReference.CreateFromFile(typeof(ExpandoObject).Assembly.Location), |
| | 132 | 83 | | // Basic types assembly |
| | 132 | 84 | | MetadataReference.CreateFromFile(typeof(string).Assembly.Location), |
| | 132 | 85 | | // SuperV Project assembly |
| | 132 | 86 | | MetadataReference.CreateFromFile(typeof(Project).Assembly.Location) |
| | 132 | 87 | | ]; |
| | | 88 | | |
| | 132 | 89 | | return CSharpCompilation |
| | 132 | 90 | | .Create(name, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) |
| | 132 | 91 | | .AddReferences(refs) |
| | 132 | 92 | | .AddSyntaxTrees(tree); |
| | 132 | 93 | | } |
| | | 94 | | } |
| | | 95 | | } |