3c1f479b9d
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
1479 lines
45 KiB
C#
1479 lines
45 KiB
C#
//
|
|
// System.Web.Compilation.BuildManager
|
|
//
|
|
// Authors:
|
|
// Chris Toshok (toshok@ximian.com)
|
|
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
|
// Marek Habersack (mhabersack@novell.com)
|
|
//
|
|
// (C) 2006-2009 Novell, Inc (http://www.novell.com)
|
|
//
|
|
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.CodeDom;
|
|
using System.CodeDom.Compiler;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
using System.Web;
|
|
using System.Web.Caching;
|
|
using System.Web.Configuration;
|
|
using System.Web.Hosting;
|
|
using System.Web.Util;
|
|
using System.Runtime.Versioning;
|
|
|
|
namespace System.Web.Compilation
|
|
{
|
|
public sealed class BuildManager
|
|
{
|
|
internal const string FAKE_VIRTUAL_PATH_PREFIX = "/@@MonoFakeVirtualPath@@";
|
|
const string BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX = "@@Build_Manager@@";
|
|
static int BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH = BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX.Length;
|
|
|
|
static readonly object bigCompilationLock = new object ();
|
|
static readonly object virtualPathsToIgnoreLock = new object ();
|
|
static readonly char[] virtualPathsToIgnoreSplitChars = {','};
|
|
|
|
static EventHandlerList events = new EventHandlerList ();
|
|
static object buildManagerRemoveEntryEvent = new object ();
|
|
|
|
static bool hosted;
|
|
static Dictionary <string, bool> virtualPathsToIgnore;
|
|
static bool virtualPathsToIgnoreChecked;
|
|
static bool haveVirtualPathsToIgnore;
|
|
static List <Assembly> AppCode_Assemblies = new List<Assembly>();
|
|
static List <Assembly> TopLevel_Assemblies = new List<Assembly>();
|
|
static Dictionary <Type, CodeDomProvider> codeDomProviders;
|
|
static Dictionary <string, BuildManagerCacheItem> buildCache;
|
|
static List <Assembly> referencedAssemblies;
|
|
static List <Assembly> configReferencedAssemblies;
|
|
static bool getReferencedAssembliesInvoked;
|
|
|
|
static int buildCount;
|
|
static bool is_precompiled;
|
|
static bool allowReferencedAssembliesCaching;
|
|
static List <Assembly> dynamicallyRegisteredAssemblies;
|
|
static bool? batchCompilationEnabled;
|
|
static FrameworkName targetFramework;
|
|
static bool preStartMethodsDone;
|
|
static bool preStartMethodsRunning;
|
|
//static bool updatable; unused
|
|
static Dictionary<string, PreCompilationData> precompiled;
|
|
|
|
// This is here _only_ for the purpose of unit tests!
|
|
internal static bool suppressDebugModeMessages;
|
|
|
|
// See comment for the cacheLock field at top of System.Web.Caching/Cache.cs
|
|
static ReaderWriterLockSlim buildCacheLock;
|
|
static ulong recursionDepth;
|
|
|
|
internal static bool AllowReferencedAssembliesCaching {
|
|
get { return allowReferencedAssembliesCaching; }
|
|
set { allowReferencedAssembliesCaching = value; }
|
|
}
|
|
|
|
internal static bool IsPrecompiled {
|
|
get { return is_precompiled; }
|
|
}
|
|
|
|
internal static event BuildManagerRemoveEntryEventHandler RemoveEntry {
|
|
add { events.AddHandler (buildManagerRemoveEntryEvent, value); }
|
|
remove { events.RemoveHandler (buildManagerRemoveEntryEvent, value); }
|
|
}
|
|
|
|
internal static bool CompilingTopLevelAssemblies {
|
|
get; set;
|
|
}
|
|
|
|
internal static bool PreStartMethodsRunning {
|
|
get { return preStartMethodsRunning; }
|
|
}
|
|
|
|
public static bool? BatchCompilationEnabled {
|
|
get { return batchCompilationEnabled; }
|
|
set {
|
|
if (preStartMethodsDone)
|
|
throw new InvalidOperationException ("This method cannot be called after the application's pre-start initialization stage.");
|
|
batchCompilationEnabled = value;
|
|
}
|
|
}
|
|
|
|
public static FrameworkName TargetFramework {
|
|
get {
|
|
if (targetFramework == null) {
|
|
CompilationSection cs = CompilationConfig;
|
|
string framework;
|
|
if (cs == null)
|
|
framework = null;
|
|
else
|
|
framework = cs.TargetFramework;
|
|
|
|
if (String.IsNullOrEmpty (framework))
|
|
targetFramework = new FrameworkName (".NETFramework,Version=v4.0");
|
|
else
|
|
targetFramework = new FrameworkName (framework);
|
|
}
|
|
|
|
return targetFramework;
|
|
}
|
|
}
|
|
internal static bool BatchMode {
|
|
get {
|
|
if (batchCompilationEnabled != null)
|
|
return (bool)batchCompilationEnabled;
|
|
if (!hosted)
|
|
return false; // Fix for bug #380985
|
|
|
|
CompilationSection cs = CompilationConfig;
|
|
if (cs == null)
|
|
return true;
|
|
|
|
return cs.Batch;
|
|
}
|
|
}
|
|
|
|
// Assemblies built from the App_Code directory
|
|
public static IList CodeAssemblies {
|
|
get { return AppCode_Assemblies; }
|
|
}
|
|
|
|
internal static CompilationSection CompilationConfig {
|
|
get { return WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection; }
|
|
}
|
|
|
|
internal static bool HaveResources {
|
|
get; set;
|
|
}
|
|
|
|
internal static IList TopLevelAssemblies {
|
|
get { return TopLevel_Assemblies; }
|
|
}
|
|
|
|
static BuildManager ()
|
|
{
|
|
hosted = (AppDomain.CurrentDomain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
|
|
buildCache = new Dictionary <string, BuildManagerCacheItem> (RuntimeHelpers.StringEqualityComparer);
|
|
buildCacheLock = new ReaderWriterLockSlim ();
|
|
referencedAssemblies = new List <Assembly> ();
|
|
recursionDepth = 0;
|
|
|
|
string appPath = HttpRuntime.AppDomainAppPath;
|
|
string precomp_name = null;
|
|
is_precompiled = String.IsNullOrEmpty (appPath) ? false : File.Exists ((precomp_name = Path.Combine (appPath, "PrecompiledApp.config")));
|
|
if (is_precompiled)
|
|
is_precompiled = LoadPrecompilationInfo (precomp_name);
|
|
}
|
|
internal static void AssertPreStartMethodsRunning ()
|
|
{
|
|
if (!BuildManager.PreStartMethodsRunning)
|
|
throw new InvalidOperationException ("This method must be called during the application's pre-start initialization stage.");
|
|
}
|
|
// Deal with precompiled sites deployed in a different virtual path
|
|
static void FixVirtualPaths ()
|
|
{
|
|
if (precompiled == null)
|
|
return;
|
|
|
|
string [] parts;
|
|
int skip = -1;
|
|
string appVirtualRoot = VirtualPathUtility.AppendTrailingSlash (HttpRuntime.AppDomainAppVirtualPath);
|
|
foreach (string vpath in precompiled.Keys) {
|
|
parts = vpath.Split ('/');
|
|
for (int i = 0; i < parts.Length; i++) {
|
|
if (String.IsNullOrEmpty (parts [i]))
|
|
continue;
|
|
// The path must be rooted, otherwise PhysicalPath returned
|
|
// below will be relative to the current request path and
|
|
// File.Exists will return a false negative. See bug #546053
|
|
string test_path = appVirtualRoot + String.Join ("/", parts, i, parts.Length - i);
|
|
VirtualPath result = GetAbsoluteVirtualPath (test_path);
|
|
if (result != null && File.Exists (result.PhysicalPath)) {
|
|
skip = i - 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
string app_vpath = HttpRuntime.AppDomainAppVirtualPath;
|
|
if (skip == -1 || (skip == 0 && app_vpath == "/"))
|
|
return;
|
|
|
|
if (!app_vpath.EndsWith ("/"))
|
|
app_vpath = app_vpath + "/";
|
|
Dictionary<string, PreCompilationData> copy = new Dictionary<string, PreCompilationData> (precompiled);
|
|
precompiled.Clear ();
|
|
foreach (KeyValuePair<string,PreCompilationData> entry in copy) {
|
|
parts = entry.Key.Split ('/');
|
|
string new_path;
|
|
if (String.IsNullOrEmpty (parts [0]))
|
|
new_path = app_vpath + String.Join ("/", parts, skip + 1, parts.Length - skip - 1);
|
|
else
|
|
new_path = app_vpath + String.Join ("/", parts, skip, parts.Length - skip);
|
|
entry.Value.VirtualPath = new_path;
|
|
precompiled.Add (new_path, entry.Value);
|
|
}
|
|
}
|
|
|
|
static bool LoadPrecompilationInfo (string precomp_config)
|
|
{
|
|
using (XmlTextReader reader = new XmlTextReader (precomp_config)) {
|
|
reader.MoveToContent ();
|
|
if (reader.Name != "precompiledApp")
|
|
return false;
|
|
|
|
/* unused
|
|
if (reader.HasAttributes)
|
|
while (reader.MoveToNextAttribute ())
|
|
if (reader.Name == "updatable") {
|
|
updatable = (reader.Value == "true");
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
string [] compiled = Directory.GetFiles (HttpRuntime.BinDirectory, "*.compiled");
|
|
foreach (string str in compiled)
|
|
LoadCompiled (str);
|
|
|
|
FixVirtualPaths ();
|
|
return true;
|
|
}
|
|
|
|
static void LoadCompiled (string filename)
|
|
{
|
|
using (XmlTextReader reader = new XmlTextReader (filename)) {
|
|
reader.MoveToContent ();
|
|
if (reader.Name == "preserve" && reader.HasAttributes) {
|
|
reader.MoveToNextAttribute ();
|
|
string val = reader.Value;
|
|
// 1 -> app_code subfolder - add the assembly to CodeAssemblies
|
|
// 2 -> ashx
|
|
// 3 -> ascx, aspx
|
|
// 6 -> app_code - add the assembly to CodeAssemblies
|
|
// 8 -> global.asax
|
|
// 9 -> App_GlobalResources - set the assembly for HttpContext
|
|
if (reader.Name == "resultType" && (val == "2" || val == "3" || val == "8"))
|
|
LoadPageData (reader, true);
|
|
else if (val == "1" || val == "6") {
|
|
PreCompilationData pd = LoadPageData (reader, false);
|
|
CodeAssemblies.Add (Assembly.Load (pd.AssemblyFileName));
|
|
} else if (val == "9") {
|
|
PreCompilationData pd = LoadPageData (reader, false);
|
|
HttpContext.AppGlobalResourcesAssembly = Assembly.Load (pd.AssemblyFileName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class PreCompilationData {
|
|
public string VirtualPath;
|
|
public string AssemblyFileName;
|
|
public string TypeName;
|
|
public Type Type;
|
|
}
|
|
|
|
static PreCompilationData LoadPageData (XmlTextReader reader, bool store)
|
|
{
|
|
PreCompilationData pc_data = new PreCompilationData ();
|
|
|
|
while (reader.MoveToNextAttribute ()) {
|
|
string name = reader.Name;
|
|
if (name == "virtualPath")
|
|
pc_data.VirtualPath = VirtualPathUtility.RemoveTrailingSlash (reader.Value);
|
|
else if (name == "assembly")
|
|
pc_data.AssemblyFileName = reader.Value;
|
|
else if (name == "type")
|
|
pc_data.TypeName = reader.Value;
|
|
}
|
|
if (store) {
|
|
if (precompiled == null)
|
|
precompiled = new Dictionary<string, PreCompilationData> (RuntimeHelpers.StringEqualityComparerCulture);
|
|
precompiled.Add (pc_data.VirtualPath, pc_data);
|
|
}
|
|
return pc_data;
|
|
}
|
|
|
|
static void AddAssembly (Assembly asm, List <Assembly> al)
|
|
{
|
|
if (al.Contains (asm))
|
|
return;
|
|
|
|
al.Add (asm);
|
|
}
|
|
|
|
static void AddPathToIgnore (string vp)
|
|
{
|
|
if (virtualPathsToIgnore == null)
|
|
virtualPathsToIgnore = new Dictionary <string, bool> (RuntimeHelpers.StringEqualityComparerCulture);
|
|
|
|
VirtualPath path = GetAbsoluteVirtualPath (vp);
|
|
string vpAbsolute = path.Absolute;
|
|
if (!virtualPathsToIgnore.ContainsKey (vpAbsolute)) {
|
|
virtualPathsToIgnore.Add (vpAbsolute, true);
|
|
haveVirtualPathsToIgnore = true;
|
|
}
|
|
|
|
string vpRelative = path.AppRelative;
|
|
if (!virtualPathsToIgnore.ContainsKey (vpRelative)) {
|
|
virtualPathsToIgnore.Add (vpRelative, true);
|
|
haveVirtualPathsToIgnore = true;
|
|
}
|
|
|
|
if (!virtualPathsToIgnore.ContainsKey (vp)) {
|
|
virtualPathsToIgnore.Add (vp, true);
|
|
haveVirtualPathsToIgnore = true;
|
|
}
|
|
}
|
|
|
|
internal static void AddToReferencedAssemblies (Assembly asm)
|
|
{
|
|
// should not be used
|
|
}
|
|
|
|
static void AssertVirtualPathExists (VirtualPath virtualPath)
|
|
{
|
|
string realpath;
|
|
bool dothrow = false;
|
|
|
|
if (virtualPath.IsFake) {
|
|
realpath = virtualPath.PhysicalPath;
|
|
if (!File.Exists (realpath) && !Directory.Exists (realpath))
|
|
dothrow = true;
|
|
} else {
|
|
VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
|
|
string vpAbsolute = virtualPath.Absolute;
|
|
|
|
if (!vpp.FileExists (vpAbsolute) && !vpp.DirectoryExists (vpAbsolute))
|
|
dothrow = true;
|
|
}
|
|
|
|
if (dothrow)
|
|
throw new HttpException (404, "The file '" + virtualPath + "' does not exist.", virtualPath.Absolute);
|
|
}
|
|
|
|
static void Build (VirtualPath vp)
|
|
{
|
|
AssertVirtualPathExists (vp);
|
|
|
|
CompilationSection cs = CompilationConfig;
|
|
lock (bigCompilationLock) {
|
|
bool entryExists;
|
|
if (HasCachedItemNoLock (vp.Absolute, out entryExists))
|
|
return;
|
|
|
|
if (recursionDepth == 0)
|
|
referencedAssemblies.Clear ();
|
|
|
|
recursionDepth++;
|
|
try {
|
|
BuildInner (vp, cs != null ? cs.Debug : false);
|
|
if (entryExists && recursionDepth <= 1)
|
|
// We count only update builds - first time a file
|
|
// (or a batch) is built doesn't count.
|
|
buildCount++;
|
|
} finally {
|
|
// See http://support.microsoft.com/kb/319947
|
|
if (buildCount > cs.NumRecompilesBeforeAppRestart)
|
|
HttpRuntime.UnloadAppDomain ();
|
|
recursionDepth--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This method assumes it is being called with the big compilation lock held
|
|
static void BuildInner (VirtualPath vp, bool debug)
|
|
{
|
|
var builder = new BuildManagerDirectoryBuilder (vp);
|
|
bool recursive = recursionDepth > 1;
|
|
List <BuildProviderGroup> builderGroups = builder.Build (IsSingleBuild (vp, recursive));
|
|
if (builderGroups == null)
|
|
return;
|
|
|
|
string vpabsolute = vp.Absolute;
|
|
int buildHash = (vpabsolute.GetHashCode () | (int)DateTime.Now.Ticks) + (int)recursionDepth;
|
|
string assemblyBaseName;
|
|
AssemblyBuilder abuilder;
|
|
CompilerType ct;
|
|
int attempts;
|
|
bool singleBuild, needMainVpBuild;
|
|
CompilationException compilationError;
|
|
|
|
// Each group becomes a separate assembly.
|
|
foreach (BuildProviderGroup group in builderGroups) {
|
|
needMainVpBuild = false;
|
|
compilationError = null;
|
|
assemblyBaseName = null;
|
|
|
|
if (group.Count == 1) {
|
|
if (recursive || !group.Master)
|
|
assemblyBaseName = String.Format ("{0}_{1}.{2:x}.", group.NamePrefix, VirtualPathUtility.GetFileName (group [0].VirtualPath), buildHash);
|
|
singleBuild = true;
|
|
} else
|
|
singleBuild = false;
|
|
|
|
if (assemblyBaseName == null)
|
|
assemblyBaseName = group.NamePrefix + "_";
|
|
|
|
ct = group.CompilerType;
|
|
attempts = 3;
|
|
while (attempts > 0) {
|
|
abuilder = new AssemblyBuilder (vp, CreateDomProvider (ct), assemblyBaseName);
|
|
abuilder.CompilerOptions = ct.CompilerParameters;
|
|
abuilder.AddAssemblyReference (GetReferencedAssemblies () as List <Assembly>);
|
|
try {
|
|
GenerateAssembly (abuilder, group, vp, debug);
|
|
attempts = 0;
|
|
} catch (CompilationException ex) {
|
|
attempts--;
|
|
if (singleBuild)
|
|
throw new HttpException ("Single file build failed.", ex);
|
|
|
|
if (attempts == 0) {
|
|
needMainVpBuild = true;
|
|
compilationError = ex;
|
|
break;
|
|
}
|
|
|
|
CompilerResults results = ex.Results;
|
|
if (results == null)
|
|
throw new HttpException ("No results returned from failed compilation.", ex);
|
|
else
|
|
RemoveFailedAssemblies (vpabsolute, ex, abuilder, group, results, debug);
|
|
}
|
|
}
|
|
|
|
if (needMainVpBuild) {
|
|
// One last attempt - try to build just the requested path
|
|
// if it's not built yet or just return without throwing the
|
|
// exception if it has already been built.
|
|
if (HasCachedItemNoLock (vpabsolute)) {
|
|
if (debug)
|
|
DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
|
|
compilationError, vpabsolute);
|
|
return;
|
|
};
|
|
|
|
// This will trigger a recursive build of the requested vp,
|
|
// which means only the vp alone will be built (or not);
|
|
Build (vp);
|
|
if (HasCachedItemNoLock (vpabsolute)) {
|
|
if (debug)
|
|
DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
|
|
compilationError, vpabsolute);
|
|
return;
|
|
}
|
|
|
|
// In theory this code is unreachable. If the recursive
|
|
// build of the main vp failed, then it should have thrown
|
|
// the build exception.
|
|
throw new HttpException ("Requested virtual path build failed.", compilationError);
|
|
}
|
|
}
|
|
}
|
|
|
|
static CodeDomProvider CreateDomProvider (CompilerType ct)
|
|
{
|
|
if (codeDomProviders == null)
|
|
codeDomProviders = new Dictionary <Type, CodeDomProvider> ();
|
|
|
|
Type type = ct.CodeDomProviderType;
|
|
if (type == null) {
|
|
CompilationSection cs = CompilationConfig;
|
|
CompilerType tmp = GetDefaultCompilerTypeForLanguage (cs.DefaultLanguage, cs);
|
|
if (tmp != null)
|
|
type = tmp.CodeDomProviderType;
|
|
}
|
|
|
|
if (type == null)
|
|
return null;
|
|
|
|
CodeDomProvider ret;
|
|
if (codeDomProviders.TryGetValue (type, out ret))
|
|
return ret;
|
|
|
|
ret = Activator.CreateInstance (type) as CodeDomProvider;
|
|
if (ret == null)
|
|
return null;
|
|
|
|
codeDomProviders.Add (type, ret);
|
|
return ret;
|
|
}
|
|
internal static void CallPreStartMethods ()
|
|
{
|
|
if (preStartMethodsDone)
|
|
return;
|
|
|
|
preStartMethodsRunning = true;
|
|
MethodInfo mi = null;
|
|
try {
|
|
List <MethodInfo> methods = LoadPreStartMethodsFromAssemblies (GetReferencedAssemblies () as List <Assembly>);
|
|
if (methods == null || methods.Count == 0)
|
|
return;
|
|
|
|
foreach (MethodInfo m in methods) {
|
|
mi = m;
|
|
m.Invoke (null, null);
|
|
}
|
|
} catch (Exception ex) {
|
|
throw new HttpException (
|
|
String.Format ("The pre-application start initialization method {0} on type {1} threw an exception with the following error message: {2}",
|
|
mi != null ? mi.Name : "UNKNOWN",
|
|
mi != null ? mi.DeclaringType.FullName : "UNKNOWN",
|
|
ex.Message),
|
|
ex
|
|
);
|
|
} finally {
|
|
preStartMethodsRunning = false;
|
|
preStartMethodsDone = true;
|
|
}
|
|
}
|
|
|
|
static List <MethodInfo> LoadPreStartMethodsFromAssemblies (List <Assembly> assemblies)
|
|
{
|
|
if (assemblies == null || assemblies.Count == 0)
|
|
return null;
|
|
|
|
var ret = new List <MethodInfo> ();
|
|
object[] attributes;
|
|
Type type;
|
|
PreApplicationStartMethodAttribute attr;
|
|
|
|
foreach (Assembly asm in assemblies) {
|
|
try {
|
|
attributes = asm.GetCustomAttributes (typeof (PreApplicationStartMethodAttribute), false);
|
|
if (attributes == null || attributes.Length == 0)
|
|
continue;
|
|
|
|
attr = attributes [0] as PreApplicationStartMethodAttribute;
|
|
type = attr.Type;
|
|
if (type == null)
|
|
continue;
|
|
} catch {
|
|
continue;
|
|
}
|
|
|
|
MethodInfo mi;
|
|
Exception error = null;
|
|
try {
|
|
if (type.IsPublic)
|
|
mi = type.GetMethod (attr.MethodName, BindingFlags.Static | BindingFlags.Public, null, new Type[] {}, null);
|
|
else
|
|
mi = null;
|
|
} catch (Exception ex) {
|
|
error = ex;
|
|
mi = null;
|
|
}
|
|
|
|
if (mi == null)
|
|
throw new HttpException (
|
|
String.Format (
|
|
"The method specified by the PreApplicationStartMethodAttribute on assembly '{0}' cannot be resolved. Type: '{1}', MethodName: '{2}'. Verify that the type is public and the method is public and static (Shared in Visual Basic).",
|
|
asm.FullName,
|
|
type.FullName,
|
|
attr.MethodName),
|
|
error
|
|
);
|
|
|
|
ret.Add (mi);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
public static Type GetGlobalAsaxType ()
|
|
{
|
|
Type ret = HttpApplicationFactory.AppType;
|
|
if (ret == null)
|
|
return typeof (HttpApplication);
|
|
|
|
return ret;
|
|
}
|
|
|
|
public static Stream CreateCachedFile (string fileName)
|
|
{
|
|
if (fileName != null && (fileName == String.Empty || fileName.IndexOf (Path.DirectorySeparatorChar) != -1))
|
|
throw new ArgumentException ("Value does not fall within the expected range.");
|
|
|
|
string path = Path.Combine (HttpRuntime.CodegenDir, fileName);
|
|
return new FileStream (path, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
|
|
}
|
|
|
|
public static Stream ReadCachedFile (string fileName)
|
|
{
|
|
if (fileName != null && (fileName == String.Empty || fileName.IndexOf (Path.DirectorySeparatorChar) != -1))
|
|
throw new ArgumentException ("Value does not fall within the expected range.");
|
|
|
|
string path = Path.Combine (HttpRuntime.CodegenDir, fileName);
|
|
if (!File.Exists (path))
|
|
return null;
|
|
|
|
return new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.None);
|
|
}
|
|
|
|
[MonoDocumentationNote ("Fully implemented but no info on application pre-init stage is available yet.")]
|
|
public static void AddReferencedAssembly (Assembly assembly)
|
|
{
|
|
if (assembly == null)
|
|
throw new ArgumentNullException ("assembly");
|
|
|
|
if (preStartMethodsDone)
|
|
throw new InvalidOperationException ("This method cannot be called after the application's pre-start initialization stage.");
|
|
|
|
if (dynamicallyRegisteredAssemblies == null)
|
|
dynamicallyRegisteredAssemblies = new List <Assembly> ();
|
|
|
|
if (!dynamicallyRegisteredAssemblies.Contains (assembly))
|
|
dynamicallyRegisteredAssemblies.Add (assembly);
|
|
}
|
|
|
|
[MonoDocumentationNote ("Not used by Mono internally. Needed for MVC3")]
|
|
public static IWebObjectFactory GetObjectFactory (string virtualPath, bool throwIfNotFound)
|
|
{
|
|
if (CompilingTopLevelAssemblies)
|
|
throw new HttpException ("Method must not be called while compiling the top level assemblies.");
|
|
|
|
Type type;
|
|
if (is_precompiled) {
|
|
type = GetPrecompiledType (virtualPath);
|
|
if (type == null) {
|
|
if (throwIfNotFound)
|
|
throw new HttpException (String.Format ("Virtual path '{0}' not found in precompiled application type cache.", virtualPath));
|
|
else
|
|
return null;
|
|
}
|
|
return new SimpleWebObjectFactory (type);
|
|
}
|
|
|
|
Exception compileException = null;
|
|
try {
|
|
type = GetCompiledType (virtualPath);
|
|
} catch (Exception ex) {
|
|
compileException = ex;
|
|
type = null;
|
|
}
|
|
|
|
if (type == null) {
|
|
if (throwIfNotFound)
|
|
throw new HttpException (String.Format ("Virtual path '{0}' does not exist.", virtualPath), compileException);
|
|
return null;
|
|
}
|
|
|
|
return new SimpleWebObjectFactory (type);
|
|
}
|
|
public static object CreateInstanceFromVirtualPath (string virtualPath, Type requiredBaseType)
|
|
{
|
|
return CreateInstanceFromVirtualPath (GetAbsoluteVirtualPath (virtualPath), requiredBaseType);
|
|
}
|
|
|
|
internal static object CreateInstanceFromVirtualPath (VirtualPath virtualPath, Type requiredBaseType)
|
|
{
|
|
if (requiredBaseType == null)
|
|
throw new NullReferenceException (); // This is what MS does, but
|
|
// from somewhere else.
|
|
|
|
Type type = GetCompiledType (virtualPath);
|
|
if (type == null)
|
|
return null;
|
|
|
|
if (!requiredBaseType.IsAssignableFrom (type))
|
|
throw new HttpException (500,
|
|
String.Format ("Type '{0}' does not inherit from '{1}'.", type.FullName, requiredBaseType.FullName));
|
|
|
|
return Activator.CreateInstance (type, null);
|
|
}
|
|
|
|
static void DescribeCompilationError (string format, CompilationException ex, params object[] parms)
|
|
{
|
|
StringBuilder sb = new StringBuilder ();
|
|
string newline = Environment.NewLine;
|
|
|
|
if (parms != null)
|
|
sb.AppendFormat (format + newline, parms);
|
|
else
|
|
sb.Append (format + newline);
|
|
|
|
CompilerResults results = ex != null ? ex.Results : null;
|
|
if (results == null)
|
|
sb.Append ("No compiler error information present." + newline);
|
|
else {
|
|
sb.Append ("Compiler errors:" + newline);
|
|
foreach (CompilerError error in results.Errors)
|
|
sb.Append (" " + error.ToString () + newline);
|
|
}
|
|
|
|
if (ex != null) {
|
|
sb.Append (newline + "Exception thrown:" + newline);
|
|
sb.Append (ex.ToString ());
|
|
}
|
|
|
|
ShowDebugModeMessage (sb.ToString ());
|
|
}
|
|
|
|
static BuildProvider FindBuildProviderForPhysicalPath (string path, BuildProviderGroup group, HttpRequest req)
|
|
{
|
|
if (req == null || String.IsNullOrEmpty (path))
|
|
return null;
|
|
|
|
foreach (BuildProvider bp in group) {
|
|
if (String.Compare (path, req.MapPath (bp.VirtualPath), RuntimeHelpers.StringComparison) == 0)
|
|
return bp;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static void GenerateAssembly (AssemblyBuilder abuilder, BuildProviderGroup group, VirtualPath vp, bool debug)
|
|
{
|
|
IDictionary <string, bool> deps;
|
|
BuildManagerCacheItem bmci;
|
|
string bvp, vpabsolute = vp.Absolute;
|
|
StringBuilder sb;
|
|
string newline;
|
|
int failedCount = 0;
|
|
|
|
if (debug) {
|
|
newline = Environment.NewLine;
|
|
sb = new StringBuilder ("Code generation for certain virtual paths in a batch failed. Those files have been removed from the batch." + newline);
|
|
sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
|
|
} else {
|
|
newline = null;
|
|
sb = null;
|
|
}
|
|
|
|
List <BuildProvider> failedBuildProviders = null;
|
|
StringComparison stringComparison = RuntimeHelpers.StringComparison;
|
|
foreach (BuildProvider bp in group) {
|
|
bvp = bp.VirtualPath;
|
|
if (HasCachedItemNoLock (bvp))
|
|
continue;
|
|
|
|
try {
|
|
bp.GenerateCode (abuilder);
|
|
} catch (Exception ex) {
|
|
if (String.Compare (bvp, vpabsolute, stringComparison) == 0) {
|
|
if (ex is CompilationException || ex is ParseException)
|
|
throw;
|
|
|
|
throw new HttpException ("Code generation failed.", ex);
|
|
}
|
|
|
|
if (failedBuildProviders == null)
|
|
failedBuildProviders = new List <BuildProvider> ();
|
|
failedBuildProviders.Add (bp);
|
|
failedCount++;
|
|
if (sb != null) {
|
|
if (failedCount > 1)
|
|
sb.Append (newline);
|
|
|
|
sb.AppendFormat ("Failed file virtual path: {0}; Exception: {1}{2}{1}", bp.VirtualPath, newline, ex);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
deps = bp.ExtractDependencies ();
|
|
if (deps != null) {
|
|
foreach (var dep in deps) {
|
|
bmci = GetCachedItemNoLock (dep.Key);
|
|
if (bmci == null || bmci.BuiltAssembly == null)
|
|
continue;
|
|
abuilder.AddAssemblyReference (bmci.BuiltAssembly);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sb != null && failedCount > 0)
|
|
ShowDebugModeMessage (sb.ToString ());
|
|
|
|
if (failedBuildProviders != null) {
|
|
foreach (BuildProvider bp in failedBuildProviders)
|
|
group.Remove (bp);
|
|
}
|
|
|
|
foreach (Assembly asm in referencedAssemblies) {
|
|
if (asm == null)
|
|
continue;
|
|
|
|
abuilder.AddAssemblyReference (asm);
|
|
}
|
|
|
|
CompilerResults results = abuilder.BuildAssembly (vp);
|
|
|
|
// No results is not an error - it is possible that the assembly builder contained only .asmx and
|
|
// .ashx files which had no body, just the directive. In such case, no code unit or code file is added
|
|
// to the assembly builder and, in effect, no assembly is produced but there are STILL types that need
|
|
// to be added to the cache.
|
|
Assembly compiledAssembly = results != null ? results.CompiledAssembly : null;
|
|
try {
|
|
buildCacheLock.EnterWriteLock ();
|
|
if (compiledAssembly != null)
|
|
referencedAssemblies.Add (compiledAssembly);
|
|
|
|
foreach (BuildProvider bp in group) {
|
|
if (HasCachedItemNoLock (bp.VirtualPath))
|
|
continue;
|
|
|
|
StoreInCache (bp, compiledAssembly, results);
|
|
}
|
|
} finally {
|
|
buildCacheLock.ExitWriteLock ();
|
|
}
|
|
}
|
|
|
|
static VirtualPath GetAbsoluteVirtualPath (string virtualPath)
|
|
{
|
|
string vp;
|
|
|
|
if (!VirtualPathUtility.IsRooted (virtualPath)) {
|
|
HttpContext ctx = HttpContext.Current;
|
|
HttpRequest req = ctx != null ? ctx.Request : null;
|
|
|
|
if (req != null) {
|
|
string fileDir = req.FilePath;
|
|
if (!String.IsNullOrEmpty (fileDir) && String.Compare (fileDir, "/", StringComparison.Ordinal) != 0)
|
|
fileDir = VirtualPathUtility.GetDirectory (fileDir);
|
|
else
|
|
fileDir = "/";
|
|
|
|
vp = VirtualPathUtility.Combine (fileDir, virtualPath);
|
|
} else
|
|
throw new HttpException ("No context, cannot map paths.");
|
|
} else
|
|
vp = virtualPath;
|
|
|
|
return new VirtualPath (vp);
|
|
}
|
|
|
|
[MonoTODO ("Not implemented, always returns null")]
|
|
public static BuildDependencySet GetCachedBuildDependencySet (HttpContext context, string virtualPath)
|
|
{
|
|
return null; // null is ok here until we store the dependency set in the Cache.
|
|
}
|
|
[MonoTODO ("Not implemented, always returns null")]
|
|
public static BuildDependencySet GetCachedBuildDependencySet (HttpContext context, string virtualPath, bool ensureIsUpToDate)
|
|
{
|
|
return null; // null is ok here until we store the dependency set in the Cache.
|
|
}
|
|
static BuildManagerCacheItem GetCachedItem (string vp)
|
|
{
|
|
try {
|
|
buildCacheLock.EnterReadLock ();
|
|
return GetCachedItemNoLock (vp);
|
|
} finally {
|
|
buildCacheLock.ExitReadLock ();
|
|
}
|
|
}
|
|
|
|
static BuildManagerCacheItem GetCachedItemNoLock (string vp)
|
|
{
|
|
BuildManagerCacheItem ret;
|
|
if (buildCache.TryGetValue (vp, out ret))
|
|
return ret;
|
|
|
|
return null;
|
|
}
|
|
|
|
internal static Type GetCodeDomProviderType (BuildProvider provider)
|
|
{
|
|
CompilerType codeCompilerType;
|
|
Type codeDomProviderType = null;
|
|
|
|
codeCompilerType = provider.CodeCompilerType;
|
|
if (codeCompilerType != null)
|
|
codeDomProviderType = codeCompilerType.CodeDomProviderType;
|
|
|
|
if (codeDomProviderType == null)
|
|
throw new HttpException (String.Concat ("Provider '", provider, " 'fails to specify the compiler type."));
|
|
|
|
return codeDomProviderType;
|
|
}
|
|
|
|
static Type GetPrecompiledType (string virtualPath)
|
|
{
|
|
if (precompiled == null || precompiled.Count == 0)
|
|
return null;
|
|
|
|
PreCompilationData pc_data;
|
|
var vp = new VirtualPath (virtualPath);
|
|
if (!precompiled.TryGetValue (vp.Absolute, out pc_data))
|
|
if (!precompiled.TryGetValue (virtualPath, out pc_data))
|
|
return null;
|
|
|
|
if (pc_data.Type == null)
|
|
pc_data.Type = Type.GetType (pc_data.TypeName + ", " + pc_data.AssemblyFileName, true);
|
|
return pc_data.Type;
|
|
}
|
|
|
|
internal static Type GetPrecompiledApplicationType ()
|
|
{
|
|
if (!is_precompiled)
|
|
return null;
|
|
|
|
string appVp = VirtualPathUtility.AppendTrailingSlash (HttpRuntime.AppDomainAppVirtualPath);
|
|
Type apptype = GetPrecompiledType (VirtualPathUtility.Combine (appVp, "global.asax"));
|
|
if (apptype == null)
|
|
apptype = GetPrecompiledType (VirtualPathUtility.Combine (appVp, "Global.asax"));
|
|
return apptype;
|
|
}
|
|
|
|
public static Assembly GetCompiledAssembly (string virtualPath)
|
|
{
|
|
return GetCompiledAssembly (GetAbsoluteVirtualPath (virtualPath));
|
|
}
|
|
|
|
internal static Assembly GetCompiledAssembly (VirtualPath virtualPath)
|
|
{
|
|
string vpabsolute = virtualPath.Absolute;
|
|
if (is_precompiled) {
|
|
Type type = GetPrecompiledType (vpabsolute);
|
|
if (type != null)
|
|
return type.Assembly;
|
|
}
|
|
BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
|
|
if (bmci != null)
|
|
return bmci.BuiltAssembly;
|
|
|
|
Build (virtualPath);
|
|
bmci = GetCachedItem (vpabsolute);
|
|
if (bmci != null)
|
|
return bmci.BuiltAssembly;
|
|
|
|
return null;
|
|
}
|
|
|
|
public static Type GetCompiledType (string virtualPath)
|
|
{
|
|
return GetCompiledType (GetAbsoluteVirtualPath (virtualPath));
|
|
}
|
|
|
|
internal static Type GetCompiledType (VirtualPath virtualPath)
|
|
{
|
|
string vpabsolute = virtualPath.Absolute;
|
|
if (is_precompiled) {
|
|
Type type = GetPrecompiledType (vpabsolute);
|
|
if (type != null)
|
|
return type;
|
|
}
|
|
BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
|
|
if (bmci != null) {
|
|
ReferenceAssemblyInCompilation (bmci);
|
|
return bmci.Type;
|
|
}
|
|
|
|
Build (virtualPath);
|
|
bmci = GetCachedItem (vpabsolute);
|
|
if (bmci != null) {
|
|
ReferenceAssemblyInCompilation (bmci);
|
|
return bmci.Type;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static string GetCompiledCustomString (string virtualPath)
|
|
{
|
|
return GetCompiledCustomString (GetAbsoluteVirtualPath (virtualPath));
|
|
}
|
|
|
|
internal static string GetCompiledCustomString (VirtualPath virtualPath)
|
|
{
|
|
string vpabsolute = virtualPath.Absolute;
|
|
BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
|
|
if (bmci != null)
|
|
return bmci.CompiledCustomString;
|
|
|
|
Build (virtualPath);
|
|
bmci = GetCachedItem (vpabsolute);
|
|
if (bmci != null)
|
|
return bmci.CompiledCustomString;
|
|
|
|
return null;
|
|
}
|
|
|
|
internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection)
|
|
{
|
|
return GetDefaultCompilerTypeForLanguage (language, configSection, true);
|
|
}
|
|
|
|
internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection, bool throwOnMissing)
|
|
{
|
|
// MS throws when accesing a Hashtable, we do here.
|
|
if (language == null || language.Length == 0)
|
|
throw new ArgumentNullException ("language");
|
|
|
|
CompilationSection config;
|
|
if (configSection == null)
|
|
config = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
|
|
else
|
|
config = configSection;
|
|
|
|
Compiler compiler = config.Compilers.Get (language);
|
|
CompilerParameters p;
|
|
Type type;
|
|
|
|
if (compiler != null) {
|
|
type = HttpApplication.LoadType (compiler.Type, true);
|
|
p = new CompilerParameters ();
|
|
p.CompilerOptions = compiler.CompilerOptions;
|
|
p.WarningLevel = compiler.WarningLevel;
|
|
SetCommonParameters (config, p, type, language);
|
|
return new CompilerType (type, p);
|
|
}
|
|
|
|
if (CodeDomProvider.IsDefinedLanguage (language)) {
|
|
CompilerInfo info = CodeDomProvider.GetCompilerInfo (language);
|
|
p = info.CreateDefaultCompilerParameters ();
|
|
type = info.CodeDomProviderType;
|
|
SetCommonParameters (config, p, type, language);
|
|
return new CompilerType (type, p);
|
|
}
|
|
|
|
if (throwOnMissing)
|
|
throw new HttpException (String.Concat ("No compiler for language '", language, "'."));
|
|
|
|
return null;
|
|
}
|
|
|
|
public static ICollection GetReferencedAssemblies ()
|
|
{
|
|
if (getReferencedAssembliesInvoked)
|
|
return configReferencedAssemblies;
|
|
|
|
if (allowReferencedAssembliesCaching)
|
|
getReferencedAssembliesInvoked = true;
|
|
|
|
if (configReferencedAssemblies == null)
|
|
configReferencedAssemblies = new List <Assembly> ();
|
|
else if (getReferencedAssembliesInvoked)
|
|
configReferencedAssemblies.Clear ();
|
|
|
|
CompilationSection compConfig = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
|
|
if (compConfig == null)
|
|
return configReferencedAssemblies;
|
|
|
|
bool addAssembliesInBin = false;
|
|
foreach (AssemblyInfo info in compConfig.Assemblies) {
|
|
if (info.Assembly == "*")
|
|
addAssembliesInBin = is_precompiled ? false : true;
|
|
else
|
|
LoadAssembly (info, configReferencedAssemblies);
|
|
}
|
|
|
|
foreach (Assembly topLevelAssembly in TopLevelAssemblies)
|
|
configReferencedAssemblies.Add (topLevelAssembly);
|
|
|
|
foreach (string assLocation in WebConfigurationManager.ExtraAssemblies)
|
|
LoadAssembly (assLocation, configReferencedAssemblies);
|
|
if (dynamicallyRegisteredAssemblies != null)
|
|
foreach (Assembly registeredAssembly in dynamicallyRegisteredAssemblies)
|
|
configReferencedAssemblies.Add (registeredAssembly);
|
|
// Precompiled sites unconditionally load all assemblies from bin/ (fix for
|
|
// bug #502016)
|
|
if (is_precompiled || addAssembliesInBin) {
|
|
foreach (string s in HttpApplication.BinDirectoryAssemblies) {
|
|
try {
|
|
LoadAssembly (s, configReferencedAssemblies);
|
|
} catch (BadImageFormatException) {
|
|
// ignore silently
|
|
}
|
|
}
|
|
}
|
|
|
|
return configReferencedAssemblies;
|
|
}
|
|
|
|
// The 2 GetType() overloads work on the global.asax, App_GlobalResources, App_WebReferences or App_Browsers
|
|
public static Type GetType (string typeName, bool throwOnError)
|
|
{
|
|
return GetType (typeName, throwOnError, false);
|
|
}
|
|
|
|
public static Type GetType (string typeName, bool throwOnError, bool ignoreCase)
|
|
{
|
|
if (String.IsNullOrEmpty (typeName))
|
|
throw new HttpException ("Type name must not be empty.");
|
|
|
|
Type ret = null;
|
|
Exception ex = null;
|
|
try {
|
|
string wantedAsmName;
|
|
string wantedTypeName;
|
|
int comma = typeName.IndexOf (',');
|
|
|
|
if (comma > 0 && comma < typeName.Length - 1) {
|
|
var aname = new AssemblyName (typeName.Substring (comma + 1));
|
|
wantedAsmName = aname.ToString ();
|
|
wantedTypeName = typeName.Substring (0, comma);
|
|
} else {
|
|
wantedAsmName = null;
|
|
wantedTypeName = typeName;
|
|
}
|
|
|
|
var assemblies = new List <Assembly> ();
|
|
assemblies.AddRange (BuildManager.GetReferencedAssemblies () as List <Assembly>);
|
|
assemblies.AddRange (TopLevel_Assemblies);
|
|
Type appType = HttpApplicationFactory.AppType;
|
|
if (appType != null)
|
|
assemblies.Add (appType.Assembly);
|
|
|
|
foreach (Assembly asm in assemblies) {
|
|
if (asm == null)
|
|
continue;
|
|
|
|
if (wantedAsmName != null) {
|
|
// So dumb...
|
|
if (String.Compare (wantedAsmName, asm.GetName ().ToString (), StringComparison.Ordinal) == 0) {
|
|
ret = asm.GetType (wantedTypeName, throwOnError, ignoreCase);
|
|
if (ret != null)
|
|
return ret;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
ret = asm.GetType (wantedTypeName, false, ignoreCase);
|
|
if (ret != null)
|
|
return ret;
|
|
}
|
|
} catch (Exception e) {
|
|
ex = e;
|
|
}
|
|
|
|
if (throwOnError)
|
|
throw new HttpException ("Failed to find the specified type.", ex);
|
|
|
|
return null;
|
|
}
|
|
|
|
public static ICollection GetVirtualPathDependencies (string virtualPath)
|
|
{
|
|
return GetVirtualPathDependencies (virtualPath, null);
|
|
}
|
|
|
|
internal static ICollection GetVirtualPathDependencies (string virtualPath, BuildProvider bprovider)
|
|
{
|
|
BuildProvider provider = bprovider;
|
|
if (provider == null) {
|
|
CompilationSection cs = CompilationConfig;
|
|
if (cs == null)
|
|
return null;
|
|
provider = BuildManagerDirectoryBuilder.GetBuildProvider (virtualPath, cs.BuildProviders);
|
|
}
|
|
|
|
if (provider == null)
|
|
return null;
|
|
|
|
IDictionary <string, bool> deps = provider.ExtractDependencies ();
|
|
if (deps == null)
|
|
return null;
|
|
|
|
return (ICollection)deps.Keys;
|
|
}
|
|
|
|
internal static bool HasCachedItemNoLock (string vp, out bool entryExists)
|
|
{
|
|
BuildManagerCacheItem item;
|
|
|
|
if (buildCache.TryGetValue (vp, out item)) {
|
|
entryExists = true;
|
|
return item != null;
|
|
}
|
|
|
|
entryExists = false;
|
|
return false;
|
|
}
|
|
|
|
internal static bool HasCachedItemNoLock (string vp)
|
|
{
|
|
bool dummy;
|
|
return HasCachedItemNoLock (vp, out dummy);
|
|
}
|
|
|
|
internal static bool IgnoreVirtualPath (string virtualPath)
|
|
{
|
|
if (!virtualPathsToIgnoreChecked) {
|
|
lock (virtualPathsToIgnoreLock) {
|
|
if (!virtualPathsToIgnoreChecked)
|
|
LoadVirtualPathsToIgnore ();
|
|
virtualPathsToIgnoreChecked = true;
|
|
}
|
|
}
|
|
|
|
if (!haveVirtualPathsToIgnore)
|
|
return false;
|
|
|
|
if (virtualPathsToIgnore.ContainsKey (virtualPath))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool IsSingleBuild (VirtualPath vp, bool recursive)
|
|
{
|
|
if (String.Compare (vp.AppRelative, "~/global.asax", StringComparison.OrdinalIgnoreCase) == 0)
|
|
return true;
|
|
|
|
if (!BatchMode)
|
|
return true;
|
|
|
|
return recursive;
|
|
}
|
|
|
|
static void LoadAssembly (string path, List <Assembly> al)
|
|
{
|
|
AddAssembly (Assembly.LoadFrom (path), al);
|
|
}
|
|
|
|
static void LoadAssembly (AssemblyInfo info, List <Assembly> al)
|
|
{
|
|
AddAssembly (Assembly.Load (info.Assembly), al);
|
|
}
|
|
|
|
static void LoadVirtualPathsToIgnore ()
|
|
{
|
|
NameValueCollection appSettings = WebConfigurationManager.AppSettings;
|
|
if (appSettings == null)
|
|
return;
|
|
|
|
string pathsFromConfig = appSettings ["MonoAspnetBatchCompileIgnorePaths"];
|
|
string pathsFromFile = appSettings ["MonoAspnetBatchCompileIgnoreFromFile"];
|
|
|
|
if (!String.IsNullOrEmpty (pathsFromConfig)) {
|
|
string[] paths = pathsFromConfig.Split (virtualPathsToIgnoreSplitChars);
|
|
string path;
|
|
|
|
foreach (string p in paths) {
|
|
path = p.Trim ();
|
|
if (path.Length == 0)
|
|
continue;
|
|
|
|
AddPathToIgnore (path);
|
|
}
|
|
}
|
|
|
|
if (!String.IsNullOrEmpty (pathsFromFile)) {
|
|
string realpath;
|
|
HttpContext ctx = HttpContext.Current;
|
|
HttpRequest req = ctx != null ? ctx.Request : null;
|
|
|
|
if (req == null)
|
|
throw new HttpException ("Missing context, cannot continue.");
|
|
|
|
realpath = req.MapPath (pathsFromFile);
|
|
if (!File.Exists (realpath))
|
|
return;
|
|
|
|
string[] paths = File.ReadAllLines (realpath);
|
|
if (paths == null || paths.Length == 0)
|
|
return;
|
|
|
|
string path;
|
|
foreach (string p in paths) {
|
|
path = p.Trim ();
|
|
if (path.Length == 0)
|
|
continue;
|
|
|
|
AddPathToIgnore (path);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void OnEntryRemoved (string vp)
|
|
{
|
|
BuildManagerRemoveEntryEventHandler eh = events [buildManagerRemoveEntryEvent] as BuildManagerRemoveEntryEventHandler;
|
|
|
|
if (eh != null)
|
|
eh (new BuildManagerRemoveEntryEventArgs (vp, HttpContext.Current));
|
|
}
|
|
|
|
static void OnVirtualPathChanged (string key, object value, CacheItemRemovedReason removedReason)
|
|
{
|
|
string virtualPath;
|
|
|
|
if (StrUtils.StartsWith (key, BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX))
|
|
virtualPath = key.Substring (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH);
|
|
else
|
|
return;
|
|
|
|
try {
|
|
buildCacheLock.EnterWriteLock ();
|
|
if (HasCachedItemNoLock (virtualPath)) {
|
|
buildCache [virtualPath] = null;
|
|
OnEntryRemoved (virtualPath);
|
|
}
|
|
} finally {
|
|
buildCacheLock.ExitWriteLock ();
|
|
}
|
|
}
|
|
|
|
static void ReferenceAssemblyInCompilation (BuildManagerCacheItem bmci)
|
|
{
|
|
if (recursionDepth == 0 || referencedAssemblies.Contains (bmci.BuiltAssembly))
|
|
return;
|
|
|
|
referencedAssemblies.Add (bmci.BuiltAssembly);
|
|
}
|
|
|
|
static void RemoveFailedAssemblies (string requestedVirtualPath, CompilationException ex, AssemblyBuilder abuilder,
|
|
BuildProviderGroup group, CompilerResults results, bool debug)
|
|
{
|
|
StringBuilder sb;
|
|
string newline;
|
|
|
|
if (debug) {
|
|
newline = Environment.NewLine;
|
|
sb = new StringBuilder ("Compilation of certain files in a batch failed. Another attempt to compile the batch will be made." + newline);
|
|
sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
|
|
} else {
|
|
newline = null;
|
|
sb = null;
|
|
}
|
|
|
|
var failedBuildProviders = new List <BuildProvider> ();
|
|
BuildProvider bp;
|
|
HttpContext ctx = HttpContext.Current;
|
|
HttpRequest req = ctx != null ? ctx.Request : null;
|
|
bool rethrow = false;
|
|
|
|
foreach (CompilerError error in results.Errors) {
|
|
if (error.IsWarning)
|
|
continue;
|
|
|
|
bp = abuilder.GetBuildProviderForPhysicalFilePath (error.FileName);
|
|
if (bp == null) {
|
|
bp = FindBuildProviderForPhysicalPath (error.FileName, group, req);
|
|
if (bp == null)
|
|
continue;
|
|
}
|
|
|
|
if (String.Compare (bp.VirtualPath, requestedVirtualPath, StringComparison.Ordinal) == 0)
|
|
rethrow = true;
|
|
|
|
if (!failedBuildProviders.Contains (bp)) {
|
|
failedBuildProviders.Add (bp);
|
|
if (sb != null)
|
|
sb.AppendFormat ("\t{0}{1}", bp.VirtualPath, newline);
|
|
}
|
|
|
|
if (sb != null)
|
|
sb.AppendFormat ("\t\t{0}{1}", error, newline);
|
|
}
|
|
|
|
foreach (BuildProvider fbp in failedBuildProviders)
|
|
group.Remove (fbp);
|
|
|
|
if (sb != null) {
|
|
sb.AppendFormat ("{0}The following exception has been thrown for the file(s) listed above:{0}{1}",
|
|
newline, ex.ToString ());
|
|
ShowDebugModeMessage (sb.ToString ());
|
|
sb = null;
|
|
}
|
|
|
|
if (rethrow)
|
|
throw new HttpException ("Compilation failed.", ex);
|
|
}
|
|
|
|
static void SetCommonParameters (CompilationSection config, CompilerParameters p, Type compilerType, string language)
|
|
{
|
|
p.IncludeDebugInformation = config.Debug;
|
|
MonoSettingsSection mss = WebConfigurationManager.GetSection ("system.web/monoSettings") as MonoSettingsSection;
|
|
if (mss == null || !mss.UseCompilersCompatibility)
|
|
return;
|
|
|
|
Compiler compiler = mss.CompilersCompatibility.Get (language);
|
|
if (compiler == null)
|
|
return;
|
|
|
|
Type type = HttpApplication.LoadType (compiler.Type, false);
|
|
if (type != compilerType)
|
|
return;
|
|
|
|
p.CompilerOptions = String.Concat (p.CompilerOptions, " ", compiler.CompilerOptions);
|
|
}
|
|
|
|
static void ShowDebugModeMessage (string msg)
|
|
{
|
|
if (suppressDebugModeMessages)
|
|
return;
|
|
|
|
Console.Error.WriteLine ();
|
|
Console.Error.WriteLine ("******* DEBUG MODE MESSAGE *******");
|
|
Console.Error.WriteLine (msg);
|
|
Console.Error.WriteLine ("******* DEBUG MODE MESSAGE *******");
|
|
Console.Error.WriteLine ();
|
|
}
|
|
|
|
static void StoreInCache (BuildProvider bp, Assembly compiledAssembly, CompilerResults results)
|
|
{
|
|
string virtualPath = bp.VirtualPath;
|
|
var item = new BuildManagerCacheItem (compiledAssembly, bp, results);
|
|
|
|
if (buildCache.ContainsKey (virtualPath))
|
|
buildCache [virtualPath] = item;
|
|
else
|
|
buildCache.Add (virtualPath, item);
|
|
|
|
HttpContext ctx = HttpContext.Current;
|
|
HttpRequest req = ctx != null ? ctx.Request : null;
|
|
CacheDependency dep;
|
|
|
|
if (req != null) {
|
|
IDictionary <string, bool> deps = bp.ExtractDependencies ();
|
|
var files = new List <string> ();
|
|
string physicalPath;
|
|
|
|
physicalPath = req.MapPath (virtualPath);
|
|
if (File.Exists (physicalPath))
|
|
files.Add (physicalPath);
|
|
|
|
if (deps != null && deps.Count > 0) {
|
|
foreach (var d in deps) {
|
|
physicalPath = req.MapPath (d.Key);
|
|
if (!File.Exists (physicalPath))
|
|
continue;
|
|
if (!files.Contains (physicalPath))
|
|
files.Add (physicalPath);
|
|
}
|
|
}
|
|
|
|
dep = new CacheDependency (files.ToArray ());
|
|
} else
|
|
dep = null;
|
|
|
|
HttpRuntime.InternalCache.Add (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX + virtualPath,
|
|
true,
|
|
dep,
|
|
Cache.NoAbsoluteExpiration,
|
|
Cache.NoSlidingExpiration,
|
|
CacheItemPriority.High,
|
|
new CacheItemRemovedCallback (OnVirtualPathChanged));
|
|
}
|
|
}
|
|
}
|
|
|