Backend/RiderPlugin/ForTea.RiderPlugin/TemplateProcessing/Managing/Impl/T4TemplateCompiler.cs (116 lines of code) (raw):

using System.Collections.Generic; using GammaJul.ForTea.Core.Parsing; using GammaJul.ForTea.Core.TemplateProcessing; using GammaJul.ForTea.Core.TemplateProcessing.CodeCollecting.Interrupt; using GammaJul.ForTea.Core.Tree; using JetBrains.Annotations; using JetBrains.Application.Parts; using JetBrains.Application.Threading; using JetBrains.ForTea.RiderPlugin.Model; using JetBrains.ForTea.RiderPlugin.TemplateProcessing.CodeGeneration; using JetBrains.ForTea.RiderPlugin.TemplateProcessing.CodeGeneration.Reference; using JetBrains.Lifetimes; using JetBrains.ProjectModel; using JetBrains.ReSharper.Psi; using JetBrains.ReSharper.Psi.Tree; using JetBrains.Util; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; namespace JetBrains.ForTea.RiderPlugin.TemplateProcessing.Managing.Impl { [SolutionComponent(Instantiation.DemandAnyThreadUnsafe)] public sealed class T4TemplateCompiler : IT4TemplateCompiler { [NotNull] private IShellLocks Locks { get; } [NotNull] private IT4TargetFileManager TargetManager { get; } [NotNull] private IT4ReferenceExtractionManager ReferenceExtractionManager { get; } [NotNull] private IT4BuildMessageConverter Converter { get; } [NotNull] private ILogger Logger { get; } public T4TemplateCompiler( [NotNull] IShellLocks locks, [NotNull] IT4TargetFileManager targetManager, [NotNull] IT4BuildMessageConverter converter, [NotNull] IT4ReferenceExtractionManager referenceExtractionManager, [NotNull] ILogger logger ) { Locks = locks; TargetManager = targetManager; Converter = converter; ReferenceExtractionManager = referenceExtractionManager; Logger = logger; } [NotNull] public T4BuildResult Compile(Lifetime lifetime, IPsiSourceFile sourceFile) { Logger.Verbose("Compiling a file"); Locks.AssertReadAccessAllowed(); // Since we need no context when compiling a file, we need to build the tree manually var file = sourceFile.BuildT4Tree(); var error = file.ThisAndDescendants<IErrorElement>().Collect(); if (!error.IsEmpty()) return Converter.SyntaxErrors(error); return lifetime.UsingNested(nested => { try { // Prepare the code var references = ReferenceExtractionManager.ExtractPortableReferencesForCompilation(lifetime, file); string code = GenerateCode(file); // Prepare the paths var executablePath = TargetManager.GetTemporaryExecutableLocation(file); var compilation = CreateCompilation(code, references, executablePath); var location = executablePath.Parent; switch (location.Exists) { case FileSystemPath.Existence.File: location.DeleteFile(); goto case FileSystemPath.Existence.Missing; case FileSystemPath.Existence.Missing: location.CreateDirectory(); break; } var pdbPath = location.Combine(executablePath.Name.WithOtherExtension("pdb")); // Delegate to Roslyn var emitOptions = new EmitOptions( debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: pdbPath.FullPath ); using var executableStream = executablePath.OpenFileForWriting(); using var pdbStream = pdbPath.OpenFileForWriting(); var emitResult = compilation.Emit( peStream: executableStream, pdbStream: pdbStream, options: emitOptions, cancellationToken: nested ); return Converter.ToT4BuildResult(emitResult.Diagnostics.AsList(), file); } catch (T4OutputGenerationException e) { return Converter.ToT4BuildResult(e); } }); } [NotNull] private string GenerateCode([NotNull] IT4File file) { Locks.AssertReadAccessAllowed(); return T4RiderCodeGeneration.GenerateExecutableCode(file).RawText; } private static CSharpCompilation CreateCompilation( [NotNull] string code, [NotNull] IEnumerable<MetadataReference> references, [NotNull] VirtualFileSystemPath executablePath ) { var options = new CSharpCompilationOptions(OutputKind.ConsoleApplication) .WithOptimizationLevel(OptimizationLevel.Debug) .WithMetadataImportOptions(MetadataImportOptions.Public); var syntaxTree = SyntaxFactory.ParseSyntaxTree( code, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest)); return CSharpCompilation.Create( executablePath.Name, new[] { syntaxTree }, options: options, references: references); } } }