// 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;
        }