private static void BasicBatchCompilation(HttpContext context, CompilerInfo compInfo, PagesWithSameCompilerInfo pwsci) { ICodeCompiler compiler = pwsci.CodeProvider.CreateCompiler(); Debug.Trace("Batching", "Compiling " + pwsci.PageCount + " pages"); CompilerParameters compilParams = compInfo.CompilParams; compilParams.TempFiles = new TempFileCollection(HttpRuntime.CodegenDirInternal); // Create the resource file (shared by all the pages in the bucket) if (pwsci._stringResourceBuilder.HasStrings) { string resFileName = compilParams.TempFiles.AddExtension("res"); pwsci._stringResourceBuilder.CreateResourceFile(resFileName); compilParams.Win32Resource = resFileName; } // Never generate debug code when we're batching compilParams.TempFiles.KeepFiles = false; compilParams.IncludeDebugInformation = false; // Compute a table of all the assemblies used by all the pages in the // bucket, removing duplicates Hashtable allAssemblies = new Hashtable(); // Place all the generated source file names in an array string[] files = new string[pwsci.PageCount]; int fileCount = 0; foreach (BatchCompilationEntry e in pwsci.Pages) { Debug.Assert(FileUtil.FileExists(e.GeneratedSourceFile), e.GeneratedSourceFile + " is missing!"); files[fileCount++] = e.GeneratedSourceFile; // Add all the assemblies if (e.AssemblyDependencies != null) { foreach (Assembly assembly in e.AssemblyDependencies.Keys) { string assemblyName = Util.GetAssemblyCodeBase(assembly); allAssemblies[assemblyName] = null; } } } Debug.Assert(fileCount == pwsci.PageCount, "fileCount == pwsci.PageCount"); // Now, add all the (non-duplicate) assemblies to the compilParams foreach (string aname in allAssemblies.Keys) { compilParams.ReferencedAssemblies.Add(aname); } // Compile them all together into an assembly CompilerResults results; try { results = compiler.CompileAssemblyFromFileBatch(compilParams, files); } catch (Exception e) { Debug.Trace("Batching", "Compilation failed! " + e.Message); throw new HttpUnhandledException(HttpRuntime.FormatResourceString(SR.CompilationUnhandledException, pwsci.CodeProvider.GetType().FullName), e); } finally { // Delete all the generated source files for (int i = 0; i < fileCount; i++) { File.Delete(files[i]); } } BaseCompiler.ThrowIfCompilerErrors(results, pwsci.CodeProvider, null, null, null); // Note the assembly that everything ended up in foreach (BatchCompilationEntry e in pwsci.Pages) { e.SetTargetAssembly(results.CompiledAssembly); CacheResults(context, e); // Do some cleanup e.PostCompilation(); } }
private static void BatchCompile(ArrayList inputList, HttpContext context, string virtualDir) { Exception errorException = null; // Used to create temporary source files TempFileCollection tempFiles = new TempFileCollection(HttpRuntime.CodegenDirInternal); // Counter to name generated files uniquely int fileCount = 0; int maxBatchGeneratedFileSize = CompilationConfiguration.GetMaxBatchGeneratedFileSize(context); int maxBatchSize = CompilationConfiguration.GetMaxBatchSize(context); Hashtable languageBuckets = new Hashtable(); // Go through all the files that need to be compiled foreach (BatchCompilationEntry currentPage in inputList) { // precompile, and skip pages that fail to precompile try { currentPage.Precompile(); } catch (Exception e) { // remember the first exception if (errorException == null) { errorException = e; } Debug.Trace("Batching", "Skipping " + currentPage.PageFilename + " due to parse error (" + e.Message + ")"); continue; } // Skip trivial pages and pages that have the debug flag if (currentPage.IsTrivialPage() || currentPage.IsDebugPage()) { continue; } // Determine what language bucket it belongs to based on the CompilerInfo CompilerInfo compInfo = currentPage.CompilerInfo; PagesWithSameCompilerInfo pwsci = (PagesWithSameCompilerInfo)languageBuckets[compInfo]; if (pwsci == null) { pwsci = new PagesWithSameCompilerInfo(currentPage.CompilerInfo.CompilerType, maxBatchGeneratedFileSize * 1024, maxBatchSize); languageBuckets[compInfo] = pwsci; } ICodeGenerator generator = pwsci.CodeProvider.CreateGenerator(); // Build the CodeDOM tree for the page currentPage.BuildCodeModel(generator, pwsci._stringResourceBuilder); CodeCompileUnit compileUnit = currentPage.GetCodeModel(); // Generate a temporary source file from the CodeDOM tree string filename = tempFiles.AddExtension( (fileCount++) + "." + pwsci.CodeProvider.FileExtension, true /*keepFiles*/); Stream temp = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read); try { StreamWriter sw = new StreamWriter(temp, Encoding.UTF8); generator.GenerateCodeFromCompileUnit(compileUnit, sw, null /*CodeGeneratorOptions*/); sw.Flush(); sw.Close(); } finally { temp.Close(); } currentPage.GeneratedSourceFile = filename; // This releases a number of things that are no longer needed after this point currentPage.PostSourceCodeGeneration(); // Add it to the language bucket pwsci.AddPage(currentPage); // If the bucket is full, compile all its pages and get rid of it if (pwsci.IsBucketFull) { try { BasicBatchCompilation(context, compInfo.Clone(), pwsci); // Tell the server that we're still running to make sure it doesn't kill us (ASURT 96452) context.SendEmptyResponse(); } catch (Exception e) { // remember the first exception if (errorException == null) { errorException = e; } } languageBuckets.Remove(compInfo); } } // Compile whatever is left in all the buckets for (IDictionaryEnumerator de = (IDictionaryEnumerator)languageBuckets.GetEnumerator(); de.MoveNext();) { try { BasicBatchCompilation(context, ((CompilerInfo)de.Key).Clone(), (PagesWithSameCompilerInfo)de.Value); } catch (Exception e) { // remember the first exception if (errorException == null) { errorException = e; } } } // If there was an error, rethrow it if (errorException != null) { throw new HttpException(null, errorException); } }