Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / xsp / System / Web / Compilation / BuildProvidersCompiler.cs / 4 / BuildProvidersCompiler.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.Compilation { using System; using System.IO; using System.Collections; using System.Collections.Specialized; using System.Reflection; using System.Globalization; using System.CodeDom; using System.CodeDom.Compiler; using System.Configuration; using System.Web.Hosting; using System.Web.Util; using System.Web.Caching; using System.Web.UI; using System.Web.Configuration; internal class BuildProvidersCompiler { private ICollection _buildProviders; private VirtualPath _configPath; private bool _supportLocalization; // The set of assemblies that we need to reference when compiling private ICollection _referencedAssemblies; internal ICollection ReferencedAssemblies { get { return _referencedAssemblies; } } private AssemblyBuilder _assemblyBuilder; // Key: CultureName string, Value: AssemblyBuilder private IDictionary _satelliteAssemblyBuilders = null; // If this is set, we only generate the source files into this directory // without compiling them. // This is used to implement ClientBuildManager.GetCodeDirectoryInformation private string _generatedFilesDir; internal BuildProvidersCompiler(VirtualPath configPath, string outputAssemblyName) : this(configPath, false, outputAssemblyName) { } internal BuildProvidersCompiler(VirtualPath configPath, bool supportLocalization, string outputAssemblyName) { _configPath = configPath; _supportLocalization = supportLocalization; _compConfig = RuntimeConfig.GetConfig(_configPath).Compilation; _referencedAssemblies = BuildManager.GetReferencedAssemblies(CompConfig); _outputAssemblyName = outputAssemblyName; } internal BuildProvidersCompiler(VirtualPath configPath, bool supportLocalization, string generatedFilesDir, int index) { _configPath = configPath; _supportLocalization = supportLocalization; _compConfig = RuntimeConfig.GetConfig(_configPath).Compilation; _referencedAssemblies = BuildManager.GetReferencedAssemblies(CompConfig, index); _generatedFilesDir = generatedFilesDir; } // Theconfig section for the set of build providers that we handle private CompilationSection _compConfig; internal CompilationSection CompConfig { get { return _compConfig; } } private string _outputAssemblyName; internal string OutputAssemblyName { get { return _outputAssemblyName; } } private bool CbmGenerateOnlyMode { get { return _generatedFilesDir != null; } } internal void SetBuildProviders(ICollection buildProviders) { _buildProviders = buildProviders; } private void ProcessBuildProviders() { CompilerType compilerType = null; BuildProvider firstLanguageBuildProvider = null; // First, delete all the existing satellite assemblies of the assembly // we're about to build (VSWhidbey 87022) (only if it has a fixed name) if (OutputAssemblyName != null) { Debug.Assert(!CbmGenerateOnlyMode); StandardDiskBuildResultCache.RemoveSatelliteAssemblies(OutputAssemblyName); } // List of BuildProvider's that don't ask for a specific language ArrayList languageFreeBuildProviders = null; foreach (BuildProvider buildProvider in _buildProviders) { // If it's an InternalBuildProvider, give it the assembly references early on buildProvider.SetReferencedAssemblies(_referencedAssemblies); // Instruct the internal build providers to continue processing for more parse errors. if (!BuildManager.ThrowOnFirstParseError) { InternalBuildProvider provider = buildProvider as InternalBuildProvider; if (provider != null) { provider.ThrowOnFirstParseError = false; } } // Get the language and culture CompilerType ctwp = BuildProvider.GetCompilerTypeFromBuildProvider(buildProvider); // Only look for a culture if we're supposed to (basically, in the resources directories) string cultureName = null; if (_supportLocalization) cultureName = buildProvider.GetCultureName(); // Is it asking for a specific language? if (ctwp != null) { // If it specifies a language, it can't also have a culture if (cultureName != null) { throw new HttpException(SR.GetString( SR.Both_culture_and_language, BuildProvider.GetDisplayName(buildProvider))); } // Do we already know the language we'll be using if (compilerType != null) { // If it's different from the current one, fail if (!ctwp.Equals(compilerType)) { throw new HttpException(SR.GetString( SR.Inconsistent_language, BuildProvider.GetDisplayName(buildProvider), BuildProvider.GetDisplayName(firstLanguageBuildProvider))); } } else { // Keep track of the build provider of error handling purpose firstLanguageBuildProvider = buildProvider; // Keep track of the language compilerType = ctwp; _assemblyBuilder = compilerType.CreateAssemblyBuilder( CompConfig, _referencedAssemblies, _generatedFilesDir, OutputAssemblyName); } } else { if (cultureName != null) { // Ignore the culture files in generate-only mode if (CbmGenerateOnlyMode) continue; if (_satelliteAssemblyBuilders == null) { _satelliteAssemblyBuilders = new Hashtable( StringComparer.OrdinalIgnoreCase); } // Check if we already have an assembly builder for this culture AssemblyBuilder satelliteAssemblyBuilder = (AssemblyBuilder) _satelliteAssemblyBuilders[cultureName]; // If not, create one and store it in the hashtable if (satelliteAssemblyBuilder == null) { satelliteAssemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( CompConfig, _referencedAssemblies, _configPath, OutputAssemblyName); satelliteAssemblyBuilder.CultureName = cultureName; _satelliteAssemblyBuilders[cultureName] = satelliteAssemblyBuilder; } satelliteAssemblyBuilder.AddBuildProvider(buildProvider); continue; } if (_assemblyBuilder == null) { // If this provider doesn't need a specific language, and we don't know // the language yet, just keep track of it if (languageFreeBuildProviders == null) languageFreeBuildProviders = new ArrayList(); languageFreeBuildProviders.Add(buildProvider); continue; } } _assemblyBuilder.AddBuildProvider(buildProvider); } // If we didn't get an AssemblyBuilder, use a default if (_assemblyBuilder == null && languageFreeBuildProviders != null) { _assemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( CompConfig, _referencedAssemblies, _configPath, _generatedFilesDir, OutputAssemblyName); } // Add all the language free providers (if any) to the AssemblyBuilder if (_assemblyBuilder != null && languageFreeBuildProviders != null) { foreach (BuildProvider languageFreeBuildProvider in languageFreeBuildProviders) { _assemblyBuilder.AddBuildProvider(languageFreeBuildProvider); } } } internal CompilerResults PerformBuild() { ProcessBuildProviders(); // Build all the satellite assemblies if (_satelliteAssemblyBuilders != null) { foreach (AssemblyBuilder satelliteAssemblyBuilder in _satelliteAssemblyBuilders.Values) { satelliteAssemblyBuilder.Compile(); } } // Build the main assembly if (_assemblyBuilder != null) return _assemblyBuilder.Compile(); return null; } internal void GenerateSources(out Type codeDomProviderType, out CompilerParameters compilerParameters) { ProcessBuildProviders(); // If we didn't get an AssemblyBuilder (happens when there was nothing to build), // get a default one. if (_assemblyBuilder == null) { _assemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( CompConfig, _referencedAssemblies, _configPath, _generatedFilesDir, null /*outputAssemblyName*/); } codeDomProviderType = _assemblyBuilder.CodeDomProviderType; compilerParameters = _assemblyBuilder.GetCompilerParameters(); } } /* * This class handles the batch compilation of one directory. It may * produce several assemblies out of them, based on dependencies and language * differences. All the BuildProvider's are expected to share the same * configuration (i.e. they live in the same directory). */ internal class WebDirectoryBatchCompiler { private DateTime _utcStart; // The set of assemblies that we will link with private ICollection _referencedAssemblies; // The config section for the set of build providers that we handle private CompilationSection _compConfig; // [VirtualPathString,InternalBuildProvider] private IDictionary _buildProviders = new Hashtable( StringComparer.OrdinalIgnoreCase); private VirtualDirectory _vdir; private ArrayList[] _nonDependentBuckets; private bool _ignoreProvidersWithErrors; // The set of parser errors detected during parsing. private ParserErrorCollection _parserErrors; // The first parse exceptions thrown during parsing. private HttpParseException _firstException; internal WebDirectoryBatchCompiler(VirtualDirectory vdir) { _vdir = vdir; _utcStart = DateTime.UtcNow; _compConfig = RuntimeConfig.GetConfig(_vdir.VirtualPath).Compilation; _referencedAssemblies = BuildManager.GetReferencedAssemblies(_compConfig); } internal void SetIgnoreErrors() { _ignoreProvidersWithErrors = true; } internal void Process() { AddBuildProviders(true /*retryIfDeletionHappens*/); // If there are no BuildProvider's, we're done if (_buildProviders.Count == 0) return; BuildManager.ReportDirectoryCompilationProgress(_vdir.VirtualPathObject); GetBuildResultDependencies(); ProcessDependencies(); foreach (ICollection buildProviders in _nonDependentBuckets) { if (!CompileNonDependentBuildProviders(buildProviders)) break; } // Report all parse exceptions if (_parserErrors != null && _parserErrors.Count > 0) { Debug.Assert(!_ignoreProvidersWithErrors); // Throw the first exception as inner exception along with the parse errors. HttpParseException newException = new HttpParseException(_firstException.Message, _firstException, _firstException.VirtualPath, _firstException.Source, _firstException.Line); // Add the rest of the parser errors to the exception. // The first one is already added. for (int i = 1; i < _parserErrors.Count; i++) { newException.ParserErrors.Add(_parserErrors[i]); } // rethrow the new exception throw newException; } } private void AddBuildProviders(bool retryIfDeletionHappens) { DiskBuildResultCache.ResetAssemblyDeleted(); foreach (VirtualFile vfile in _vdir.Files) { // If it's already built and up to date, skip it BuildResult result = null; try { result = BuildManager.GetVPathBuildResultFromCache(vfile.VirtualPathObject); } catch { // Ignore the cached error in batch compilation mode, since we want to compile // as many files as possible. // But don't ignore it in CBM or precompile cases, since we always want to try // to compile everything that had failed before. if (!BuildManager.PerformingPrecompilation) { // Skip it if an exception occurs (e.g. if a compile error was cached for it) continue; } } if (result != null) continue; BuildProvider buildProvider = BuildManager.CreateBuildProvider(vfile.VirtualPathObject, _compConfig, _referencedAssemblies, false /*failIfUnknown*/); // Non-supported file type if (buildProvider == null) continue; // IgnoreFileBuildProvider's should never be created Debug.Assert(!(buildProvider is IgnoreFileBuildProvider)); _buildProviders[vfile.VirtualPath] = buildProvider; } // If an assembly had to be deleted/renamed as a result of calling GetVPathBuildResultFromCache, // me way need to run the AddBuildProviders logic again. The reason is that as a result of // deleting the assembly, we may have invalidated other BuildResult that we had earlier found // to be up to date (VSWhidbey 269297) if (DiskBuildResultCache.InUseAssemblyWasDeleted) { Debug.Assert(retryIfDeletionHappens); // Only retry if we're doing precompilation. For standard batching, we can live // with the fact that not everything will be built after we're done (and we want to // be done as quickly as possible since the user is waiting). if (retryIfDeletionHappens && BuildManager.PerformingPrecompilation) { Debug.Trace("WebDirectoryBatchCompiler", "Rerunning AddBuildProviders for '" + _vdir.VirtualPath + "' because an assembly was out of date."); // Pass false for retryIfDeletionHappens to make sure we don't get in an // infinite recursion. AddBuildProviders(false /*retryIfDeletionHappens*/); } } } private void CacheAssemblyResults(AssemblyBuilder assemblyBuilder, CompilerResults results) { foreach (BuildProvider buildProvider in assemblyBuilder.BuildProviders) { BuildResult result = buildProvider.GetBuildResult(results); // If the provider didn't produce anything, ignore it if (result == null) continue; // If CacheVPathBuildResult returns false, something was found to be invalidated // and we need to abort the caching (VSWhidbey 578372) if (!BuildManager.CacheVPathBuildResult(buildProvider.VirtualPathObject, result, _utcStart)) break; #if DBG if (results != null) { Debug.Trace("BuildManager", buildProvider.VirtualPath + results.CompiledAssembly.EscapedCodeBase); } else { Debug.Trace("BuildManager", buildProvider.VirtualPath + ": no assembly"); } #endif } } // Cache the various compile errors found during batching private void CacheCompileErrors(AssemblyBuilder assemblyBuilder, CompilerResults results) { BuildProvider previous = null; // Go through all the compile errors foreach (CompilerError error in results.Errors) { // Skip warnings if (error.IsWarning) continue; // Try to map the error back to a BuildProvider. If we can't, skip the error. BuildProvider buildProvider = assemblyBuilder.GetBuildProviderFromLinePragma(error.FileName); if (buildProvider == null) continue; // Only cache the error for template controls. Otherwise, for file types like // asmx/ashx, it's too likely that two of them define the same class. if (!(buildProvider is BaseTemplateBuildProvider)) continue; // If the error is for the same page as the previous one, ignore it if (buildProvider == previous) continue; previous = buildProvider; // Create a new CompilerResults for this error CompilerResults newResults = new CompilerResults(null /*tempFiles*/); // Copy all the output to the new result. Note that this will include all the // error lines, not just the ones for this BuildProvider. But that's not a big deal, // and we can't easily filter the output here. foreach (string s in results.Output) newResults.Output.Add(s); // Copy various other fields to the new CompilerResults object newResults.PathToAssembly = results.PathToAssembly; newResults.NativeCompilerReturnValue = results.NativeCompilerReturnValue; // Add this error. It will be the only one in the CompilerResults object. newResults.Errors.Add(error); // Create a new HttpCompileException & BuildResultCompileError to wrap this error HttpCompileException e = new HttpCompileException(newResults, assemblyBuilder.GetGeneratedSourceFromBuildProvider(buildProvider)); BuildResult result = new BuildResultCompileError(buildProvider.VirtualPathObject, e); // Add the dependencies to the compile error build provider, so that // we will retry compilation when a dependency changes buildProvider.SetBuildResultDependencies(result); // Cache it BuildManager.CacheVPathBuildResult(buildProvider.VirtualPathObject, result, _utcStart); } } private void GetBuildResultDependencies() { foreach (BuildProvider buildProvider in _buildProviders.Values) { ICollection virtualPathDependencies = buildProvider.GetBuildResultVirtualPathDependencies(); if (virtualPathDependencies == null) continue; foreach (string virtualPathDependency in virtualPathDependencies) { BuildProvider dependentBuildProvider = (BuildProvider) _buildProviders[virtualPathDependency]; if (dependentBuildProvider != null) buildProvider.AddBuildProviderDependency(dependentBuildProvider); } } } // Split the providers into non dependent buckets private void ProcessDependencies() { // First phase: compute levels in the dependency tree int totaldepth = 0; Hashtable depth = new Hashtable(); Stack stack = new Stack(); // compute depths foreach (BuildProvider buildProvider in _buildProviders.Values) { stack.Push(buildProvider); while (stack.Count > 0) { BuildProvider curnode = (BuildProvider)stack.Peek(); bool recurse = false; int maxdepth = 0; if (curnode.BuildProviderDependencies != null) { foreach (BuildProvider child in curnode.BuildProviderDependencies) { if (depth.ContainsKey(child)) { if (maxdepth <= (int)depth[child]) maxdepth = (int)depth[child] + 1; else if ((int)depth[child] == -1) throw new HttpException(SR.GetString(SR.File_Circular_Reference, child.VirtualPath)); } else { recurse = true; stack.Push(child); } } } if (recurse) depth[curnode] = -1; // being computed; else { stack.Pop(); depth[curnode] = maxdepth; if (totaldepth <= maxdepth) totaldepth = maxdepth + 1; } } } // drop into buckets by depth _nonDependentBuckets = new ArrayList[totaldepth]; for (IDictionaryEnumerator en = (IDictionaryEnumerator)depth.GetEnumerator(); en.MoveNext();) { int level = (int)en.Value; if (_nonDependentBuckets[level] == null) _nonDependentBuckets[level] = new ArrayList(); _nonDependentBuckets[level].Add(en.Key); } #if DBG int i = 0; foreach (ICollection buildProviders in _nonDependentBuckets) { Debug.Trace("BuildManager", String.Empty); Debug.Trace("BuildManager", "Bucket " + i + " contains " + buildProviders.Count + " files"); foreach (BuildProvider buildProvider in buildProviders) Debug.Trace("BuildManager", buildProvider.VirtualPath); i++; } #endif } private bool IsBuildProviderSkipable(BuildProvider buildProvider) { // If another build provider depends on it, we should not skip it if (buildProvider.IsDependedOn) return false; // No one depends on it (at least in this directory) // If it's a source file, skip it. We need to do this for v1 compatibility, // since v1 VS projects contain many source files which have already been // precompiled into bin, and that should not be compiled dynamically if (buildProvider is SourceFileBuildProvider) return true; // For the same reason, skip resources if (buildProvider is ResXBuildProvider) return true; return false; } private bool CompileNonDependentBuildProviders(ICollection buildProviders) { // Key: CompilerType, Value: AssemblyBuilder IDictionary assemblyBuilders = new Hashtable(); // List of InternalBuildProvider's that don't ask for a specific language ArrayList languageFreeBuildProviders = null; // AssemblyBuilder used for providers that don't need a specific language AssemblyBuilder defaultAssemblyBuilder = null; bool hasParserErrors = false; foreach (BuildProvider buildProvider in buildProviders) { if (IsBuildProviderSkipable(buildProvider)) continue; // Instruct the internal build providers to continue processing for more parse errors. if (!BuildManager.ThrowOnFirstParseError) { InternalBuildProvider provider = buildProvider as InternalBuildProvider; if (provider != null) { provider.ThrowOnFirstParseError = false; } } CompilerType compilerType = null; // Get the language try { compilerType = BuildProvider.GetCompilerTypeFromBuildProvider( buildProvider); } catch (HttpParseException ex) { // Ignore the error if we are in that mode. if (_ignoreProvidersWithErrors) { continue; } hasParserErrors = true; // Remember the first parse exception if (_firstException == null) { _firstException = ex; } if (_parserErrors == null) { _parserErrors = new ParserErrorCollection(); } _parserErrors.AddRange(ex.ParserErrors); continue; } catch { // Ignore the error if we are in that mode. if (_ignoreProvidersWithErrors) { continue; } throw; } AssemblyBuilder assemblyBuilder = defaultAssemblyBuilder; // Is it asking for a specific language? if (compilerType == null) { // If this provider doesn't need a specific language, and we haven't created // a default builder yet, just keep track of it if (defaultAssemblyBuilder == null) { if (languageFreeBuildProviders == null) { languageFreeBuildProviders = new ArrayList(); } languageFreeBuildProviders.Add(buildProvider); continue; } } else { // Check if we already have an assembly builder of the right type assemblyBuilder = (AssemblyBuilder)assemblyBuilders[compilerType]; } ICollection typeNames = buildProvider.GetGeneratedTypeNames(); // Starts a new assemblyBuilder if the old one already contains another buildprovider // that uses the same type name if (assemblyBuilder == null || assemblyBuilder.IsBatchFull || assemblyBuilder.ContainsTypeNames(typeNames)) { // If the assemblyBuilder is full, compile it. if (assemblyBuilder != null) { CompileAssemblyBuilder(assemblyBuilder); } AssemblyBuilder newBuilder = compilerType.CreateAssemblyBuilder( _compConfig, _referencedAssemblies); assemblyBuilders[compilerType] = newBuilder; // Remember it as the default if we don't already have one, // or if the default is already full, switch the default to the new one. if (defaultAssemblyBuilder == null || defaultAssemblyBuilder == assemblyBuilder) { defaultAssemblyBuilder = newBuilder; } assemblyBuilder = newBuilder; } assemblyBuilder.AddTypeNames(typeNames); assemblyBuilder.AddBuildProvider(buildProvider); } // Don't try to compile providers, otherwise compile exceptions will be bubbled up, // and we lose the parse errors. if (hasParserErrors) { return false; } // Handle all the left over language free providers if (languageFreeBuildProviders != null) { // Indicates whether the default assembly builder is not a language specific builder. bool newDefaultAssemblyBuilder = (defaultAssemblyBuilder == null); // Add language independent providers to the default assembly builder. foreach (BuildProvider languageFreeBuildProvider in languageFreeBuildProviders) { ICollection typeNames = languageFreeBuildProvider.GetGeneratedTypeNames(); // If we don't have a default language assembly builder, get one or // starts a new assemblyBuilder if the old one already contains another buildprovider // that uses the same type name if (defaultAssemblyBuilder == null || defaultAssemblyBuilder.IsBatchFull || defaultAssemblyBuilder.ContainsTypeNames(typeNames)) { // If the default assemblyBuilder is full, compile it. if (defaultAssemblyBuilder != null) { CompileAssemblyBuilder(defaultAssemblyBuilder); } defaultAssemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( _compConfig, _referencedAssemblies, _vdir.VirtualPathObject /*configPath*/, null /*outputAssemblyName*/); // the default assembly builder needs to be compiled separately. newDefaultAssemblyBuilder = true; } defaultAssemblyBuilder.AddTypeNames(typeNames); defaultAssemblyBuilder.AddBuildProvider(languageFreeBuildProvider); } // Only compile the default assembly builder if it's not part of language specific // assembly builder (which will be compiled separately) if (newDefaultAssemblyBuilder) { // Compile the default assembly builder. CompileAssemblyBuilder(defaultAssemblyBuilder); } } foreach (AssemblyBuilder assemblyBuilder in assemblyBuilders.Values) { CompileAssemblyBuilder(assemblyBuilder); } return true; } private void CompileAssemblyBuilder(AssemblyBuilder builder) { CompilerResults results; try { results = builder.Compile(); } catch (HttpCompileException e) { CacheCompileErrors(builder, e.Results); throw; } CacheAssemblyResults(builder, results); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.Compilation { using System; using System.IO; using System.Collections; using System.Collections.Specialized; using System.Reflection; using System.Globalization; using System.CodeDom; using System.CodeDom.Compiler; using System.Configuration; using System.Web.Hosting; using System.Web.Util; using System.Web.Caching; using System.Web.UI; using System.Web.Configuration; internal class BuildProvidersCompiler { private ICollection _buildProviders; private VirtualPath _configPath; private bool _supportLocalization; // The set of assemblies that we need to reference when compiling private ICollection _referencedAssemblies; internal ICollection ReferencedAssemblies { get { return _referencedAssemblies; } } private AssemblyBuilder _assemblyBuilder; // Key: CultureName string, Value: AssemblyBuilder private IDictionary _satelliteAssemblyBuilders = null; // If this is set, we only generate the source files into this directory // without compiling them. // This is used to implement ClientBuildManager.GetCodeDirectoryInformation private string _generatedFilesDir; internal BuildProvidersCompiler(VirtualPath configPath, string outputAssemblyName) : this(configPath, false, outputAssemblyName) { } internal BuildProvidersCompiler(VirtualPath configPath, bool supportLocalization, string outputAssemblyName) { _configPath = configPath; _supportLocalization = supportLocalization; _compConfig = RuntimeConfig.GetConfig(_configPath).Compilation; _referencedAssemblies = BuildManager.GetReferencedAssemblies(CompConfig); _outputAssemblyName = outputAssemblyName; } internal BuildProvidersCompiler(VirtualPath configPath, bool supportLocalization, string generatedFilesDir, int index) { _configPath = configPath; _supportLocalization = supportLocalization; _compConfig = RuntimeConfig.GetConfig(_configPath).Compilation; _referencedAssemblies = BuildManager.GetReferencedAssemblies(CompConfig, index); _generatedFilesDir = generatedFilesDir; } // Theconfig section for the set of build providers that we handle private CompilationSection _compConfig; internal CompilationSection CompConfig { get { return _compConfig; } } private string _outputAssemblyName; internal string OutputAssemblyName { get { return _outputAssemblyName; } } private bool CbmGenerateOnlyMode { get { return _generatedFilesDir != null; } } internal void SetBuildProviders(ICollection buildProviders) { _buildProviders = buildProviders; } private void ProcessBuildProviders() { CompilerType compilerType = null; BuildProvider firstLanguageBuildProvider = null; // First, delete all the existing satellite assemblies of the assembly // we're about to build (VSWhidbey 87022) (only if it has a fixed name) if (OutputAssemblyName != null) { Debug.Assert(!CbmGenerateOnlyMode); StandardDiskBuildResultCache.RemoveSatelliteAssemblies(OutputAssemblyName); } // List of BuildProvider's that don't ask for a specific language ArrayList languageFreeBuildProviders = null; foreach (BuildProvider buildProvider in _buildProviders) { // If it's an InternalBuildProvider, give it the assembly references early on buildProvider.SetReferencedAssemblies(_referencedAssemblies); // Instruct the internal build providers to continue processing for more parse errors. if (!BuildManager.ThrowOnFirstParseError) { InternalBuildProvider provider = buildProvider as InternalBuildProvider; if (provider != null) { provider.ThrowOnFirstParseError = false; } } // Get the language and culture CompilerType ctwp = BuildProvider.GetCompilerTypeFromBuildProvider(buildProvider); // Only look for a culture if we're supposed to (basically, in the resources directories) string cultureName = null; if (_supportLocalization) cultureName = buildProvider.GetCultureName(); // Is it asking for a specific language? if (ctwp != null) { // If it specifies a language, it can't also have a culture if (cultureName != null) { throw new HttpException(SR.GetString( SR.Both_culture_and_language, BuildProvider.GetDisplayName(buildProvider))); } // Do we already know the language we'll be using if (compilerType != null) { // If it's different from the current one, fail if (!ctwp.Equals(compilerType)) { throw new HttpException(SR.GetString( SR.Inconsistent_language, BuildProvider.GetDisplayName(buildProvider), BuildProvider.GetDisplayName(firstLanguageBuildProvider))); } } else { // Keep track of the build provider of error handling purpose firstLanguageBuildProvider = buildProvider; // Keep track of the language compilerType = ctwp; _assemblyBuilder = compilerType.CreateAssemblyBuilder( CompConfig, _referencedAssemblies, _generatedFilesDir, OutputAssemblyName); } } else { if (cultureName != null) { // Ignore the culture files in generate-only mode if (CbmGenerateOnlyMode) continue; if (_satelliteAssemblyBuilders == null) { _satelliteAssemblyBuilders = new Hashtable( StringComparer.OrdinalIgnoreCase); } // Check if we already have an assembly builder for this culture AssemblyBuilder satelliteAssemblyBuilder = (AssemblyBuilder) _satelliteAssemblyBuilders[cultureName]; // If not, create one and store it in the hashtable if (satelliteAssemblyBuilder == null) { satelliteAssemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( CompConfig, _referencedAssemblies, _configPath, OutputAssemblyName); satelliteAssemblyBuilder.CultureName = cultureName; _satelliteAssemblyBuilders[cultureName] = satelliteAssemblyBuilder; } satelliteAssemblyBuilder.AddBuildProvider(buildProvider); continue; } if (_assemblyBuilder == null) { // If this provider doesn't need a specific language, and we don't know // the language yet, just keep track of it if (languageFreeBuildProviders == null) languageFreeBuildProviders = new ArrayList(); languageFreeBuildProviders.Add(buildProvider); continue; } } _assemblyBuilder.AddBuildProvider(buildProvider); } // If we didn't get an AssemblyBuilder, use a default if (_assemblyBuilder == null && languageFreeBuildProviders != null) { _assemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( CompConfig, _referencedAssemblies, _configPath, _generatedFilesDir, OutputAssemblyName); } // Add all the language free providers (if any) to the AssemblyBuilder if (_assemblyBuilder != null && languageFreeBuildProviders != null) { foreach (BuildProvider languageFreeBuildProvider in languageFreeBuildProviders) { _assemblyBuilder.AddBuildProvider(languageFreeBuildProvider); } } } internal CompilerResults PerformBuild() { ProcessBuildProviders(); // Build all the satellite assemblies if (_satelliteAssemblyBuilders != null) { foreach (AssemblyBuilder satelliteAssemblyBuilder in _satelliteAssemblyBuilders.Values) { satelliteAssemblyBuilder.Compile(); } } // Build the main assembly if (_assemblyBuilder != null) return _assemblyBuilder.Compile(); return null; } internal void GenerateSources(out Type codeDomProviderType, out CompilerParameters compilerParameters) { ProcessBuildProviders(); // If we didn't get an AssemblyBuilder (happens when there was nothing to build), // get a default one. if (_assemblyBuilder == null) { _assemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( CompConfig, _referencedAssemblies, _configPath, _generatedFilesDir, null /*outputAssemblyName*/); } codeDomProviderType = _assemblyBuilder.CodeDomProviderType; compilerParameters = _assemblyBuilder.GetCompilerParameters(); } } /* * This class handles the batch compilation of one directory. It may * produce several assemblies out of them, based on dependencies and language * differences. All the BuildProvider's are expected to share the same * configuration (i.e. they live in the same directory). */ internal class WebDirectoryBatchCompiler { private DateTime _utcStart; // The set of assemblies that we will link with private ICollection _referencedAssemblies; // The config section for the set of build providers that we handle private CompilationSection _compConfig; // [VirtualPathString,InternalBuildProvider] private IDictionary _buildProviders = new Hashtable( StringComparer.OrdinalIgnoreCase); private VirtualDirectory _vdir; private ArrayList[] _nonDependentBuckets; private bool _ignoreProvidersWithErrors; // The set of parser errors detected during parsing. private ParserErrorCollection _parserErrors; // The first parse exceptions thrown during parsing. private HttpParseException _firstException; internal WebDirectoryBatchCompiler(VirtualDirectory vdir) { _vdir = vdir; _utcStart = DateTime.UtcNow; _compConfig = RuntimeConfig.GetConfig(_vdir.VirtualPath).Compilation; _referencedAssemblies = BuildManager.GetReferencedAssemblies(_compConfig); } internal void SetIgnoreErrors() { _ignoreProvidersWithErrors = true; } internal void Process() { AddBuildProviders(true /*retryIfDeletionHappens*/); // If there are no BuildProvider's, we're done if (_buildProviders.Count == 0) return; BuildManager.ReportDirectoryCompilationProgress(_vdir.VirtualPathObject); GetBuildResultDependencies(); ProcessDependencies(); foreach (ICollection buildProviders in _nonDependentBuckets) { if (!CompileNonDependentBuildProviders(buildProviders)) break; } // Report all parse exceptions if (_parserErrors != null && _parserErrors.Count > 0) { Debug.Assert(!_ignoreProvidersWithErrors); // Throw the first exception as inner exception along with the parse errors. HttpParseException newException = new HttpParseException(_firstException.Message, _firstException, _firstException.VirtualPath, _firstException.Source, _firstException.Line); // Add the rest of the parser errors to the exception. // The first one is already added. for (int i = 1; i < _parserErrors.Count; i++) { newException.ParserErrors.Add(_parserErrors[i]); } // rethrow the new exception throw newException; } } private void AddBuildProviders(bool retryIfDeletionHappens) { DiskBuildResultCache.ResetAssemblyDeleted(); foreach (VirtualFile vfile in _vdir.Files) { // If it's already built and up to date, skip it BuildResult result = null; try { result = BuildManager.GetVPathBuildResultFromCache(vfile.VirtualPathObject); } catch { // Ignore the cached error in batch compilation mode, since we want to compile // as many files as possible. // But don't ignore it in CBM or precompile cases, since we always want to try // to compile everything that had failed before. if (!BuildManager.PerformingPrecompilation) { // Skip it if an exception occurs (e.g. if a compile error was cached for it) continue; } } if (result != null) continue; BuildProvider buildProvider = BuildManager.CreateBuildProvider(vfile.VirtualPathObject, _compConfig, _referencedAssemblies, false /*failIfUnknown*/); // Non-supported file type if (buildProvider == null) continue; // IgnoreFileBuildProvider's should never be created Debug.Assert(!(buildProvider is IgnoreFileBuildProvider)); _buildProviders[vfile.VirtualPath] = buildProvider; } // If an assembly had to be deleted/renamed as a result of calling GetVPathBuildResultFromCache, // me way need to run the AddBuildProviders logic again. The reason is that as a result of // deleting the assembly, we may have invalidated other BuildResult that we had earlier found // to be up to date (VSWhidbey 269297) if (DiskBuildResultCache.InUseAssemblyWasDeleted) { Debug.Assert(retryIfDeletionHappens); // Only retry if we're doing precompilation. For standard batching, we can live // with the fact that not everything will be built after we're done (and we want to // be done as quickly as possible since the user is waiting). if (retryIfDeletionHappens && BuildManager.PerformingPrecompilation) { Debug.Trace("WebDirectoryBatchCompiler", "Rerunning AddBuildProviders for '" + _vdir.VirtualPath + "' because an assembly was out of date."); // Pass false for retryIfDeletionHappens to make sure we don't get in an // infinite recursion. AddBuildProviders(false /*retryIfDeletionHappens*/); } } } private void CacheAssemblyResults(AssemblyBuilder assemblyBuilder, CompilerResults results) { foreach (BuildProvider buildProvider in assemblyBuilder.BuildProviders) { BuildResult result = buildProvider.GetBuildResult(results); // If the provider didn't produce anything, ignore it if (result == null) continue; // If CacheVPathBuildResult returns false, something was found to be invalidated // and we need to abort the caching (VSWhidbey 578372) if (!BuildManager.CacheVPathBuildResult(buildProvider.VirtualPathObject, result, _utcStart)) break; #if DBG if (results != null) { Debug.Trace("BuildManager", buildProvider.VirtualPath + results.CompiledAssembly.EscapedCodeBase); } else { Debug.Trace("BuildManager", buildProvider.VirtualPath + ": no assembly"); } #endif } } // Cache the various compile errors found during batching private void CacheCompileErrors(AssemblyBuilder assemblyBuilder, CompilerResults results) { BuildProvider previous = null; // Go through all the compile errors foreach (CompilerError error in results.Errors) { // Skip warnings if (error.IsWarning) continue; // Try to map the error back to a BuildProvider. If we can't, skip the error. BuildProvider buildProvider = assemblyBuilder.GetBuildProviderFromLinePragma(error.FileName); if (buildProvider == null) continue; // Only cache the error for template controls. Otherwise, for file types like // asmx/ashx, it's too likely that two of them define the same class. if (!(buildProvider is BaseTemplateBuildProvider)) continue; // If the error is for the same page as the previous one, ignore it if (buildProvider == previous) continue; previous = buildProvider; // Create a new CompilerResults for this error CompilerResults newResults = new CompilerResults(null /*tempFiles*/); // Copy all the output to the new result. Note that this will include all the // error lines, not just the ones for this BuildProvider. But that's not a big deal, // and we can't easily filter the output here. foreach (string s in results.Output) newResults.Output.Add(s); // Copy various other fields to the new CompilerResults object newResults.PathToAssembly = results.PathToAssembly; newResults.NativeCompilerReturnValue = results.NativeCompilerReturnValue; // Add this error. It will be the only one in the CompilerResults object. newResults.Errors.Add(error); // Create a new HttpCompileException & BuildResultCompileError to wrap this error HttpCompileException e = new HttpCompileException(newResults, assemblyBuilder.GetGeneratedSourceFromBuildProvider(buildProvider)); BuildResult result = new BuildResultCompileError(buildProvider.VirtualPathObject, e); // Add the dependencies to the compile error build provider, so that // we will retry compilation when a dependency changes buildProvider.SetBuildResultDependencies(result); // Cache it BuildManager.CacheVPathBuildResult(buildProvider.VirtualPathObject, result, _utcStart); } } private void GetBuildResultDependencies() { foreach (BuildProvider buildProvider in _buildProviders.Values) { ICollection virtualPathDependencies = buildProvider.GetBuildResultVirtualPathDependencies(); if (virtualPathDependencies == null) continue; foreach (string virtualPathDependency in virtualPathDependencies) { BuildProvider dependentBuildProvider = (BuildProvider) _buildProviders[virtualPathDependency]; if (dependentBuildProvider != null) buildProvider.AddBuildProviderDependency(dependentBuildProvider); } } } // Split the providers into non dependent buckets private void ProcessDependencies() { // First phase: compute levels in the dependency tree int totaldepth = 0; Hashtable depth = new Hashtable(); Stack stack = new Stack(); // compute depths foreach (BuildProvider buildProvider in _buildProviders.Values) { stack.Push(buildProvider); while (stack.Count > 0) { BuildProvider curnode = (BuildProvider)stack.Peek(); bool recurse = false; int maxdepth = 0; if (curnode.BuildProviderDependencies != null) { foreach (BuildProvider child in curnode.BuildProviderDependencies) { if (depth.ContainsKey(child)) { if (maxdepth <= (int)depth[child]) maxdepth = (int)depth[child] + 1; else if ((int)depth[child] == -1) throw new HttpException(SR.GetString(SR.File_Circular_Reference, child.VirtualPath)); } else { recurse = true; stack.Push(child); } } } if (recurse) depth[curnode] = -1; // being computed; else { stack.Pop(); depth[curnode] = maxdepth; if (totaldepth <= maxdepth) totaldepth = maxdepth + 1; } } } // drop into buckets by depth _nonDependentBuckets = new ArrayList[totaldepth]; for (IDictionaryEnumerator en = (IDictionaryEnumerator)depth.GetEnumerator(); en.MoveNext();) { int level = (int)en.Value; if (_nonDependentBuckets[level] == null) _nonDependentBuckets[level] = new ArrayList(); _nonDependentBuckets[level].Add(en.Key); } #if DBG int i = 0; foreach (ICollection buildProviders in _nonDependentBuckets) { Debug.Trace("BuildManager", String.Empty); Debug.Trace("BuildManager", "Bucket " + i + " contains " + buildProviders.Count + " files"); foreach (BuildProvider buildProvider in buildProviders) Debug.Trace("BuildManager", buildProvider.VirtualPath); i++; } #endif } private bool IsBuildProviderSkipable(BuildProvider buildProvider) { // If another build provider depends on it, we should not skip it if (buildProvider.IsDependedOn) return false; // No one depends on it (at least in this directory) // If it's a source file, skip it. We need to do this for v1 compatibility, // since v1 VS projects contain many source files which have already been // precompiled into bin, and that should not be compiled dynamically if (buildProvider is SourceFileBuildProvider) return true; // For the same reason, skip resources if (buildProvider is ResXBuildProvider) return true; return false; } private bool CompileNonDependentBuildProviders(ICollection buildProviders) { // Key: CompilerType, Value: AssemblyBuilder IDictionary assemblyBuilders = new Hashtable(); // List of InternalBuildProvider's that don't ask for a specific language ArrayList languageFreeBuildProviders = null; // AssemblyBuilder used for providers that don't need a specific language AssemblyBuilder defaultAssemblyBuilder = null; bool hasParserErrors = false; foreach (BuildProvider buildProvider in buildProviders) { if (IsBuildProviderSkipable(buildProvider)) continue; // Instruct the internal build providers to continue processing for more parse errors. if (!BuildManager.ThrowOnFirstParseError) { InternalBuildProvider provider = buildProvider as InternalBuildProvider; if (provider != null) { provider.ThrowOnFirstParseError = false; } } CompilerType compilerType = null; // Get the language try { compilerType = BuildProvider.GetCompilerTypeFromBuildProvider( buildProvider); } catch (HttpParseException ex) { // Ignore the error if we are in that mode. if (_ignoreProvidersWithErrors) { continue; } hasParserErrors = true; // Remember the first parse exception if (_firstException == null) { _firstException = ex; } if (_parserErrors == null) { _parserErrors = new ParserErrorCollection(); } _parserErrors.AddRange(ex.ParserErrors); continue; } catch { // Ignore the error if we are in that mode. if (_ignoreProvidersWithErrors) { continue; } throw; } AssemblyBuilder assemblyBuilder = defaultAssemblyBuilder; // Is it asking for a specific language? if (compilerType == null) { // If this provider doesn't need a specific language, and we haven't created // a default builder yet, just keep track of it if (defaultAssemblyBuilder == null) { if (languageFreeBuildProviders == null) { languageFreeBuildProviders = new ArrayList(); } languageFreeBuildProviders.Add(buildProvider); continue; } } else { // Check if we already have an assembly builder of the right type assemblyBuilder = (AssemblyBuilder)assemblyBuilders[compilerType]; } ICollection typeNames = buildProvider.GetGeneratedTypeNames(); // Starts a new assemblyBuilder if the old one already contains another buildprovider // that uses the same type name if (assemblyBuilder == null || assemblyBuilder.IsBatchFull || assemblyBuilder.ContainsTypeNames(typeNames)) { // If the assemblyBuilder is full, compile it. if (assemblyBuilder != null) { CompileAssemblyBuilder(assemblyBuilder); } AssemblyBuilder newBuilder = compilerType.CreateAssemblyBuilder( _compConfig, _referencedAssemblies); assemblyBuilders[compilerType] = newBuilder; // Remember it as the default if we don't already have one, // or if the default is already full, switch the default to the new one. if (defaultAssemblyBuilder == null || defaultAssemblyBuilder == assemblyBuilder) { defaultAssemblyBuilder = newBuilder; } assemblyBuilder = newBuilder; } assemblyBuilder.AddTypeNames(typeNames); assemblyBuilder.AddBuildProvider(buildProvider); } // Don't try to compile providers, otherwise compile exceptions will be bubbled up, // and we lose the parse errors. if (hasParserErrors) { return false; } // Handle all the left over language free providers if (languageFreeBuildProviders != null) { // Indicates whether the default assembly builder is not a language specific builder. bool newDefaultAssemblyBuilder = (defaultAssemblyBuilder == null); // Add language independent providers to the default assembly builder. foreach (BuildProvider languageFreeBuildProvider in languageFreeBuildProviders) { ICollection typeNames = languageFreeBuildProvider.GetGeneratedTypeNames(); // If we don't have a default language assembly builder, get one or // starts a new assemblyBuilder if the old one already contains another buildprovider // that uses the same type name if (defaultAssemblyBuilder == null || defaultAssemblyBuilder.IsBatchFull || defaultAssemblyBuilder.ContainsTypeNames(typeNames)) { // If the default assemblyBuilder is full, compile it. if (defaultAssemblyBuilder != null) { CompileAssemblyBuilder(defaultAssemblyBuilder); } defaultAssemblyBuilder = CompilerType.GetDefaultAssemblyBuilder( _compConfig, _referencedAssemblies, _vdir.VirtualPathObject /*configPath*/, null /*outputAssemblyName*/); // the default assembly builder needs to be compiled separately. newDefaultAssemblyBuilder = true; } defaultAssemblyBuilder.AddTypeNames(typeNames); defaultAssemblyBuilder.AddBuildProvider(languageFreeBuildProvider); } // Only compile the default assembly builder if it's not part of language specific // assembly builder (which will be compiled separately) if (newDefaultAssemblyBuilder) { // Compile the default assembly builder. CompileAssemblyBuilder(defaultAssemblyBuilder); } } foreach (AssemblyBuilder assemblyBuilder in assemblyBuilders.Values) { CompileAssemblyBuilder(assemblyBuilder); } return true; } private void CompileAssemblyBuilder(AssemblyBuilder builder) { CompilerResults results; try { results = builder.Compile(); } catch (HttpCompileException e) { CacheCompileErrors(builder, e.Results); throw; } CacheAssemblyResults(builder, results); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- InputBinder.cs
- TabItemAutomationPeer.cs
- ISCIIEncoding.cs
- IFlowDocumentViewer.cs
- AppDomainCompilerProxy.cs
- ListViewEditEventArgs.cs
- SpecialFolderEnumConverter.cs
- EnvironmentPermission.cs
- CorruptStoreException.cs
- TrackingRecord.cs
- XpsPackagingException.cs
- DoubleCollection.cs
- ClientFactory.cs
- MatrixTransform3D.cs
- XmlTypeAttribute.cs
- X509SecurityToken.cs
- ToolStripPanelDesigner.cs
- FilterEventArgs.cs
- XamlParser.cs
- BaseResourcesBuildProvider.cs
- MainMenu.cs
- ObjectFullSpanRewriter.cs
- SeparatorAutomationPeer.cs
- ItemAutomationPeer.cs
- X509Utils.cs
- OutKeywords.cs
- cookie.cs
- Util.cs
- RequestQueue.cs
- ActivitySurrogateSelector.cs
- DataQuery.cs
- MemberCollection.cs
- CodePageEncoding.cs
- AudioLevelUpdatedEventArgs.cs
- ActiveXSite.cs
- RemotingSurrogateSelector.cs
- CodeMethodInvokeExpression.cs
- HashSet.cs
- UIElementIsland.cs
- ViewGenerator.cs
- ConstrainedDataObject.cs
- StringResourceManager.cs
- SerialStream.cs
- ClaimSet.cs
- EventMap.cs
- XPathEmptyIterator.cs
- AdornerHitTestResult.cs
- SqlPersistenceProviderFactory.cs
- HyperLinkColumn.cs
- _NtlmClient.cs
- XsdBuilder.cs
- ParameterCollection.cs
- UpdatePanelTrigger.cs
- AutomationProperties.cs
- CodeMemberMethod.cs
- InvalidPipelineStoreException.cs
- LinkConverter.cs
- CompilerGeneratedAttribute.cs
- SignatureHelper.cs
- MonikerUtility.cs
- EventLogger.cs
- SqlServices.cs
- TraceInternal.cs
- TraceInternal.cs
- WindowsSolidBrush.cs
- DataConnectionHelper.cs
- DependencyPropertyKey.cs
- DependencyObjectPropertyDescriptor.cs
- Material.cs
- HashCryptoHandle.cs
- Msec.cs
- PrimaryKeyTypeConverter.cs
- TemplateBindingExpressionConverter.cs
- ContextMenuStripGroupCollection.cs
- XmlSiteMapProvider.cs
- FilterException.cs
- TreeNode.cs
- BufferAllocator.cs
- ProfilePropertyMetadata.cs
- LoginUtil.cs
- Viewport3DAutomationPeer.cs
- SqlException.cs
- NestedContainer.cs
- shaperfactoryquerycacheentry.cs
- ProtocolViolationException.cs
- CodeTypeConstructor.cs
- TreeNodeCollection.cs
- DataSourceCacheDurationConverter.cs
- SynthesizerStateChangedEventArgs.cs
- DebugView.cs
- NamespaceCollection.cs
- TextTreeUndoUnit.cs
- WebPartCatalogAddVerb.cs
- StrokeSerializer.cs
- GeometryGroup.cs
- SrgsElement.cs
- FrugalList.cs
- QueryPrefixOp.cs
- X509ChainElement.cs
- CorePropertiesFilter.cs