AssemblyBuilder.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / Compilation / AssemblyBuilder.cs / 6 / AssemblyBuilder.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

 
 
namespace System.Web.Compilation {
 
using System;
using System.Security.Permissions;
using System.IO;
using System.Collections; 
using System.Collections.Specialized;
using System.Collections.Generic; 
using System.Reflection; 
using System.Text;
using System.Globalization; 
using System.Security;
using System.Security.Cryptography;
using System.Xml;
using System.Xml.Schema; 
using System.CodeDom;
using System.CodeDom.Compiler; 
using System.Web.Hosting; 
using System.Web.Util;
using System.Web.UI; 
using System.Web.Configuration;
using System.Web.Management;

/* 
 * This class is used to handle a single compilation using a CodeDom compiler.
 * It is instantiated via CompilerType.CreateAssemblyBuilder. 
 */ 
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)] 
public class AssemblyBuilder {

    private CompilationSection _compConfig;
 
    // Field relating to the checksum calculation
    private static HashAlgorithm s_md5HashAlgorithm; 
    private static Guid s_hashMD5Guid; 

    // List of BuildProviders involved in this compilation 
    // The key is either a virtual path, or the BuildProvider itself if
    // it doesn't give us a virtual path.
    // The value is the BuildProvider.
    private Hashtable _buildProviders = new Hashtable(StringComparer.OrdinalIgnoreCase); 

    internal ICollection BuildProviders { 
        get { return _buildProviders.Values; } 
    }
 
    // List of physical source files to be compiled
    private StringSet _sourceFiles = new StringSet();

    // CodeCompileUnit to hold various top level things we need to generate 
    private CodeCompileUnit _miscCodeCompileUnit;
 
    // List of physical embedded resource files to be compiled 
    private StringSet _embeddedResourceFiles;
 
    // The set of assemblies that we will be linked with
    private AssemblySet _initialReferencedAssemblies;

    // The additional set of assemblies that we will be linked with, and that are 
    // requested by various BuildProviders.  We need to keep them separate to avoid
    // having BuildProviders see assemblies that were requested by earlier providers, 
    // which would lead to unpredictable behavior (since the order is arbitrary) 
    private AssemblySet _additionalReferencedAssemblies;
 
    internal CodeDomProvider _codeProvider;

    private Hashtable _buildProviderToSourceFileMap;
 
    // The type of CodeDom compiler (i.e. language, flags)
    private CompilerType _compilerType; 
 
    internal Type CodeDomProviderType {
        get { return _compilerType.CodeDomProviderType; } 
    }

    // Used to generate fast Type factories
    private ObjectFactoryCodeDomTreeGenerator _objectFactoryGenerator; 

    private StringResourceBuilder _stringResourceBuilder; 
    internal StringResourceBuilder StringResourceBuilder { 
        get {
            if (_stringResourceBuilder == null) 
                _stringResourceBuilder = new StringResourceBuilder();

            return _stringResourceBuilder;
        } 
    }
 
    // Used to create temporary source files 
    private TempFileCollection _tempFiles = new TempFileCollection(HttpRuntime.CodegenDirInternal);
    private int _fileCount; 

    private string _cultureName;
    internal string CultureName {
        get { return _cultureName; } 
        set { _cultureName = value; }
    } 
 
    private string _outputAssemblyName;
    private string OutputAssemblyName { 
        get {
            if (_outputAssemblyName == null) {
                // If we don't have the assembly name, we should never have a culture
                Debug.Assert(CultureName == null); 

                // If the assembly name was not specified, use a generated one based on the TempFileCollection. 
                // But prefix it with a fixed token, to make it easier to recognize the assembly (DevDiv 36625) 
                string basePath = _tempFiles.BasePath;
                string baseFileName = Path.GetFileName(basePath); 
                _outputAssemblyName = BuildManager.WebAssemblyNamePrefix + baseFileName;
            }

            return _outputAssemblyName; 
        }
    } 
 
    private int _maxBatchSize;
    private long _maxBatchGeneratedFileSize; 
    private long _totalFileLength;

    private CaseInsensitiveStringSet _registeredTypeNames;
    internal bool ContainsTypeNames(ICollection typeNames) { 
        if (_registeredTypeNames != null && typeNames != null) {
            foreach (String typeName in typeNames) { 
                if (_registeredTypeNames.Contains(typeName)) { 
                    return true;
                } 
            }
        }

        return false; 
    }
 
    internal void AddTypeNames(ICollection typeNames) { 
        if (typeNames == null) {
            return; 
        }

        if (_registeredTypeNames == null) {
            _registeredTypeNames = new CaseInsensitiveStringSet(); 
        }
 
        _registeredTypeNames.AddCollection(typeNames); 
    }
 
    internal AssemblyBuilder(CompilationSection compConfig,
        ICollection referencedAssemblies, CompilerType compilerType, string outputAssemblyName) {

        _compConfig = compConfig; 

        _outputAssemblyName = outputAssemblyName; 
 
        // Clone the referenced assemblies
        _initialReferencedAssemblies = AssemblySet.Create(referencedAssemblies); 

        // We need to clone it to avoid modifying the original (VSWhidbey 338935)
        _compilerType = compilerType.Clone();
 
        if (BuildManager.PrecompilingWithDebugInfo) {
            // If the precompile flag indicates force debug, always compile as debug 
            _compilerType.CompilerParameters.IncludeDebugInformation = true; 
        }
        else if (BuildManager.PrecompilingForDeployment) { 
            // If we're precompiling the app, never compile in debug mode (VSWhidbey 178377)
            _compilerType.CompilerParameters.IncludeDebugInformation = false;
        }
        else if (DeploymentSection.RetailInternal) { 
            // If we're in retail deployment mode, always turn off debug (DevDiv 36396)
            _compilerType.CompilerParameters.IncludeDebugInformation = false; 
        } 
        else if (_compConfig.AssemblyPostProcessorTypeInternal != null) {
            // If an IAssemblyPostProcessor is registered always compile as debug 
            _compilerType.CompilerParameters.IncludeDebugInformation = true;
        }

        // 
        _tempFiles.KeepFiles = _compilerType.CompilerParameters.IncludeDebugInformation;
 
        _codeProvider = CompilationUtil.CreateCodeDomProviderNonPublic( 
            _compilerType.CodeDomProviderType);
 
        _maxBatchSize = _compConfig.MaxBatchSize;
        _maxBatchGeneratedFileSize = _compConfig.MaxBatchGeneratedFileSize * 1024;
    }
 
