e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
548 lines
18 KiB
C#
548 lines
18 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="BuildProvider.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/*********************************
|
|
|
|
Class hierarchy
|
|
|
|
BuildProvider
|
|
ProfileBuildProvider
|
|
BaseResourcesBuildProvider
|
|
ResXBuildProvider
|
|
ResourcesBuildProvider
|
|
XsdBuildProvider
|
|
WsdlBuildProvider
|
|
InternalBuildProvider
|
|
SourceFileBuildProvider
|
|
SimpleHandlerBuildProvider
|
|
WebServiceBuildProvider
|
|
WebHandlerBuildProvider
|
|
ImageGeneratorBuildProvider
|
|
BaseTemplateBuildProvider
|
|
ApplicationBuildProvider
|
|
TemplateControlBuildProvider
|
|
PageBuildProvider
|
|
UserControlBuildProvider
|
|
MasterPageBuildProvider
|
|
PageThemeBuildProvider
|
|
GlobalPageThemeBuildProvider
|
|
|
|
**********************************/
|
|
|
|
|
|
namespace System.Web.Compilation {
|
|
|
|
using System;
|
|
using System.Security.Permissions;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.CodeDom;
|
|
using System.CodeDom.Compiler;
|
|
using System.Web.Util;
|
|
using System.Web.UI;
|
|
using System.Web.Hosting;
|
|
|
|
// Flags returned from BuildProvider.GetResultFlags
|
|
[Flags]
|
|
public enum BuildProviderResultFlags {
|
|
Default = 0,
|
|
ShutdownAppDomainOnChange = 1,
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>
|
|
/// Base class for build providers that want to participate in a compilation.
|
|
/// It should be used by build providers that process files based on a virtual path.
|
|
/// </para>
|
|
/// </devdoc>
|
|
public abstract class BuildProvider {
|
|
|
|
private static Dictionary<string, BuildProviderInfo> s_dynamicallyRegisteredProviders = new Dictionary<string, BuildProviderInfo>();
|
|
|
|
//
|
|
// Public interface
|
|
//
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns the CompilerType for the language that this build provider
|
|
/// needs to use, or null of it can use any language.
|
|
/// </devdoc>
|
|
public virtual CompilerType CodeCompilerType { get { return null; } }
|
|
|
|
|
|
/// <devdoc>
|
|
/// Asks this build provider to generate any code that it needs, using the various
|
|
/// methods on the passed in BuildProvider.
|
|
/// </devdoc>
|
|
public virtual void GenerateCode(AssemblyBuilder assemblyBuilder) {}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns the class generated by this buildprovider
|
|
/// </devdoc>
|
|
public virtual Type GetGeneratedType(CompilerResults results) {
|
|
return null;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns a string that the provider wants to have persisted with the compiled assembly.
|
|
/// </devdoc>
|
|
public virtual string GetCustomString(CompilerResults results) {
|
|
return null;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns some flags that drives the behavior of the persisted result.
|
|
/// </devdoc>
|
|
public virtual BuildProviderResultFlags GetResultFlags(CompilerResults results) {
|
|
return BuildProviderResultFlags.Default;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Give the BuildProvider a chance to look at the compile errors, and possibly tweak them
|
|
/// </devdoc>
|
|
public virtual void ProcessCompileErrors(CompilerResults results) {
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// Returns the list of files (virtual paths) that this provider depends on.
|
|
/// Those files need to be built before this BuildProvider. The resulting assemblies
|
|
/// are added as references when compiling this BuildProvider.
|
|
/// This does not include things like server side includes, which don't directly
|
|
/// produce a BuildResult.
|
|
/// This is used to implement batching by separating BuildProviders
|
|
/// </devdoc>
|
|
internal virtual ICollection GetBuildResultVirtualPathDependencies() { return null; }
|
|
|
|
public virtual ICollection VirtualPathDependencies {
|
|
get {
|
|
// By default, return the virtual path as its only dependency
|
|
return new SingleObjectCollection(VirtualPath);
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns the virtual path that this build provider handles
|
|
/// </devdoc>
|
|
protected internal string VirtualPath {
|
|
get { return System.Web.VirtualPath.GetVirtualPathString(_virtualPath); }
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns the virtual path object that this build provider handles
|
|
/// </devdoc>
|
|
internal VirtualPath VirtualPathObject {
|
|
get { return _virtualPath; }
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Opens a stream for the virtual file handled by this provider
|
|
/// </devdoc>
|
|
protected Stream OpenStream() {
|
|
return OpenStream(VirtualPath);
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Opens a stream for a virtual file
|
|
/// </devdoc>
|
|
protected Stream OpenStream(string virtualPath) {
|
|
return VirtualPathProvider.OpenFile(virtualPath);
|
|
}
|
|
|
|
internal /*protected*/ Stream OpenStream(VirtualPath virtualPath) {
|
|
return virtualPath.OpenFile();
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Opens a reader for the virtual file handled by this provider
|
|
/// </devdoc>
|
|
protected TextReader OpenReader() {
|
|
return OpenReader(VirtualPathObject);
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Opens a reader for a virtual file
|
|
/// </devdoc>
|
|
protected TextReader OpenReader(string virtualPath) {
|
|
return OpenReader(System.Web.VirtualPath.Create(virtualPath));
|
|
}
|
|
|
|
internal /*protected*/ TextReader OpenReader(VirtualPath virtualPath) {
|
|
Stream stream = OpenStream(virtualPath);
|
|
return Util.ReaderFromStream(stream, virtualPath);
|
|
}
|
|
|
|
public static void RegisterBuildProvider(string extension, Type providerType) {
|
|
if (String.IsNullOrEmpty(extension)) {
|
|
throw ExceptionUtil.ParameterNullOrEmpty("extension");
|
|
}
|
|
if (providerType == null) {
|
|
throw new ArgumentNullException("providerType");
|
|
}
|
|
if (!typeof(BuildProvider).IsAssignableFrom(providerType)) {
|
|
//
|
|
throw ExceptionUtil.ParameterInvalid("providerType");
|
|
}
|
|
BuildManager.ThrowIfPreAppStartNotRunning();
|
|
|
|
// Last call wins. If a user wants to use a different provider they can always provide an
|
|
// override in the app's config.
|
|
s_dynamicallyRegisteredProviders[extension] = new CompilationBuildProviderInfo(providerType);
|
|
}
|
|
|
|
internal static BuildProviderInfo GetBuildProviderInfo(System.Web.Configuration.CompilationSection config, string extension) {
|
|
Debug.Assert(extension != null);
|
|
var entry = config.BuildProviders[extension];
|
|
if (entry != null) {
|
|
return entry.BuildProviderInfo;
|
|
}
|
|
|
|
BuildProviderInfo info = null;
|
|
s_dynamicallyRegisteredProviders.TryGetValue(extension, out info);
|
|
return info;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns a collection of assemblies that the build provider will be compiled with.
|
|
/// </devdoc>
|
|
protected ICollection ReferencedAssemblies {
|
|
get { return _referencedAssemblies; }
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns the default CompilerType to be used for a specific language
|
|
/// </devdoc>
|
|
protected CompilerType GetDefaultCompilerTypeForLanguage(string language) {
|
|
return CompilationUtil.GetCompilerInfoFromLanguage(VirtualPathObject, language);
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// Returns the default CompilerType to be used for the default language
|
|
/// </devdoc>
|
|
protected CompilerType GetDefaultCompilerType() {
|
|
return CompilationUtil.GetDefaultLanguageCompilerInfo(null /*compConfig*/, VirtualPathObject);
|
|
}
|
|
|
|
|
|
//
|
|
// Internal code
|
|
//
|
|
|
|
#pragma warning disable 0649
|
|
internal SimpleBitVector32 flags;
|
|
#pragma warning restore 0649
|
|
|
|
// const masks into the BitVector32
|
|
internal const int isDependedOn = 0x00000001;
|
|
internal const int noBuildResult = 0x00000002;
|
|
internal const int ignoreParseErrors = 0x00000004;
|
|
internal const int ignoreControlProperties = 0x00000008;
|
|
internal const int dontThrowOnFirstParseError = 0x00000010;
|
|
internal const int contributedCode = 0x00000020;
|
|
|
|
private VirtualPath _virtualPath;
|
|
|
|
private ICollection _referencedAssemblies;
|
|
|
|
private BuildProviderSet _buildProviderDependencies;
|
|
internal BuildProviderSet BuildProviderDependencies {
|
|
get {
|
|
return _buildProviderDependencies;
|
|
}
|
|
}
|
|
|
|
// Is any other BuildProvider depending on this BuildProvider
|
|
internal bool IsDependedOn { get { return flags[isDependedOn]; } }
|
|
|
|
internal void SetNoBuildResult() {
|
|
flags[noBuildResult] = true;
|
|
}
|
|
|
|
// Remember that this BuildProvider has contributed to the compilation
|
|
internal void SetContributedCode() {
|
|
flags[contributedCode] = true;
|
|
}
|
|
|
|
internal void SetVirtualPath(VirtualPath virtualPath) {
|
|
_virtualPath = virtualPath;
|
|
}
|
|
|
|
internal void SetReferencedAssemblies(ICollection referencedAssemblies) {
|
|
_referencedAssemblies = referencedAssemblies;
|
|
}
|
|
|
|
internal void AddBuildProviderDependency(BuildProvider dependentBuildProvider) {
|
|
if (_buildProviderDependencies == null)
|
|
_buildProviderDependencies = new BuildProviderSet();
|
|
|
|
_buildProviderDependencies.Add(dependentBuildProvider);
|
|
|
|
dependentBuildProvider.flags[isDependedOn] = true;
|
|
}
|
|
|
|
/*
|
|
* Return the culture name for this provider (e.g. "fr" or "fr-fr").
|
|
* If no culture applies, return null.
|
|
*/
|
|
internal string GetCultureName() {
|
|
return Util.GetCultureName(VirtualPath);
|
|
}
|
|
|
|
internal BuildResult GetBuildResult(CompilerResults results) {
|
|
|
|
BuildResult result = CreateBuildResult(results);
|
|
if (result == null)
|
|
return null;
|
|
|
|
result.VirtualPath = VirtualPathObject;
|
|
SetBuildResultDependencies(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
internal virtual BuildResult CreateBuildResult(CompilerResults results) {
|
|
|
|
// If the build provider is not supposed to have a build result, just return
|
|
if (flags[noBuildResult])
|
|
return null;
|
|
|
|
// Access the CompiledAssembly property to make sure the assembly gets loaded
|
|
// Otherwise, the user code in GetGeneratedType() will fail to load it in medium trust since
|
|
// they don't have access to the codegen folder
|
|
if (!BuildManagerHost.InClientBuildManager && results != null) {
|
|
// The ClientBuildManager runs in full trust, so we skip this statement
|
|
// to avoid unnecessary loading of the assembly.
|
|
var assembly = results.CompiledAssembly;
|
|
}
|
|
|
|
// Ask the build provider if it wants to persist a Type
|
|
Type type = GetGeneratedType(results);
|
|
|
|
BuildResult result;
|
|
|
|
if (type != null) {
|
|
// Create a BuildResult for it
|
|
BuildResultCompiledType resultCompiledType = CreateBuildResult(type);
|
|
|
|
if (!resultCompiledType.IsDelayLoadType) {
|
|
// If the returned type doesn't come from the generated assembly, set a flag
|
|
if (results == null || type.Assembly != results.CompiledAssembly)
|
|
resultCompiledType.UsesExistingAssembly = true;
|
|
}
|
|
|
|
result = resultCompiledType;
|
|
}
|
|
else {
|
|
// Ask the build provider if it instead wants to persist a custom string
|
|
string customString = GetCustomString(results);
|
|
|
|
// If it does, persist it
|
|
if (customString != null) {
|
|
// Only preserve an assembly if the BuildProvider has actually contributed code
|
|
// to the compilation.
|
|
result = new BuildResultCustomString(
|
|
flags[contributedCode] ? results.CompiledAssembly : null,
|
|
customString);
|
|
}
|
|
else {
|
|
// Nothing was built: nothing to persist
|
|
if (results == null)
|
|
return null;
|
|
|
|
// Otherwise, just persist the assembly, if any
|
|
result = new BuildResultCompiledAssembly(results.CompiledAssembly);
|
|
}
|
|
}
|
|
|
|
// Ask the provider it it wants to set some flags on the result
|
|
int resultFlags = (int) GetResultFlags(results);
|
|
if (resultFlags != 0) {
|
|
// Make sure only the lower bits are set
|
|
Debug.Assert((resultFlags & 0xFFFF0000) == 0);
|
|
resultFlags &= 0x0000FFFF;
|
|
|
|
result.Flags |= resultFlags;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal virtual BuildResultCompiledType CreateBuildResult(Type t) {
|
|
return new BuildResultCompiledType(t);
|
|
}
|
|
|
|
internal void SetBuildResultDependencies(BuildResult result) {
|
|
|
|
result.AddVirtualPathDependencies(VirtualPathDependencies);
|
|
}
|
|
|
|
|
|
//
|
|
// Helper methods
|
|
//
|
|
|
|
internal static CompilerType GetCompilerTypeFromBuildProvider(
|
|
BuildProvider buildProvider) {
|
|
|
|
HttpContext context = null;
|
|
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure) && (context = HttpContext.Current) != null)
|
|
EtwTrace.Trace(EtwTraceType.ETW_TYPE_PARSE_ENTER, context.WorkerRequest);
|
|
|
|
try {
|
|
CompilerType compilerType = buildProvider.CodeCompilerType;
|
|
if (compilerType != null) {
|
|
CompilationUtil.CheckCompilerOptionsAllowed(compilerType.CompilerParameters.CompilerOptions,
|
|
false /*config*/, null, 0);
|
|
}
|
|
return compilerType;
|
|
}
|
|
finally {
|
|
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure) && context != null)
|
|
EtwTrace.Trace(EtwTraceType.ETW_TYPE_PARSE_LEAVE, context.WorkerRequest);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return a 'friendly' string that identifies the build provider
|
|
//
|
|
internal static string GetDisplayName(BuildProvider buildProvider) {
|
|
|
|
// If it has a VirtualPath, use it
|
|
if (buildProvider.VirtualPath != null) {
|
|
return buildProvider.VirtualPath;
|
|
}
|
|
|
|
// Otherwise, the best we can do is the type name
|
|
return buildProvider.GetType().Name;
|
|
}
|
|
|
|
internal virtual ICollection GetGeneratedTypeNames() {
|
|
return null;
|
|
}
|
|
|
|
#region Methods from InternalBuildProvider
|
|
|
|
// Protected internal methods: IgnoreParseErrors and GetCodeCompileUnit
|
|
|
|
// When this is set, we ignore parse errors and keep on processing the page as
|
|
// well as possible. This is used for the Venus CBM scenario
|
|
internal virtual bool IgnoreParseErrors {
|
|
get { return flags[ignoreParseErrors]; }
|
|
set { flags[ignoreParseErrors] = value; }
|
|
}
|
|
|
|
// This is used in CBM to instruct the control builders to skip control properties
|
|
internal bool IgnoreControlProperties {
|
|
get { return flags[ignoreControlProperties]; }
|
|
set { flags[ignoreControlProperties] = value; }
|
|
}
|
|
|
|
// Indicates whether the parser should continue processing for more errors.
|
|
// This is used in both CBM and aspnet_compiler tool.
|
|
internal bool ThrowOnFirstParseError {
|
|
get { return !flags[dontThrowOnFirstParseError]; }
|
|
set { flags[dontThrowOnFirstParseError] = !value; }
|
|
}
|
|
|
|
// This is used in the CBM scenario only
|
|
internal virtual IAssemblyDependencyParser AssemblyDependencyParser {
|
|
get { return null; }
|
|
}
|
|
|
|
// This is used in the CBM scenario only
|
|
/// <devdoc>
|
|
/// Returns the CodeCompileUnit and sets the associated dictionary containing the line pragmas.
|
|
/// </devdoc>
|
|
protected internal virtual CodeCompileUnit GetCodeCompileUnit(out IDictionary linePragmasTable) {
|
|
|
|
// Default implementation with code at line 1
|
|
|
|
string sourceString = Util.StringFromVirtualPath(VirtualPathObject);
|
|
CodeSnippetCompileUnit snippetCompileUnit = new CodeSnippetCompileUnit(sourceString);
|
|
|
|
LinePragmaCodeInfo codeInfo = new LinePragmaCodeInfo(1 /* startLine */, 1 /* startColumn */, 1 /* startGeneratedColumn */, -1 /* codeLength */, false /* isCodeNuggest */);
|
|
linePragmasTable = new Hashtable();
|
|
linePragmasTable[1] = codeInfo;
|
|
|
|
return snippetCompileUnit;
|
|
}
|
|
|
|
internal virtual ICollection GetCompileWithDependencies() { return null; }
|
|
|
|
#endregion Methods from InternalBuildProvider
|
|
|
|
private class CompilationBuildProviderInfo : BuildProviderInfo {
|
|
|
|
private readonly Type _type;
|
|
|
|
public CompilationBuildProviderInfo(Type type) {
|
|
Debug.Assert(type != null);
|
|
_type = type;
|
|
}
|
|
|
|
internal override Type Type {
|
|
get {
|
|
return _type;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The InternalBuildProvider class is still retained to internally distinguish between
|
|
// buildProviders that actually implement the required methods and those that don't, and
|
|
// also to minimize code change where possible.
|
|
internal abstract class InternalBuildProvider: BuildProvider {
|
|
|
|
}
|
|
|
|
internal abstract class BuildProviderInfo {
|
|
// AppliesTo value from the BuildProviderAppliesToAttribute
|
|
private BuildProviderAppliesTo _appliesTo;
|
|
|
|
internal abstract Type Type { get; }
|
|
|
|
internal BuildProviderAppliesTo AppliesTo {
|
|
get {
|
|
if (_appliesTo != 0)
|
|
return _appliesTo;
|
|
|
|
// Check whether the control builder's class exposes an AppliesTo attribute
|
|
object[] attrs = Type.GetCustomAttributes(
|
|
typeof(BuildProviderAppliesToAttribute), /*inherit*/ true);
|
|
|
|
if ((attrs != null) && (attrs.Length > 0)) {
|
|
Debug.Assert(attrs[0] is BuildProviderAppliesToAttribute);
|
|
_appliesTo = ((BuildProviderAppliesToAttribute)attrs[0]).AppliesTo;
|
|
}
|
|
else {
|
|
// Default to applying to All
|
|
_appliesTo = BuildProviderAppliesTo.All;
|
|
}
|
|
|
|
return _appliesTo;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|