// Wyam - Use the Roslyn scripting engine for compilation // In MVC, this part is done in RoslynCompilationService private Type Compile([NotNull] RelativeFileInfo file, [NotNull] string compilationContent) { HashSet <Assembly> assemblies = new HashSet <Assembly>(new AssemblyEqualityComparer()) { Assembly.GetAssembly(typeof(Modules.Razor.Razor)) // Wyam.Modules.Razor }; if (_executionContext.Assemblies != null) { assemblies.UnionWith(_executionContext.Assemblies); } var assemblyName = Path.GetRandomFileName(); var parseOptions = new CSharpParseOptions(); var syntaxTree = CSharpSyntaxTree.ParseText(SourceText.From(compilationContent, Encoding.UTF8), parseOptions, assemblyName); var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location); var compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, assemblies.Select(x => _metadataReferences.GetOrAdd(x.Location, y => MetadataReference.CreateFromFile(y))), compilationOptions) .AddReferences( // For some reason, Roslyn really wants these added by filename // See http://stackoverflow.com/questions/23907305/roslyn-has-no-reference-to-system-runtime _metadataReferences.GetOrAdd(Path.Combine(assemblyPath, "mscorlib.dll"), x => MetadataReference.CreateFromFile(x)), _metadataReferences.GetOrAdd(Path.Combine(assemblyPath, "System.dll"), x => MetadataReference.CreateFromFile(x)), _metadataReferences.GetOrAdd(Path.Combine(assemblyPath, "System.Core.dll"), x => MetadataReference.CreateFromFile(x)), _metadataReferences.GetOrAdd(Path.Combine(assemblyPath, "System.Runtime.dll"), x => MetadataReference.CreateFromFile(x)) ); if (_executionContext.RawConfigAssembly != null && _executionContext.RawConfigAssembly.Length > 0) { using (MemoryStream memoryStream = new MemoryStream(_executionContext.RawConfigAssembly)) { compilation = compilation.AddReferences(MetadataReference.CreateFromStream(memoryStream)); } } using (var ms = new MemoryStream()) { EmitResult result = compilation.Emit(ms); if (!result.Success) { Trace.Error("{0} errors compiling {1}:{2}{3}", result.Diagnostics.Length, file.RelativePath, Environment.NewLine, string.Join(Environment.NewLine, result.Diagnostics)); throw new AggregateException(result.Diagnostics.Select(x => new Exception(x.ToString()))); } ms.Seek(0, SeekOrigin.Begin); byte[] assemblyBytes = ms.ToArray(); Assembly assembly = Assembly.Load(assemblyBytes); var type = assembly.GetExportedTypes() .First(t => t.Name.StartsWith(_razorHost.MainClassNamePrefix, StringComparison.Ordinal)); return(type); } }
/// <inheritdoc /> public Type Compile([NotNull] RelativeFileInfo file) { GeneratorResults results; using (var inputStream = file.FileInfo.CreateReadStreamWithRetry()) { results = GenerateCode(file.RelativePath, inputStream); } if (!results.Success) { Trace.Error("{0} errors parsing {1}:{2}{3}", results.ParserErrors.Count(), file.RelativePath, Environment.NewLine, string.Join(Environment.NewLine, results.ParserErrors)); throw new AggregateException(results.ParserErrors.Select(x => new Exception(x.Message))); } return(Compile(file, results.GeneratedCode)); }
public IRazorPage CreateInstance([NotNull] string relativePath, string content) { if (relativePath.StartsWith("~/", StringComparison.Ordinal)) { // For tilde slash paths, drop the leading ~ to make it work with the underlying IFileProvider. relativePath = relativePath.Substring(1); } // Code below is taken from CompilerCache (specifically OnCacheMiss) which is responsible for managing the compilation step in MVC // Check the file var fileProvider = content == null ? _fileProvider : new DocumentFileProvider(_rootDirectory, content); var fileInfo = fileProvider.GetFileInfo(relativePath); if (!fileInfo.Exists) { return null; } // If relative path is the root, it probably means this isn't from reading a file so don't bother with caching string hash = null; if (relativePath != "/") { // Check the cache, always just use the hash because we're going to have to store it anyway and the content might not come from a file CacheEntry cacheEntry; hash = RazorFileHash.GetHash(fileInfo); if (_pageCache.TryGetValue(relativePath, out cacheEntry) && cacheEntry.Hash == hash) { return cacheEntry.Page; } } // Compile and store in cache var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); Type result = _razorcompilationService.Compile(relativeFileInfo); IRazorPage page = (IRazorPage)Activator.CreateInstance(result); page.Path = relativePath; if (relativePath != "/") { _pageCache[relativePath] = new CacheEntry { Page = page, Hash = hash }; } return page; }
public IRazorPage CreateInstance([NotNull] string relativePath, Stream stream) { if (relativePath.StartsWith("~/", StringComparison.Ordinal)) { // For tilde slash paths, drop the leading ~ to make it work with the underlying IFileProvider. relativePath = relativePath.Substring(1); } // Code below is taken from CompilerCache (specifically OnCacheMiss) which is responsible for managing the compilation step in MVC // Check the file var fileProvider = stream == null ? _fileProvider : new DocumentFileProvider(_rootDirectory, stream); var fileInfo = fileProvider.GetFileInfo(relativePath); if (!fileInfo.Exists) { return null; } // If relative path is the root, it probably means this isn't from reading a file so don't bother with caching IExecutionCache cache = relativePath == "/" ? null : _executionContext.ExecutionCache; string key = null; Type pageType = null; if (cache != null) { key = relativePath + " " + RazorFileHash.GetHash(fileInfo); if (!cache.TryGetValue(key, out pageType)) { pageType = null; } } // Compile and store in cache if not found if (pageType == null) { var relativeFileInfo = new RelativeFileInfo(fileInfo, relativePath); pageType = _razorcompilationService.Compile(relativeFileInfo); cache?.Set(key, pageType); } // Create an return a new page instance IRazorPage page = (IRazorPage)Activator.CreateInstance(pageType); page.Path = relativePath; return page; }