    // Beginning of public contract
 
 
    /// 
    ///     Adds an assembly that will be referenced during compilation. 
    /// 
    public void AddAssemblyReference(Assembly a) {
        if (_additionalReferencedAssemblies == null)
            _additionalReferencedAssemblies = new AssemblySet(); 
        _additionalReferencedAssemblies.Add(a);
    } 
 
    /// 
    /// Adds an assembly that will be referenced during compilation. Also adds the 
    /// assembly the the ReferencedAssemblies list in the CodeCompileUnit.
    /// 
    internal void AddAssemblyReference(Assembly a, CodeCompileUnit ccu) {
        AddAssemblyReference(a); 
        Util.AddAssemblyToStringCollection(a, ccu.ReferencedAssemblies);
    } 
 
    /// 
    ///     Creates a new source file that will be added to the compilation. See the public overload 
    ///     method for detail.
    /// 
    internal virtual TextWriter CreateCodeFile(BuildProvider buildProvider, out string filename) {
 
        string generatedFilePath = GetTempFilePhysicalPath(_codeProvider.FileExtension);
        filename = generatedFilePath; 
 
        if (buildProvider != null) {
            if (_buildProviderToSourceFileMap == null) 
                _buildProviderToSourceFileMap = new Hashtable();
            _buildProviderToSourceFileMap[buildProvider] = generatedFilePath;
            buildProvider.SetContributedCode();
        } 

        _sourceFiles.Add(generatedFilePath); 
 
        Stream temp = new FileStream(generatedFilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
        return new StreamWriter(temp, Encoding.UTF8); 
    }


    ///  
    ///     Creates a new source file that will be added to the compilation.  The build provider
    ///     can write source code to this file using the returned TextWriter. 
    ///     The build provider should close the TextWriter when it is done writing to it. 
    ///     The build provider should pass itself as a parameter to this method.
    ///  
    public TextWriter CreateCodeFile(BuildProvider buildProvider) {
        // Ignore the unused filename param.
        string filename;
 
        return CreateCodeFile(buildProvider, out filename);
    } 
 
// Punting this method for the sake of simplifying the API, as the same thing can be
// achieved with the other methods (with a bit more work). 
#if UNUSED

    /// 
    ///     Returns the physical path to a source file that will be included in the 
    ///     compilation.  Note that the file is not actually created.  It is up to the
    ///     build provider to do this. 
    ///     The source file has the correct extension for the target language. 
    ///     The build provider should pass itself as a parameter to this method.
    ///  
    public string GetCodeFilePhysicalPath(BuildProvider buildProvider) {

        string generatedFilePath = GetTempFilePhysicalPath(_codeProvider.FileExtension);
        _sourceFiles.Add(generatedFilePath); 
        return generatedFilePath;
    } 
#endif 

    // Indicates whether the assemblyBuilder has reached its capacity limit. 
    internal bool IsBatchFull {
        get {
            return (_sourceFiles.Count >= _maxBatchSize) ||
                (_totalFileLength >= _maxBatchGeneratedFileSize); 
        }
    } 
 

    ///  
    ///     Adds a CodeCompileUnit to the compilation.  This is typically used as an
    ///     alternative to CreateSourceFile, by providers who are CodeDOM aware.
    ///     The build provider should pass itself as a parameter to this method.
    ///  
    public void AddCodeCompileUnit(BuildProvider buildProvider, CodeCompileUnit compileUnit) {
 
        // Add a checksum pragma to the compile unit if appropriate 
        AddChecksumPragma(buildProvider, compileUnit);
 
        // Add all the referenced assemblies to the CodeCompileUnit in case the CodeDom
        // provider needs them for code generation
        Util.AddAssembliesToStringCollection(_initialReferencedAssemblies, compileUnit.ReferencedAssemblies);
 
        // Merge the _additionalReferencedAssemblies from individul build providers
        Util.AddAssembliesToStringCollection(_additionalReferencedAssemblies, compileUnit.ReferencedAssemblies); 
 
        String filename;
 
        // Revert impersonation when generating source code in the codegen dir (VSWhidbey 176576)
        using (new ProcessImpersonationContext()) {
            TextWriter writer = CreateCodeFile(buildProvider, out filename);
 
            try {
                _codeProvider.GenerateCodeFromCompileUnit(compileUnit, writer, null /*CodeGeneratorOptions*/); 
            } 
            finally {
                writer.Flush(); 
                writer.Close();
            }
        }
 
        if (filename != null) {
            FileInfo info = new FileInfo(filename); 
            _totalFileLength += info.Length; 
        }
    } 

    /// 
    ///     Tell the host about a type that is being generated.  This allows the host
    ///     To generate a fast object factory for it. 
    /// 
    public void GenerateTypeFactory(string typeName) { 
 
        // Create the object factory generator on demand
        if (_objectFactoryGenerator == null) { 
            _objectFactoryGenerator = new ObjectFactoryCodeDomTreeGenerator(OutputAssemblyName);
        }

        // Add a method to fast create this type 
        _objectFactoryGenerator.AddFactoryMethod(typeName);
    } 
 
    /// 
    ///     Creates a new resource that will be added to the compilation.  The build provider 
    ///     can write to it using the returned Stream.
    ///     The build provider should close the Stream when it is done writing to it.
    ///     The build provider should pass itself as a parameter to this method.
    ///  
    public Stream CreateEmbeddedResource(BuildProvider buildProvider, string name) {
 
        // Make sure it's just a valid simple file name 
        if (!Util.IsValidFileName(name)) {
            throw new ArgumentException(null, name); 
        }

        string resourceFile = Path.Combine(_tempFiles.TempDir, name);
        _tempFiles.AddFile(resourceFile, _tempFiles.KeepFiles); 

        if (_embeddedResourceFiles == null) 
            _embeddedResourceFiles = new StringSet(); 

        _embeddedResourceFiles.Add(resourceFile); 

        return File.OpenWrite(resourceFile);
    }
 
    /// 
    ///     Returns a CodeDomProvider that the build provider can use to generate a CodeCompileUnit. 
    ///  
    public CodeDomProvider CodeDomProvider {
        get { return _codeProvider; } 
    }

    private string _tempFilePhysicalPathPrefix;
    private string TempFilePhysicalPathPrefix { 
        get {
            if (_tempFilePhysicalPathPrefix == null) { 
                _tempFilePhysicalPathPrefix = Path.Combine(_tempFiles.TempDir, OutputAssemblyName) + "."; 

                // Append the culture name to avoid naming conflicts 
                if (CultureName != null) {
                    _tempFilePhysicalPathPrefix += CultureName + "_";
                }
            } 

            return _tempFilePhysicalPathPrefix; 
        } 
    }
 
    /// 
    ///     Returns the physical path to a temporary file that the build provider
    ///     can use for intermediate results.  Note that the file is not actually
    ///     created.  It is up to the build provider to do this. 
    ///     The temp file's extension is passed in by the build provider.
    ///     The file is automatically deleted after the compilation, so the 
    ///     build provider does not need to explicitly delete it. 
    /// 
    public string GetTempFilePhysicalPath(string extension) { 

        // Do the right thing depending on whether the extension include the starting '.'
        string tempPath;
        if (!String.IsNullOrEmpty(extension) && extension[0] == '.') { 
            tempPath = TempFilePhysicalPathPrefix + ((_fileCount++) + extension);
        } 
        else { 
            tempPath = TempFilePhysicalPathPrefix + ((_fileCount++) + "." + extension);
        } 

        _tempFiles.AddFile(tempPath, _tempFiles.KeepFiles);

        InternalSecurityPermissions.PathDiscovery(tempPath).Demand(); 

        return tempPath; 
    } 

    // End of public contract 

    private void AddCompileWithBuildProvider(VirtualPath virtualPath, BuildProvider owningBuildProvider) {

        BuildProvider buildProvider = BuildManager.CreateBuildProvider(virtualPath, 
            _compConfig, _initialReferencedAssemblies, true /*failIfUnknown*/);
 
        // Since it's referenced via compileWith, it doesn't need its own build result 
        buildProvider.SetNoBuildResult();
 
        // If it's a CompileWith provider, remember the main provider
        SourceFileBuildProvider sourceBuildProvider = buildProvider as SourceFileBuildProvider;
        if (sourceBuildProvider != null)
            sourceBuildProvider.OwningBuildProvider = owningBuildProvider; 

        AddBuildProvider(buildProvider); 
    } 

    internal virtual void AddBuildProvider(BuildProvider buildProvider) { 

        // By default, use the build provider itself as the key
        object hashtableKey = buildProvider;
 
        // Keep track of the build provider's virtual path, if any
        if (buildProvider.VirtualPath != null) { 
 
            // It has a virtual path, so use that as the key
            hashtableKey = buildProvider.VirtualPath; 

            // If we already had it, ignore it.  This can happen when there is a user control
            // with a code beside in App_Code (VSWhidbey 481426)
            if (_buildProviders.ContainsKey(hashtableKey)) 
                return;
        } 
 
        _buildProviders[hashtableKey] = buildProvider;
 
        // Ask the provider to generate the code
        // If it throws an Xml exception, extra the relevant info and turn it
        // into our own ParseException
        try { 
            buildProvider.GenerateCode(this);
        } 
        catch (XmlException e) { 
            throw new HttpParseException(e.Message, null /*innerException*/,
                buildProvider.VirtualPath, null /*sourceCode*/, e.LineNumber); 
        }
        catch (XmlSchemaException e) {
            throw new HttpParseException(e.Message, null /*innerException*/,
                buildProvider.VirtualPath, null /*sourceCode*/, e.LineNumber); 
        }
        catch (Exception e) { 
            throw new HttpParseException(e.Message, e, 
                buildProvider.VirtualPath, null /*sourceCode*/, 1);
        } 

        // Handle any 'compileWith' dependencies, i.e. files that must be compiled
        // within the same assembly as the current file
        InternalBuildProvider internalBuildProvider = buildProvider as InternalBuildProvider; 
        if (internalBuildProvider != null) {
            ICollection compileWith = internalBuildProvider.GetCompileWithDependencies(); 
            if (compileWith != null) { 
                foreach (VirtualPath virtualPath in compileWith) {
 
                    // If we already have it, ignore it
                    if (_buildProviders.ContainsKey(virtualPath.VirtualPathString))
                        continue;
 
                    // Add the compileWith dependency to our compilation
                    AddCompileWithBuildProvider(virtualPath, internalBuildProvider); 
                } 
            }
        } 

    }

    private void AddAssemblyCultureAttribute() { 

        if (CultureName == null) return; 
 
        CodeAttributeDeclaration declaration = new CodeAttributeDeclaration(
            new CodeTypeReference(typeof(System.Reflection.AssemblyCultureAttribute)), 
            new CodeAttributeArgument[] {
                    new CodeAttributeArgument(new CodePrimitiveExpression(CultureName))});

        AddAssemblyAttribute(declaration); 
    }
 
#if UNUSED 
    private void AddComVisibleAttribute() {
 
        CodeAttributeDeclaration declaration = new CodeAttributeDeclaration(
            new CodeTypeReference(typeof(System.Runtime.InteropServices.ComVisibleAttribute)),
            new CodeAttributeArgument[] {
                    new CodeAttributeArgument(new CodePrimitiveExpression(false))}); 

        AddAssemblyAttribute(declaration); 
    } 
#endif
 
    private void AddAspNetGeneratedCodeAttribute() {

        CodeAttributeDeclaration declaration = new CodeAttributeDeclaration(
            new CodeTypeReference(typeof(GeneratedCodeAttribute))); 
        declaration.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression("ASP.NET")));
        declaration.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(VersionInfo.SystemWebVersion))); 
 
        AddAssemblyAttribute(declaration);
    } 

    private void AddAllowPartiallyTrustedCallersAttribute() {
        if (BuildManager.CompileWithAllowPartiallyTrustedCallersAttribute) {
            CodeAttributeDeclaration declaration = new CodeAttributeDeclaration( 
                new CodeTypeReference(typeof(AllowPartiallyTrustedCallersAttribute)));
 
            AddAssemblyAttribute(declaration); 
        }
    } 

    private void AddAssemblyKeyFileAttribute() {
        if (!String.IsNullOrEmpty(BuildManager.StrongNameKeyFile)) {
            CodeAttributeDeclaration declaration = new CodeAttributeDeclaration( 
                new CodeTypeReference(typeof(AssemblyKeyFileAttribute)),
                new CodeAttributeArgument(new CodePrimitiveExpression(BuildManager.StrongNameKeyFile))); 
 
            AddAssemblyAttribute(declaration);
        } 
    }

    private void AddAssemblyKeyContainerAttribute() {
        if (!String.IsNullOrEmpty(BuildManager.StrongNameKeyContainer)) { 
            CodeAttributeDeclaration declaration = new CodeAttributeDeclaration(
                new CodeTypeReference(typeof(AssemblyKeyNameAttribute)), 
                new CodeAttributeArgument(new CodePrimitiveExpression(BuildManager.StrongNameKeyContainer))); 

            AddAssemblyAttribute(declaration); 
        }
    }

    private void AddAssemblyDelaySignAttribute() { 
        if (BuildManager.CompileWithDelaySignAttribute) {
            CodeAttributeDeclaration declaration = new CodeAttributeDeclaration( 
                new CodeTypeReference(typeof(AssemblyDelaySignAttribute)), 
                new CodeAttributeArgument(new CodePrimitiveExpression(true)));
 
            AddAssemblyAttribute(declaration);
        }
    }
 
    // Add an assembly level attribute to the assembly
    private void AddAssemblyAttribute(CodeAttributeDeclaration declaration) { 
 
        if (_miscCodeCompileUnit == null)
            _miscCodeCompileUnit = new CodeCompileUnit(); 

        _miscCodeCompileUnit.AssemblyCustomAttributes.Add(declaration);
    }
 
    private void GenerateMiscCodeCompileUnit() {
 
        // If there aren't any, return 
        if (_miscCodeCompileUnit == null)
            return; 

        AddCodeCompileUnit(null /*buildProvider*/, _miscCodeCompileUnit);
    }
 
    // Add a checksum pragma.  This is used for improved debugging experience.
    private void AddChecksumPragma(BuildProvider buildProvider, CodeCompileUnit compileUnit) { 
 
        // If we can't get a virtual path, do nothing
        if (buildProvider == null || buildProvider.VirtualPath == null) 
            return;

        // Only do this if we're compiling in debug mode
        if (!_compilerType.CompilerParameters.IncludeDebugInformation) 
            return;
 
        string physicalPath = HostingEnvironment.MapPathInternal(buildProvider.VirtualPath); 

        // Only do this is the file physically exists, which it would not in the 
        // case of a non-file based VirtualPathProvider.  In such case, there is
        // no point in putting the pragma, since the debugger could not locate
        // the file anyway.
        if (!File.Exists(physicalPath)) 
            return;
 
#if !FEATURE_PAL // FEATURE_PAL does not yet support MD5 Crypto 
        // Initialize the MD5 objects on demand
        if (s_md5HashAlgorithm == null) { 
            s_md5HashAlgorithm = new MD5CryptoServiceProvider();
            s_hashMD5Guid = new Guid(0x406ea660, 0x64cf, 0x4c82, 0xb6, 0xf0, 0x42, 0xd4, 0x81, 0x72, 0xa7, 0x99);
        }
 
        CodeChecksumPragma pragma = new CodeChecksumPragma();
        if (_compConfig.UrlLinePragmas) { 
            pragma.FileName = ErrorFormatter.MakeHttpLinePragma(buildProvider.VirtualPathObject.VirtualPathString); 
        }
        else { 
            pragma.FileName = physicalPath;
        }
        pragma.ChecksumAlgorithmId = s_hashMD5Guid;
 
        // Generate an MD5 hash from the contents of the file
        using (Stream stream = new FileStream(physicalPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { 
            pragma.ChecksumData = s_md5HashAlgorithm.ComputeHash(stream); 
        }
 
        // Add the pragma to the CodeCompileUnit
        compileUnit.StartDirectives.Add(pragma);
#endif
    } 

    internal CompilerParameters GetCompilerParameters() { 
        CompilerParameters compilParams = _compilerType.CompilerParameters; 

        string dir = _tempFiles.TempDir; 

        // If a culture is set, modify the assembly name and location based on it
        if (CultureName != null) {
            dir = Path.Combine(dir, CultureName); 
            Directory.CreateDirectory(dir);
            compilParams.OutputAssembly = Path.Combine(dir, OutputAssemblyName + ".resources.dll"); 
        } 
        else {
            compilParams.OutputAssembly = Path.Combine(dir, OutputAssemblyName + ".dll"); 
        }

        // If such file already exist, try to delete or rename it
        if (File.Exists(compilParams.OutputAssembly)) 
            Util.RemoveOrRenameFile(compilParams.OutputAssembly);
 
        compilParams.TempFiles = _tempFiles; 

        // Create the string resource file (shared by all the pages we're compiling) 
        if (_stringResourceBuilder != null && _stringResourceBuilder.HasStrings) {
            string resFileName = _tempFiles.AddExtension("res");
            _stringResourceBuilder.CreateResourceFile(resFileName);
            compilParams.Win32Resource = resFileName; 
        }
 
        // Add all the embedded resources to the compilParams 
        if (_embeddedResourceFiles != null) {
            foreach (string aname in _embeddedResourceFiles) 
                compilParams.EmbeddedResources.Add(aname);
        }

        // Merge the two sets of assemblies 
        if (_additionalReferencedAssemblies != null) {
            foreach (Assembly assembly in _additionalReferencedAssemblies) { 
                _initialReferencedAssemblies.Add(assembly); 
            }
        } 

        // Add all the referenced assemblies to the compilParams
        Util.AddAssembliesToStringCollection(_initialReferencedAssemblies, compilParams.ReferencedAssemblies);
 
        // Make any fix up adjustments to the CompilerParameters to work around some issues
        FixUpCompilerParameters(_compilerType.CodeDomProviderType, compilParams); 
 
        return compilParams;
    } 

    static string s_vbImportsString;

    private static void AddVBGlobalNamespaceImports(CompilerParameters compilParams) { 
        // Put together the VB import string on demand
        if (s_vbImportsString == null) { 
            PagesSection pagesConfig = RuntimeConfig.GetAppConfig().Pages; 
            if (pagesConfig.Namespaces == null) {
                s_vbImportsString = String.Empty; 
            }
            else {
                StringBuilder sb = new StringBuilder();
                sb.Append("/imports:"); 

                bool nextItemNeedsComma = false; 
 
                // Auto-import Microsoft.VisualBasic is needed
                if (pagesConfig.Namespaces.AutoImportVBNamespace) { 
                    sb.Append("Microsoft.VisualBasic");
                    nextItemNeedsComma = true;
                }
 
                // Add all the namespaces from the config  section
                foreach (NamespaceInfo entry in pagesConfig.Namespaces) { 
 
                    // If there was a previous entry, we need a comma separator
                    if (nextItemNeedsComma) 
                        sb.Append(',');

                    sb.Append(entry.Namespace);
 
                    nextItemNeedsComma = true;
                } 
 
                s_vbImportsString = sb.ToString();
            } 
        }

        // Prepend it to the compilerOptions
        if (s_vbImportsString.Length > 0) { 
            if (compilParams.CompilerOptions == null)
                compilParams.CompilerOptions = s_vbImportsString; 
            else 
                compilParams.CompilerOptions = s_vbImportsString + " " + compilParams.CompilerOptions;
        } 

    }

    // Command line string for My.* support 
    private const string MySupport = @"/define:_MYTYPE=\""Web\""";
 
    private static void AddVBMyFlags(CompilerParameters compilParams) { 

        // Prepend it to the compilerOptions 
        if (compilParams.CompilerOptions == null)
            compilParams.CompilerOptions = MySupport;
        else
            compilParams.CompilerOptions = MySupport + " " + compilParams.CompilerOptions; 
    }
 
    internal static void FixUpCompilerParameters(Type codeDomProviderType, CompilerParameters compilParams) { 
        // If C#, remove the warning that complains about variables that start with "__"
        // Also ignore warning that complains about assemblyKeyName and delaysign 
        // Also ignore warning about assuming assembly versions matching (CS1701, DevDiv 137847, warning about System.Web.Extensions v1.0 matching v3.5)
        if (codeDomProviderType == typeof(Microsoft.CSharp.CSharpCodeProvider)) {
            CodeDomUtility.PrependCompilerOption(compilParams, "/nowarn:1659;1699;1701");
        } 
        else if (codeDomProviderType == typeof(Microsoft.VisualBasic.VBCodeProvider)) {
 
            // If VB, add all the imported namespaces on the command line (DevDiv 21499). 
            // This is VB only because other languages don't support global command line
            // namespace imports. 
            AddVBGlobalNamespaceImports(compilParams);

            // Add any command line flags needed to support the My.* feature
            AddVBMyFlags(compilParams); 
        }
 
        ProcessProviderOptions(codeDomProviderType, compilParams); 
        FixTreatWarningsAsErrors(codeDomProviderType, compilParams);
 
        // Add CodeAnalysis symbol if required by client.
        if (BuildManager.PrecompilingWithCodeAnalysisSymbol) {
            CodeDomUtility.PrependCompilerOption(compilParams, "/define:CODE_ANALYSIS");
        } 
    }
 
    // DevDiv 114316 
    // CodeDom sets TreatWarningAsErrors to true whenever warningLevel is non-zero.
    // To get warnings only, the workaround is to use /warnaserror- in CompilerOptions. 
    // However this does not work in some cases, as TreatWarningAsErrors set to true still emits
    // /warnaserror+.
    // So, whenever the user wants /warnaserror[+|-|numberlist], we explicitly set TreatWarningsAsErrors to false,
    // so that the /warnaserror+ is not emitted, and the user can specify exactly what is desired. 
    internal static void FixTreatWarningsAsErrors(Type codeDomProviderType, CompilerParameters compilParams) {
        // Only do so for C# and VB. 
        if (codeDomProviderType != typeof(Microsoft.CSharp.CSharpCodeProvider) && 
            codeDomProviderType != typeof(Microsoft.VisualBasic.VBCodeProvider))
            return; 

        if (CultureInfo.InvariantCulture.CompareInfo.IndexOf(compilParams.CompilerOptions, "/warnaserror", CompareOptions.IgnoreCase) >= 0)
            compilParams.TreatWarningsAsErrors = false;
    } 

 
    // Check for OptionInfer and WarnAsError. This is the workaround as use of compilerOptions is not allowed in partial trust. 
    // Devdiv 130325
    private static void ProcessProviderOptions(Type codeDomProviderType, CompilerParameters compilParams) { 
        IDictionary providerOptions = CompilationUtil.GetProviderOptions(codeDomProviderType);
        if (providerOptions == null) return;

        // For C# and VB, check for WarnAsError 
        if (codeDomProviderType == typeof(Microsoft.VisualBasic.VBCodeProvider) ||
            codeDomProviderType == typeof(Microsoft.CSharp.CSharpCodeProvider)) 
            ProcessBooleanProviderOption("WarnAsError", "/warnaserror+", "/warnaserror-", providerOptions, compilParams); 

        // Only process OptionInfer for v3.5 compiler 
        if (codeDomProviderType == null || !CompilationUtil.IsCompilerVersion35(codeDomProviderType))
            return;

        // For VB, check for OptionInfer 
        if (codeDomProviderType == typeof(Microsoft.VisualBasic.VBCodeProvider))
            ProcessBooleanProviderOption("OptionInfer", "/optionInfer+", "/optionInfer-", providerOptions, compilParams); 
 
    }
 
    private static void ProcessBooleanProviderOption(string providerOptionName, string trueCompilerOption, string falseCompilerOption,
        IDictionary providerOptions, CompilerParameters compilParams) {

        if (providerOptions == null || compilParams == null) return; 
        Debug.Assert(providerOptionName != null, "providerOptionName should not be null");
        Debug.Assert(trueCompilerOption != null, "trueCompilerOption should not be null"); 
        Debug.Assert(falseCompilerOption != null, "falseCompilerOption should not be null"); 

        string providerOptionValue = null; 
        if (!providerOptions.TryGetValue(providerOptionName, out providerOptionValue)) return;
        if (string.IsNullOrEmpty(providerOptionValue))
            throw new System.Configuration.ConfigurationException(SR.GetString(SR.Property_NullOrEmpty, CompilationUtil.CodeDomProviderOptionPath + providerOptionName));
 
        bool value;
 
        if (Boolean.TryParse(providerOptionValue, out value)) { 
            // If the value is boolean, insert the compiler options
            if (value) 
                CodeDomUtility.AppendCompilerOption(compilParams, trueCompilerOption);
            else
                CodeDomUtility.AppendCompilerOption(compilParams, falseCompilerOption);
        } 
        else {
            // If the value is not boolean, throw an exception 
            throw new System.Configuration.ConfigurationException(SR.GetString(SR.Value_must_be_boolean, CompilationUtil.CodeDomProviderOptionPath + providerOptionName)); 
        }
    } 


    internal CompilerResults Compile() {
 
        // First, check if there is something to compile
        if (_sourceFiles.Count == 0 && _embeddedResourceFiles == null) 
            return null; 

        // if we have some fast object factories to generate, get the CodeCompileUnit 
        if (_objectFactoryGenerator != null) {
            _miscCodeCompileUnit = _objectFactoryGenerator.CodeCompileUnit;
        }
 
        // Add a culture attribute if needed
        AddAssemblyCultureAttribute(); 
 
        // Add a ComVisible(false) attribute (VSWhidbey 436453)
        // Actually, don't do it to avoid breaking migrated apps (VSWhidbey 446788) 
        //AddComVisibleAttribute();

        // Add an AspNetGeneratedCode attribute to help fxcop ignore some violations (VSWhidbey 437581)
        AddAspNetGeneratedCodeAttribute(); 

        // Add an AllowPartiallyTrustedCallers attribute to make strong-name assemblies. (Devdiv 39696) 
        AddAllowPartiallyTrustedCallersAttribute(); 

        AddAssemblyDelaySignAttribute(); 

        AddAssemblyKeyFileAttribute();

        AddAssemblyKeyContainerAttribute(); 

        // Generate a source file for the misc top level items if needed 
        GenerateMiscCodeCompileUnit(); 

        CompilerParameters compilParams = GetCompilerParameters(); 

        string[] files = new string[_sourceFiles.Count];
        _sourceFiles.CopyTo(files, 0);
 
        // Increment compilation counter
        PerfCounters.IncrementCounter(AppPerfCounter.COMPILATIONS); 
 
        // Raise Web Event
        WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationCompilationStart); 

        HttpContext context = HttpContext.Current;

        if (context != null) { 
            if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_COMPILE_ENTER, context.WorkerRequest);
        } 
 
        CompilerResults results = null;
 
        try {
            try {
                // Revert impersonation when compiling source code in the codegen dir (VSWhidbey 176576)
                using (new ProcessImpersonationContext()) { 
                    results = _codeProvider.CompileAssemblyFromFile(compilParams, files);
                } 
            } 
            finally {
                if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure) && context != null) { 

                    string fileNames = null;
                    if (_buildProviders.Count < 20) {
                        IDictionaryEnumerator e = _buildProviders.GetEnumerator(); 
                        while(e.MoveNext()) {
                            if (fileNames != null) 
                                fileNames += ","; 
                            fileNames += e.Key;
                        } 
                    }
                    else {
                        fileNames = String.Format(CultureInfo.InstalledUICulture, SR.Resources.GetString(SR.Etw_Batch_Compilation, CultureInfo.InstalledUICulture), new object[1] {_buildProviders.Count});
                    } 

                    string status; 
                    if (results != null && (results.NativeCompilerReturnValue != 0 || results.Errors.HasErrors)) 
                        status = SR.Resources.GetString(SR.Etw_Failure, CultureInfo.InstalledUICulture);
                    else 
                        status = SR.Resources.GetString(SR.Etw_Success, CultureInfo.InstalledUICulture);

                    EtwTrace.Trace(EtwTraceType.ETW_TYPE_COMPILE_LEAVE, context.WorkerRequest, fileNames, status);
                } 
            }
        } 
        catch { throw; }    // Prevent Exception Filter Security Issue (ASURT 122835) 

        // If an IAssemblyPostProcessor is registered, call it 
        Type postProcessorType = _compConfig.AssemblyPostProcessorTypeInternal;
        if (postProcessorType != null) {
            using (IAssemblyPostProcessor postProcessor = (IAssemblyPostProcessor) HttpRuntime.FastCreatePublicInstance(postProcessorType)) {
                postProcessor.PostProcessAssembly(results.PathToAssembly); 
            }
        } 
 
        // Raise Web Event
        WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationCompilationEnd); 

        if (results != null) {

            // Invalidate an invalid assembly to trigger recompilation 
            InvalidateInvalidAssembly(results, compilParams);
 
            // Fix up with line pragmas to account for the http case, and for some special conditions 
            FixUpLinePragmas(results);
 
            // If there is a CBM callback, inform it of the errors/warnings
            if (BuildManager.CBMCallback != null) {
                foreach (CompilerError error in results.Errors) {
                    BuildManager.CBMCallback.ReportCompilerError(error); 
                }
            } 
 
            // If there are errors, increment the relevant perf counters and throw
            if (results.NativeCompilerReturnValue != 0 || results.Errors.HasErrors) { 

                // Increment the compilation error and total error counters
                PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_COMPILING);
                PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_TOTAL); 

                throw new HttpCompileException(results, GetErrorSourceFileContents(results)); 
            } 
        }
 
        return results;
    }

    private void InvalidateInvalidAssembly(CompilerResults results, CompilerParameters compilParams) { 
        // VSWhidbey 610291
        // If target assembly gets locked, we invalidate the assembly, so that it does 
        // not get used, and the next compilation will use a new assembly name. 
        // CS0016 is the error code for "Could not write to output file 'file' -- 'reason'"
 
        if (results == null || !results.Errors.HasErrors)
            return;

        foreach (CompilerError error in results.Errors) { 
            if (error.IsWarning) continue;
 
            if (StringUtil.EqualsIgnoreCase(error.ErrorNumber, "CS0016")){ 

                // Also invalidate the base assembly if this is a localized resource assembly 
                if (CultureName != null) {
                    string dir = _tempFiles.TempDir;
                    string baseAssemblyFile = Path.Combine(dir, OutputAssemblyName + ".dll");
                    DiskBuildResultCache.TryDeleteFile(new FileInfo(baseAssemblyFile)); 
                }
 
                // Invalidate the target assembly 
                DiskBuildResultCache.TryDeleteFile(compilParams.OutputAssembly);
            } 
        }
    }

    /* 
     * Fix up all the source files in the errors in case they are HTTP (VS compiler scenario).
     * Also, fix the error in case the base class was incorrect in the code beside model 
     */ 
    private void FixUpLinePragmas(CompilerResults results) {
 
        CompilerError badBaseClassError = null;

        // Go through the errors backwards so we can delete them as needed
        for (int i=results.Errors.Count-1; i>=0; i--) { 
            CompilerError error = results.Errors[i];
 
            string physicalPath = ErrorFormatter.ResolveHttpFileName(error.FileName); 

            // Only replace it by the physical path if it actually exists, which may not 
            // be the case when using a VirtualPathProvider
            if (File.Exists(physicalPath)) {
                error.FileName = physicalPath;
 
                // If it is our special marker line number, remember it and remove it.
                if (error.Line == TemplateControlCodeDomTreeGenerator.badBaseClassLineMarker) { 
                    badBaseClassError = error; 
                    results.Errors.RemoveAt(i);
                } 
                else if (error.Line > TemplateControlCodeDomTreeGenerator.badBaseClassLineMarker &&
                    error.Line < TemplateControlCodeDomTreeGenerator.badBaseClassLineMarker + 50) {

                    // Also, if within range of it, remove it altogether 
                    results.Errors.RemoveAt(i);
                } 
            } 
        }
 
        // If we found our special marker error, we're most likely in a situation where
        // the class in the code beside file doesn't match the 'inherits' in the aspx/ascx,
        // or is missing the based type (or has the wrong base type).  In that case, change
        // the error message to make the problem explicit to the user (VSWhidbey 376977/468830) 
        if (badBaseClassError != null) {
 
            // Read the content of the code beside file 
            string codeFileContent = Util.StringFromFile(badBaseClassError.FileName);
 
            // Search for the partial class declaration within the file.  We do this by searching for
            // the string "partial class" in case insensitive way.  This is far from fool proof, but
            // it covers the common VB and C# cases, and the fallback when not found is reasonable.
            int classOffset = CultureInfo.InvariantCulture.CompareInfo.IndexOf(codeFileContent, 
                "partial class", CompareOptions.IgnoreCase);
 
            if (classOffset >= 0) { 
                // We found it, so figure out the line number from it
                badBaseClassError.Line = Util.LineCount(codeFileContent, 0, classOffset) + 1; 
            }
            else {
                // Otherwise, just use 1.  It won't point to the right line, but at least the error
                // message is helpful 
                badBaseClassError.Line = 1;
            } 
 
            // Change the error message to make the situation clear to the user
            badBaseClassError.ErrorText = SR.GetString(SR.Bad_Base_Class_In_Code_File); 
            badBaseClassError.ErrorNumber = "ASPNET";

            // Insert the error at the begining of the collection, since we display the first error.
            results.Errors.Insert(0, badBaseClassError); 
        }
    } 
 
    /*
     * Attempt to find the generated source file that has the error, and return 
     * its contents as a string (for error reproting purposes).
     * Note that when debug is false, we set tempFiles.KeepFiles to false, and
     * all the sources will be gone by the time we get here.  I filed VSWhidbey 103673,
     * to get a solution to this from BCL. 
     */
    private string GetErrorSourceFileContents(CompilerResults results) { 
 
        if (!results.Errors.HasErrors)
            return null; 

        // Get the physical path of the file that has the error. Note that this could be
        // either the path to a high level file (e.g. aspx) if pragmas are in play,
        // or the path to a generated file if there are no pragmas 
        string linePragma = results.Errors[0].FileName;
 
        // Attempt to locate the correct build provider 
        BuildProvider buildProvider = GetBuildProviderFromLinePragma(linePragma);
 
        if (buildProvider != null) {
            // Return the generated file for this build provider
            return GetGeneratedSourceFromBuildProvider(buildProvider);
        } 

        // If we didn't find it, then we're probably in the no pragma case, in 
        // which case linePragma itself is the generated file 
        return Util.StringFromFileIfExists(linePragma);
    } 

    internal string GetGeneratedSourceFromBuildProvider(BuildProvider buildProvider) {

        // Return the generated file content for this build provider 
        string generatedFilePath = (string) _buildProviderToSourceFileMap[buildProvider];
        return Util.StringFromFileIfExists(generatedFilePath); 
    } 

    internal BuildProvider GetBuildProviderFromLinePragma(string linePragma) { 
        BuildProvider buildProvider = GetBuildProviderFromLinePragmaInternal(linePragma);

        // If it's a CompileWith provider, return the main provider instead
        SourceFileBuildProvider sourceBuildProvider = buildProvider as SourceFileBuildProvider; 
        if (sourceBuildProvider != null)
            buildProvider = sourceBuildProvider.OwningBuildProvider; 
 
        return buildProvider;
    } 

    private BuildProvider GetBuildProviderFromLinePragmaInternal(string linePragma) {

        // If we didn't keep track of any generated files, we can't do much 
        if (_buildProviderToSourceFileMap == null)
            return null; 
 
        // Check if it's an http line pragma, from which we can get a VirtualPath
        string virtualPath = ErrorFormatter.GetVirtualPathFromHttpLinePragma(linePragma); 

        // First, look for the pragma case
        foreach (BuildProvider buildProvider in BuildProviders) {
 
            // If the build provider can't give us a virtual path, skip it
            if (buildProvider.VirtualPath == null) 
                continue; 

            // If we got a virtual path, use it to locate the correct BuildProvider 
            if (virtualPath != null) {
                if (StringUtil.EqualsIgnoreCase(virtualPath, buildProvider.VirtualPath)) {
                    return buildProvider;
                } 

                continue; 
            } 

            // Otherwise, work with the physical path 

            string physicalPath = HostingEnvironment.MapPathInternal(buildProvider.VirtualPath);

            if (StringUtil.EqualsIgnoreCase(linePragma, physicalPath)) { 
                return buildProvider;
            } 
        } 

        return null; 
    }
}

/* 
 * This class is used intead of AssemblyBuilder when handling
 * ClientBuildManager.GetCodeDirectoryInformation 
 * It is instantiated via CompilerType.CreateAssemblyBuilder. 
 */
internal class CbmCodeGeneratorBuildProviderHost: AssemblyBuilder { 

    private string _generatedFilesDir;

    internal CbmCodeGeneratorBuildProviderHost(CompilationSection compConfig, 
        ICollection referencedAssemblies, CompilerType compilerType,
        string generatedFilesDir, string outputAssemblyName) 
        : base(compConfig, referencedAssemblies, compilerType, outputAssemblyName) { 

        // Wipe out any existing directory, and recreate it 
        // This is where we will put generated source files
        if (Directory.Exists(generatedFilesDir)) {

            // Delete all the files in the directory 
            foreach (FileData fileData in FileEnumerator.Create(generatedFilesDir)) {
 
                // It should only contain files 
                Debug.Assert(!fileData.IsDirectory);
                if (fileData.IsDirectory) continue; 

                Debug.Trace("CbmCodeGeneratorBuildProviderHost", "Deleting " + fileData.FullName);
                File.Delete(fileData.FullName);
            } 

        } 
 
        // Create it to make sure it exists
        Directory.CreateDirectory(generatedFilesDir); 

        _generatedFilesDir = generatedFilesDir;
    }
 
    internal override TextWriter CreateCodeFile(BuildProvider buildProvider, out string filename) {
 
        // use GetCacheKeyFromVirtualPath to get a file name that looks like 
        // the original file, but is guaranteed unique across different virtual dirs.
        string generatedCodeFile = BuildManager.GetCacheKeyFromVirtualPath( 
            buildProvider.VirtualPathObject);

        generatedCodeFile = Path.Combine(_generatedFilesDir, generatedCodeFile);
        generatedCodeFile = FileUtil.TruncatePathIfNeeded(generatedCodeFile, 10 /*length of extension */); 

        generatedCodeFile = generatedCodeFile + "." + _codeProvider.FileExtension; 
        filename = generatedCodeFile; 

        BuildManager.GenerateFileTable[buildProvider.VirtualPathObject.VirtualPathStringNoTrailingSlash] = generatedCodeFile; 

        Debug.Trace("CbmCodeGeneratorBuildProviderHost", "Generating " + generatedCodeFile);

        Stream temp = new FileStream(generatedCodeFile, FileMode.Create, FileAccess.Write, FileShare.Read); 
        return new StreamWriter(temp, Encoding.UTF8);
    } 
 
    internal override void AddBuildProvider(BuildProvider buildProvider) {
 
        // Skip source files, since their code generation is an identity transform
        if (buildProvider is SourceFileBuildProvider)
            return;
 
        base.AddBuildProvider(buildProvider);
    } 
} 

}
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK