2 // System.Web.Compilation.BuildManager
5 // Chris Toshok (toshok@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@novell.com)
7 // Marek Habersack (mhabersack@novell.com)
9 // (C) 2006-2009 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.CodeDom.Compiler;
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Collections.Specialized;
39 using System.ComponentModel;
41 using System.Reflection;
43 using System.Threading;
46 using System.Web.Caching;
47 using System.Web.Configuration;
48 using System.Web.Hosting;
49 using System.Web.Util;
51 namespace System.Web.Compilation
53 public sealed class BuildManager
55 internal const string FAKE_VIRTUAL_PATH_PREFIX = "/@@MonoFakeVirtualPath@@";
56 const string BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX = "@@Build_Manager@@";
57 static int BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH = BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX.Length;
59 static readonly object bigCompilationLock = new object ();
60 static readonly char[] virtualPathsToIgnoreSplitChars = {','};
62 static EventHandlerList events = new EventHandlerList ();
63 static object buildManagerRemoveEntryEvent = new object ();
66 static IEqualityComparer <string> comparer;
67 static StringComparison stringComparer;
68 static Dictionary <string, bool> virtualPathsToIgnore;
69 static bool haveVirtualPathsToIgnore;
70 static List <Assembly> AppCode_Assemblies = new List<Assembly>();
71 static List <Assembly> TopLevel_Assemblies = new List<Assembly>();
72 static Dictionary <Type, CodeDomProvider> codeDomProviders;
73 static Dictionary <string, BuildManagerCacheItem> buildCache;
74 static List <Assembly> referencedAssemblies;
76 static int buildCount;
77 static bool is_precompiled;
78 //static bool updatable; unused
79 static Dictionary<string, PreCompilationData> precompiled;
81 // This is here _only_ for the purpose of unit tests!
82 internal static bool suppressDebugModeMessages;
85 static ReaderWriterLockSlim buildCacheLock;
87 static ReaderWriterLock buildCacheLock;
89 static ulong recursionDepth;
91 internal static bool IsPrecompiled {
92 get { return is_precompiled; }
95 internal static event BuildManagerRemoveEntryEventHandler RemoveEntry {
96 add { events.AddHandler (buildManagerRemoveEntryEvent, value); }
97 remove { events.RemoveHandler (buildManagerRemoveEntryEvent, value); }
100 internal static bool BatchMode {
103 return false; // Fix for bug #380985
105 CompilationSection cs = CompilationConfig;
113 // Assemblies built from the App_Code directory
114 public static IList CodeAssemblies {
115 get { return AppCode_Assemblies; }
118 internal static CompilationSection CompilationConfig {
119 get { return WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection; }
122 internal static bool HaveResources {
126 internal static IList TopLevelAssemblies {
127 get { return TopLevel_Assemblies; }
130 static BuildManager ()
132 if (HttpRuntime.CaseInsensitive) {
133 comparer = StringComparer.CurrentCultureIgnoreCase;
134 stringComparer = StringComparison.CurrentCultureIgnoreCase;
136 comparer = StringComparer.CurrentCulture;
137 stringComparer = StringComparison.CurrentCulture;
140 hosted = (AppDomain.CurrentDomain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
141 buildCache = new Dictionary <string, BuildManagerCacheItem> (comparer);
143 buildCacheLock = new ReaderWriterLockSlim ();
145 buildCacheLock = new ReaderWriterLock ();
147 referencedAssemblies = new List <Assembly> ();
150 string appPath = HttpRuntime.AppDomainAppPath;
151 string precomp_name = null;
152 is_precompiled = String.IsNullOrEmpty (appPath) ? false : File.Exists ((precomp_name = Path.Combine (appPath, "PrecompiledApp.config")));
154 is_precompiled = LoadPrecompilationInfo (precomp_name);
155 LoadVirtualPathsToIgnore ();
158 // Deal with precompiled sites deployed in a different virtual path
159 static void FixVirtualPaths ()
161 if (precompiled == null)
166 foreach (string vpath in precompiled.Keys) {
167 parts = vpath.Split ('/');
168 for (int i = 0; i < parts.Length; i++) {
169 if (String.IsNullOrEmpty (parts [i]))
171 // The path must be rooted, otherwise PhysicalPath returned
172 // below will be relative to the current request path and
173 // File.Exists will return a false negative. See bug #546053
174 string test_path = "/" + String.Join ("/", parts, i, parts.Length - i);
175 VirtualPath result = GetAbsoluteVirtualPath (test_path);
176 if (result != null && File.Exists (result.PhysicalPath)) {
183 string app_vpath = HttpRuntime.AppDomainAppVirtualPath;
184 if (skip == -1 || (skip == 0 && app_vpath == "/"))
187 if (!app_vpath.EndsWith ("/"))
188 app_vpath = app_vpath + "/";
189 Dictionary<string, PreCompilationData> copy = new Dictionary<string, PreCompilationData> (precompiled);
190 precompiled.Clear ();
191 foreach (KeyValuePair<string,PreCompilationData> entry in copy) {
192 parts = entry.Key.Split ('/');
194 if (String.IsNullOrEmpty (parts [0]))
195 new_path = app_vpath + String.Join ("/", parts, skip + 1, parts.Length - skip - 1);
197 new_path = app_vpath + String.Join ("/", parts, skip, parts.Length - skip);
198 entry.Value.VirtualPath = new_path;
199 precompiled.Add (new_path, entry.Value);
203 static bool LoadPrecompilationInfo (string precomp_config)
205 using (XmlTextReader reader = new XmlTextReader (precomp_config)) {
206 reader.MoveToContent ();
207 if (reader.Name != "precompiledApp")
211 if (reader.HasAttributes)
212 while (reader.MoveToNextAttribute ())
213 if (reader.Name == "updatable") {
214 updatable = (reader.Value == "true");
220 string [] compiled = Directory.GetFiles (HttpRuntime.BinDirectory, "*.compiled");
221 foreach (string str in compiled)
228 static void LoadCompiled (string filename)
230 using (XmlTextReader reader = new XmlTextReader (filename)) {
231 reader.MoveToContent ();
232 if (reader.Name == "preserve" && reader.HasAttributes) {
233 reader.MoveToNextAttribute ();
234 string val = reader.Value;
237 // 6 -> app_code - nothing to do here
239 // 9 -> App_GlobalResources - set the assembly for HttpContext
240 if (reader.Name == "resultType" && (val == "2" || val == "3" || val == "8"))
241 LoadPageData (reader, true);
242 else if (val == "9") {
243 PreCompilationData pd = LoadPageData (reader, false);
244 HttpContext.AppGlobalResourcesAssembly = Assembly.Load (pd.AssemblyFileName);
250 class PreCompilationData {
251 public string VirtualPath;
252 public string AssemblyFileName;
253 public string TypeName;
257 static PreCompilationData LoadPageData (XmlTextReader reader, bool store)
259 PreCompilationData pc_data = new PreCompilationData ();
261 while (reader.MoveToNextAttribute ()) {
262 string name = reader.Name;
263 if (name == "virtualPath")
264 pc_data.VirtualPath = VirtualPathUtility.RemoveTrailingSlash (reader.Value);
265 else if (name == "assembly")
266 pc_data.AssemblyFileName = reader.Value;
267 else if (name == "type")
268 pc_data.TypeName = reader.Value;
271 if (precompiled == null)
272 precompiled = new Dictionary<string, PreCompilationData> (comparer);
273 precompiled.Add (pc_data.VirtualPath, pc_data);
278 static void AddAssembly (Assembly asm, List <Assembly> al)
280 if (al.Contains (asm))
286 static void AddPathToIgnore (string vp)
288 if (virtualPathsToIgnore == null)
289 virtualPathsToIgnore = new Dictionary <string, bool> (comparer);
291 VirtualPath path = GetAbsoluteVirtualPath (vp);
292 string vpAbsolute = path.Absolute;
293 if (!virtualPathsToIgnore.ContainsKey (vpAbsolute)) {
294 virtualPathsToIgnore.Add (vpAbsolute, true);
295 haveVirtualPathsToIgnore = true;
298 string vpRelative = path.AppRelative;
299 if (!virtualPathsToIgnore.ContainsKey (vpRelative)) {
300 virtualPathsToIgnore.Add (vpRelative, true);
301 haveVirtualPathsToIgnore = true;
304 if (!virtualPathsToIgnore.ContainsKey (vp)) {
305 virtualPathsToIgnore.Add (vp, true);
306 haveVirtualPathsToIgnore = true;
310 internal static void AddToReferencedAssemblies (Assembly asm)
312 // should not be used
315 static void AssertVirtualPathExists (VirtualPath virtualPath)
318 bool dothrow = false;
320 if (virtualPath.IsFake) {
321 realpath = virtualPath.PhysicalPath;
322 if (!File.Exists (realpath) && !Directory.Exists (realpath))
325 VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
326 string vpAbsolute = virtualPath.Absolute;
328 if (!vpp.FileExists (vpAbsolute) && !vpp.DirectoryExists (vpAbsolute))
333 throw new HttpException (404, "The file '" + virtualPath + "' does not exist.", virtualPath.Absolute);
336 static void Build (VirtualPath vp)
338 AssertVirtualPathExists (vp);
340 CompilationSection cs = CompilationConfig;
341 lock (bigCompilationLock) {
342 if (HasCachedItemNoLock (vp.Absolute))
345 if (recursionDepth == 0)
346 referencedAssemblies.Clear ();
350 BuildInner (vp, cs != null ? cs.Debug : false);
351 if (recursionDepth <= 1)
354 // See http://support.microsoft.com/kb/319947
355 if (buildCount > cs.NumRecompilesBeforeAppRestart)
356 HttpRuntime.UnloadAppDomain ();
363 // This method assumes it is being called with the big compilation lock held
364 static void BuildInner (VirtualPath vp, bool debug)
366 var builder = new BuildManagerDirectoryBuilder (vp);
367 bool recursive = recursionDepth > 1;
368 List <BuildProviderGroup> builderGroups = builder.Build (IsSingleBuild (vp, recursive));
369 if (builderGroups == null)
372 string vpabsolute = vp.Absolute;
373 int buildHash = (vpabsolute.GetHashCode () | (int)DateTime.Now.Ticks) + (int)recursionDepth;
374 string assemblyBaseName;
375 AssemblyBuilder abuilder;
378 bool singleBuild, needMainVpBuild;
379 CompilationException compilationError;
381 // Each group becomes a separate assembly.
382 foreach (BuildProviderGroup group in builderGroups) {
383 needMainVpBuild = false;
384 compilationError = null;
385 assemblyBaseName = null;
387 if (group.Count == 1) {
388 if (recursive || !group.Master)
389 assemblyBaseName = String.Format ("{0}_{1}.{2:x}.", group.NamePrefix, VirtualPathUtility.GetFileName (group [0].VirtualPath), buildHash);
394 if (assemblyBaseName == null)
395 assemblyBaseName = group.NamePrefix + "_";
397 ct = group.CompilerType;
399 while (attempts > 0) {
400 abuilder = new AssemblyBuilder (vp, CreateDomProvider (ct), assemblyBaseName);
401 abuilder.CompilerOptions = ct.CompilerParameters;
402 abuilder.AddAssemblyReference (GetReferencedAssemblies () as List <Assembly>);
404 GenerateAssembly (abuilder, group, vp, debug);
406 } catch (CompilationException ex) {
409 throw new HttpException ("Single file build failed.", ex);
412 needMainVpBuild = true;
413 compilationError = ex;
417 CompilerResults results = ex.Results;
419 throw new HttpException ("No results returned from failed compilation.", ex);
421 RemoveFailedAssemblies (vpabsolute, ex, abuilder, group, results, debug);
425 if (needMainVpBuild) {
426 // One last attempt - try to build just the requested path
427 // if it's not built yet or just return without throwing the
428 // exception if it has already been built.
429 if (HasCachedItemNoLock (vpabsolute)) {
431 DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
432 compilationError, vpabsolute);
436 // This will trigger a recursive build of the requested vp,
437 // which means only the vp alone will be built (or not);
439 if (HasCachedItemNoLock (vpabsolute)) {
441 DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
442 compilationError, vpabsolute);
446 // In theory this code is unreachable. If the recursive
447 // build of the main vp failed, then it should have thrown
448 // the build exception.
449 throw new HttpException ("Requested virtual path build failed.", compilationError);
454 static CodeDomProvider CreateDomProvider (CompilerType ct)
456 if (codeDomProviders == null)
457 codeDomProviders = new Dictionary <Type, CodeDomProvider> ();
459 Type type = ct.CodeDomProviderType;
461 CompilationSection cs = CompilationConfig;
462 CompilerType tmp = GetDefaultCompilerTypeForLanguage (cs.DefaultLanguage, cs);
464 type = tmp.CodeDomProviderType;
471 if (codeDomProviders.TryGetValue (type, out ret))
474 ret = Activator.CreateInstance (type) as CodeDomProvider;
478 codeDomProviders.Add (type, ret);
482 public static object CreateInstanceFromVirtualPath (string virtualPath, Type requiredBaseType)
484 return CreateInstanceFromVirtualPath (GetAbsoluteVirtualPath (virtualPath), requiredBaseType);
487 internal static object CreateInstanceFromVirtualPath (VirtualPath virtualPath, Type requiredBaseType)
489 if (requiredBaseType == null)
490 throw new NullReferenceException (); // This is what MS does, but
491 // from somewhere else.
493 Type type = GetCompiledType (virtualPath);
497 if (!requiredBaseType.IsAssignableFrom (type))
498 throw new HttpException (500,
499 String.Format ("Type '{0}' does not inherit from '{1}'.", type.FullName, requiredBaseType.FullName));
501 return Activator.CreateInstance (type, null);
504 static void DescribeCompilationError (string format, CompilationException ex, params object[] parms)
506 StringBuilder sb = new StringBuilder ();
507 string newline = Environment.NewLine;
510 sb.AppendFormat (format + newline, parms);
512 sb.Append (format + newline);
514 CompilerResults results = ex != null ? ex.Results : null;
516 sb.Append ("No compiler error information present." + newline);
518 sb.Append ("Compiler errors:" + newline);
519 foreach (CompilerError error in results.Errors)
520 sb.Append (" " + error.ToString () + newline);
524 sb.Append (newline + "Exception thrown:" + newline);
525 sb.Append (ex.ToString ());
528 ShowDebugModeMessage (sb.ToString ());
531 static BuildProvider FindBuildProviderForPhysicalPath (string path, BuildProviderGroup group, HttpRequest req)
533 if (req == null || String.IsNullOrEmpty (path))
536 foreach (BuildProvider bp in group) {
537 if (String.Compare (path, req.MapPath (bp.VirtualPath), stringComparer) == 0)
544 static void GenerateAssembly (AssemblyBuilder abuilder, BuildProviderGroup group, VirtualPath vp, bool debug)
546 IDictionary <string, bool> deps;
547 BuildManagerCacheItem bmci;
548 string bvp, vpabsolute = vp.Absolute;
554 newline = Environment.NewLine;
555 sb = new StringBuilder ("Code generation for certain virtual paths in a batch failed. Those files have been removed from the batch." + newline);
556 sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
562 List <BuildProvider> failedBuildProviders = null;
563 foreach (BuildProvider bp in group) {
564 bvp = bp.VirtualPath;
565 if (HasCachedItemNoLock (bvp))
569 bp.GenerateCode (abuilder);
570 } catch (Exception ex) {
571 if (String.Compare (bvp, vpabsolute, stringComparer) == 0) {
572 if (ex is CompilationException || ex is ParseException)
575 throw new HttpException ("Code generation failed.", ex);
578 if (failedBuildProviders == null)
579 failedBuildProviders = new List <BuildProvider> ();
580 failedBuildProviders.Add (bp);
586 sb.AppendFormat ("Failed file virtual path: {0}; Exception: {1}{2}{1}", bp.VirtualPath, newline, ex);
591 deps = bp.ExtractDependencies ();
593 foreach (var dep in deps) {
594 bmci = GetCachedItemNoLock (dep.Key);
595 if (bmci == null || bmci.BuiltAssembly == null)
597 abuilder.AddAssemblyReference (bmci.BuiltAssembly);
602 if (sb != null && failedCount > 0)
603 ShowDebugModeMessage (sb.ToString ());
605 if (failedBuildProviders != null) {
606 foreach (BuildProvider bp in failedBuildProviders)
610 foreach (Assembly asm in referencedAssemblies) {
614 abuilder.AddAssemblyReference (asm);
617 CompilerResults results = abuilder.BuildAssembly (vp);
619 // No results is not an error - it is possible that the assembly builder contained only .asmx and
620 // .ashx files which had no body, just the directive. In such case, no code unit or code file is added
621 // to the assembly builder and, in effect, no assembly is produced but there are STILL types that need
622 // to be added to the cache.
623 Assembly compiledAssembly = results != null ? results.CompiledAssembly : null;
627 buildCacheLock.EnterWriteLock ();
629 buildCacheLock.AcquireWriterLock (-1);
632 if (compiledAssembly != null)
633 referencedAssemblies.Add (compiledAssembly);
635 foreach (BuildProvider bp in group) {
636 if (HasCachedItemNoLock (bp.VirtualPath))
639 StoreInCache (bp, compiledAssembly, results);
644 buildCacheLock.ExitWriteLock ();
646 buildCacheLock.ReleaseWriterLock ();
652 static VirtualPath GetAbsoluteVirtualPath (string virtualPath)
656 if (!VirtualPathUtility.IsRooted (virtualPath)) {
657 HttpContext ctx = HttpContext.Current;
658 HttpRequest req = ctx != null ? ctx.Request : null;
661 string fileDir = req.FilePath;
662 if (!String.IsNullOrEmpty (fileDir) && String.Compare (fileDir, "/", StringComparison.Ordinal) != 0)
663 fileDir = VirtualPathUtility.GetDirectory (fileDir);
667 vp = VirtualPathUtility.Combine (fileDir, virtualPath);
669 throw new HttpException ("No context, cannot map paths.");
673 return new VirtualPath (vp);
676 [MonoTODO ("Not implemented, always returns null")]
677 public static BuildDependencySet GetCachedBuildDependencySet (HttpContext context, string virtualPath)
679 return null; // null is ok here until we store the dependency set in the Cache.
682 static BuildManagerCacheItem GetCachedItem (string vp)
688 buildCacheLock.EnterReadLock ();
690 buildCacheLock.AcquireReaderLock (-1);
693 return GetCachedItemNoLock (vp);
697 buildCacheLock.ExitReadLock ();
699 buildCacheLock.ReleaseReaderLock ();
705 static BuildManagerCacheItem GetCachedItemNoLock (string vp)
707 BuildManagerCacheItem ret;
708 if (buildCache.TryGetValue (vp, out ret))
714 internal static Type GetCodeDomProviderType (BuildProvider provider)
716 CompilerType codeCompilerType;
717 Type codeDomProviderType = null;
719 codeCompilerType = provider.CodeCompilerType;
720 if (codeCompilerType != null)
721 codeDomProviderType = codeCompilerType.CodeDomProviderType;
723 if (codeDomProviderType == null)
724 throw new HttpException (String.Concat ("Provider '", provider, " 'fails to specify the compiler type."));
726 return codeDomProviderType;
729 static Type GetPrecompiledType (string virtualPath)
731 PreCompilationData pc_data;
732 if (precompiled != null && precompiled.TryGetValue (virtualPath, out pc_data)) {
733 if (pc_data.Type == null) {
734 pc_data.Type = Type.GetType (pc_data.TypeName + ", " + pc_data.AssemblyFileName, true);
741 internal static Type GetPrecompiledApplicationType ()
746 Type apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath, "Global.asax"));
748 apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath , "global.asax"));
752 public static Assembly GetCompiledAssembly (string virtualPath)
754 return GetCompiledAssembly (GetAbsoluteVirtualPath (virtualPath));
757 internal static Assembly GetCompiledAssembly (VirtualPath virtualPath)
759 string vpabsolute = virtualPath.Absolute;
760 if (is_precompiled) {
761 Type type = GetPrecompiledType (vpabsolute);
763 return type.Assembly;
765 BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
767 return bmci.BuiltAssembly;
770 bmci = GetCachedItem (vpabsolute);
772 return bmci.BuiltAssembly;
777 public static Type GetCompiledType (string virtualPath)
779 return GetCompiledType (GetAbsoluteVirtualPath (virtualPath));
782 internal static Type GetCompiledType (VirtualPath virtualPath)
784 string vpabsolute = virtualPath.Absolute;
785 if (is_precompiled) {
786 Type type = GetPrecompiledType (vpabsolute);
790 BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
792 ReferenceAssemblyInCompilation (bmci);
797 bmci = GetCachedItem (vpabsolute);
799 ReferenceAssemblyInCompilation (bmci);
806 public static string GetCompiledCustomString (string virtualPath)
808 return GetCompiledCustomString (GetAbsoluteVirtualPath (virtualPath));
811 internal static string GetCompiledCustomString (VirtualPath virtualPath)
813 string vpabsolute = virtualPath.Absolute;
814 BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
816 return bmci.CompiledCustomString;
819 bmci = GetCachedItem (vpabsolute);
821 return bmci.CompiledCustomString;
826 internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection)
828 return GetDefaultCompilerTypeForLanguage (language, configSection, true);
831 internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection, bool throwOnMissing)
833 // MS throws when accesing a Hashtable, we do here.
834 if (language == null || language.Length == 0)
835 throw new ArgumentNullException ("language");
837 CompilationSection config;
838 if (configSection == null)
839 config = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
841 config = configSection;
843 Compiler compiler = config.Compilers.Get (language);
844 CompilerParameters p;
847 if (compiler != null) {
848 type = HttpApplication.LoadType (compiler.Type, true);
849 p = new CompilerParameters ();
850 p.CompilerOptions = compiler.CompilerOptions;
851 p.WarningLevel = compiler.WarningLevel;
852 SetCommonParameters (config, p, type, language);
853 return new CompilerType (type, p);
856 if (CodeDomProvider.IsDefinedLanguage (language)) {
857 CompilerInfo info = CodeDomProvider.GetCompilerInfo (language);
858 p = info.CreateDefaultCompilerParameters ();
859 type = info.CodeDomProviderType;
860 SetCommonParameters (config, p, type, language);
861 return new CompilerType (type, p);
865 throw new HttpException (String.Concat ("No compiler for language '", language, "'."));
870 public static ICollection GetReferencedAssemblies ()
872 List <Assembly> al = new List <Assembly> ();
874 CompilationSection compConfig = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
875 if (compConfig == null)
878 bool addAssembliesInBin = false;
879 foreach (AssemblyInfo info in compConfig.Assemblies) {
880 if (info.Assembly == "*")
881 addAssembliesInBin = is_precompiled ? false : true;
883 LoadAssembly (info, al);
886 foreach (Assembly topLevelAssembly in TopLevelAssemblies)
887 al.Add (topLevelAssembly);
889 foreach (string assLocation in WebConfigurationManager.ExtraAssemblies)
890 LoadAssembly (assLocation, al);
893 // Precompiled sites unconditionally load all assemblies from bin/ (fix for
895 if (is_precompiled || addAssembliesInBin) {
896 foreach (string s in HttpApplication.BinDirectoryAssemblies) {
898 LoadAssembly (s, al);
899 } catch (BadImageFormatException) {
908 // The 2 GetType() overloads work on the global.asax, App_GlobalResources, App_WebReferences or App_Browsers
909 public static Type GetType (string typeName, bool throwOnError)
911 return GetType (typeName, throwOnError, false);
914 public static Type GetType (string typeName, bool throwOnError, bool ignoreCase)
918 foreach (Assembly asm in TopLevel_Assemblies) {
919 ret = asm.GetType (typeName, throwOnError, ignoreCase);
923 } catch (Exception ex) {
924 throw new HttpException ("Failed to find the specified type.", ex);
929 public static IDictionary <string, bool> GetVirtualPathDependencies (string virtualPath)
931 return GetVirtualPathDependencies (virtualPath, null);
934 internal static IDictionary <string, bool> GetVirtualPathDependencies (string virtualPath, BuildProvider bprovider)
936 BuildProvider provider = bprovider;
937 if (provider == null) {
938 CompilationSection cs = CompilationConfig;
941 provider = BuildManagerDirectoryBuilder.GetBuildProvider (virtualPath, cs.BuildProviders);
944 if (provider == null)
947 return provider.ExtractDependencies ();
950 internal static bool HasCachedItemNoLock (string vp)
952 return buildCache.ContainsKey (vp);
955 internal static bool IgnoreVirtualPath (string virtualPath)
957 if (!haveVirtualPathsToIgnore)
960 if (virtualPathsToIgnore.ContainsKey (virtualPath))
966 static bool IsSingleBuild (VirtualPath vp, bool recursive)
968 if (String.Compare (vp.AppRelative, "~/global.asax", StringComparison.OrdinalIgnoreCase) == 0)
977 static void LoadAssembly (string path, List <Assembly> al)
979 AddAssembly (Assembly.LoadFrom (path), al);
982 static void LoadAssembly (AssemblyInfo info, List <Assembly> al)
984 AddAssembly (Assembly.Load (info.Assembly), al);
987 static void LoadVirtualPathsToIgnore ()
989 NameValueCollection appSettings = WebConfigurationManager.AppSettings;
990 if (appSettings == null)
993 string pathsFromConfig = appSettings ["MonoAspnetBatchCompileIgnorePaths"];
994 string pathsFromFile = appSettings ["MonoAspnetBatchCompileIgnoreFromFile"];
996 if (!String.IsNullOrEmpty (pathsFromConfig)) {
997 string[] paths = pathsFromConfig.Split (virtualPathsToIgnoreSplitChars);
1000 foreach (string p in paths) {
1002 if (path.Length == 0)
1005 AddPathToIgnore (path);
1009 if (!String.IsNullOrEmpty (pathsFromFile)) {
1011 HttpContext ctx = HttpContext.Current;
1012 HttpRequest req = ctx != null ? ctx.Request : null;
1015 throw new HttpException ("Missing context, cannot continue.");
1017 realpath = req.MapPath (pathsFromFile);
1018 if (!File.Exists (realpath))
1021 string[] paths = File.ReadAllLines (realpath);
1022 if (paths == null || paths.Length == 0)
1026 foreach (string p in paths) {
1028 if (path.Length == 0)
1031 AddPathToIgnore (path);
1036 static void OnEntryRemoved (string vp)
1038 BuildManagerRemoveEntryEventHandler eh = events [buildManagerRemoveEntryEvent] as BuildManagerRemoveEntryEventHandler;
1041 eh (new BuildManagerRemoveEntryEventArgs (vp, HttpContext.Current));
1044 static void OnVirtualPathChanged (string key, object value, CacheItemRemovedReason removedReason)
1048 if (StrUtils.StartsWith (key, BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX))
1049 virtualPath = key.Substring (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH);
1053 bool locked = false;
1056 buildCacheLock.EnterWriteLock ();
1058 buildCacheLock.AcquireWriterLock (-1);
1062 if (HasCachedItemNoLock (virtualPath)) {
1063 buildCache.Remove (virtualPath);
1064 OnEntryRemoved (virtualPath);
1069 buildCacheLock.ExitWriteLock ();
1071 buildCacheLock.ReleaseWriterLock ();
1077 static void ReferenceAssemblyInCompilation (BuildManagerCacheItem bmci)
1079 if (recursionDepth == 0 || referencedAssemblies.Contains (bmci.BuiltAssembly))
1082 referencedAssemblies.Add (bmci.BuiltAssembly);
1085 static void RemoveFailedAssemblies (string requestedVirtualPath, CompilationException ex, AssemblyBuilder abuilder,
1086 BuildProviderGroup group, CompilerResults results, bool debug)
1092 newline = Environment.NewLine;
1093 sb = new StringBuilder ("Compilation of certain files in a batch failed. Another attempt to compile the batch will be made." + newline);
1094 sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
1100 var failedBuildProviders = new List <BuildProvider> ();
1102 HttpContext ctx = HttpContext.Current;
1103 HttpRequest req = ctx != null ? ctx.Request : null;
1104 bool rethrow = false;
1106 foreach (CompilerError error in results.Errors) {
1107 if (error.IsWarning)
1110 bp = abuilder.GetBuildProviderForPhysicalFilePath (error.FileName);
1112 bp = FindBuildProviderForPhysicalPath (error.FileName, group, req);
1117 if (String.Compare (bp.VirtualPath, requestedVirtualPath, StringComparison.Ordinal) == 0)
1120 if (!failedBuildProviders.Contains (bp)) {
1121 failedBuildProviders.Add (bp);
1123 sb.AppendFormat ("\t{0}{1}", bp.VirtualPath, newline);
1127 sb.AppendFormat ("\t\t{0}{1}", error, newline);
1130 foreach (BuildProvider fbp in failedBuildProviders)
1134 sb.AppendFormat ("{0}The following exception has been thrown for the file(s) listed above:{0}{1}",
1135 newline, ex.ToString ());
1136 ShowDebugModeMessage (sb.ToString ());
1141 throw new HttpException ("Compilation failed.", ex);
1144 static void SetCommonParameters (CompilationSection config, CompilerParameters p, Type compilerType, string language)
1146 p.IncludeDebugInformation = config.Debug;
1147 MonoSettingsSection mss = WebConfigurationManager.GetSection ("system.web/monoSettings") as MonoSettingsSection;
1148 if (mss == null || !mss.UseCompilersCompatibility)
1151 Compiler compiler = mss.CompilersCompatibility.Get (language);
1152 if (compiler == null)
1155 Type type = HttpApplication.LoadType (compiler.Type, false);
1156 if (type != compilerType)
1159 p.CompilerOptions = String.Concat (p.CompilerOptions, " ", compiler.CompilerOptions);
1162 static void ShowDebugModeMessage (string msg)
1164 if (suppressDebugModeMessages)
1167 Console.WriteLine ();
1168 Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
1169 Console.WriteLine (msg);
1170 Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
1171 Console.WriteLine ();
1174 static void StoreInCache (BuildProvider bp, Assembly compiledAssembly, CompilerResults results)
1176 buildCache.Add (bp.VirtualPath, new BuildManagerCacheItem (compiledAssembly, bp, results));
1178 HttpContext ctx = HttpContext.Current;
1179 HttpRequest req = ctx != null ? ctx.Request : null;
1180 CacheDependency dep;
1181 string virtualPath = bp.VirtualPath;
1184 IDictionary <string, bool> deps = bp.ExtractDependencies ();
1185 var files = new List <string> ();
1186 string physicalPath;
1188 physicalPath = req.MapPath (virtualPath);
1189 if (File.Exists (physicalPath))
1190 files.Add (physicalPath);
1192 if (deps != null && deps.Count > 0) {
1193 foreach (var d in deps) {
1194 physicalPath = req.MapPath (d.Key);
1195 if (!File.Exists (physicalPath))
1197 if (!files.Contains (physicalPath))
1198 files.Add (physicalPath);
1202 dep = new CacheDependency (files.ToArray ());
1206 HttpRuntime.InternalCache.Add (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX + virtualPath,
1209 Cache.NoAbsoluteExpiration,
1210 Cache.NoSlidingExpiration,
1211 CacheItemPriority.High,
1212 new CacheItemRemovedCallback (OnVirtualPathChanged));