Normalize line endings.
[mono.git] / mcs / class / System.Web / System.Web.Compilation / BuildManager.cs
1 //
2 // System.Web.Compilation.BuildManager
3 //
4 // Authors:
5 //      Chris Toshok (toshok@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
7 //      Marek Habersack (mhabersack@novell.com)
8 //
9 // (C) 2006-2009 Novell, Inc (http://www.novell.com)
10 //
11
12 //
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:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
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.
31 //
32
33 using System;
34 using System.CodeDom;
35 using System.CodeDom.Compiler;
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Collections.Specialized;
39 using System.ComponentModel;
40 using System.IO;
41 using System.Reflection;
42 using System.Text;
43 using System.Threading;
44 using System.Xml;
45 using System.Web;
46 using System.Web.Caching;
47 using System.Web.Configuration;
48 using System.Web.Hosting;
49 using System.Web.Util;
50 #if NET_4_0
51 using System.Runtime.Versioning;
52 #endif
53
54 namespace System.Web.Compilation
55 {
56         public sealed class BuildManager
57         {
58                 internal const string FAKE_VIRTUAL_PATH_PREFIX = "/@@MonoFakeVirtualPath@@";
59                 const string BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX = "@@Build_Manager@@";
60                 static int BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH = BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX.Length;
61
62                 static readonly object bigCompilationLock = new object ();
63                 static readonly object virtualPathsToIgnoreLock = new object ();
64                 static readonly char[] virtualPathsToIgnoreSplitChars = {','};
65                 
66                 static EventHandlerList events = new EventHandlerList ();
67                 static object buildManagerRemoveEntryEvent = new object ();
68                 
69                 static bool hosted;
70                 static Dictionary <string, bool> virtualPathsToIgnore;
71                 static bool virtualPathsToIgnoreChecked;
72                 static bool haveVirtualPathsToIgnore;
73                 static List <Assembly> AppCode_Assemblies = new List<Assembly>();
74                 static List <Assembly> TopLevel_Assemblies = new List<Assembly>();
75                 static Dictionary <Type, CodeDomProvider> codeDomProviders;
76                 static Dictionary <string, BuildManagerCacheItem> buildCache;
77                 static List <Assembly> referencedAssemblies;
78                 static List <Assembly> configReferencedAssemblies;
79                 static bool getReferencedAssembliesInvoked;
80                 
81                 static int buildCount;
82                 static bool is_precompiled;
83                 static bool allowReferencedAssembliesCaching;
84 #if NET_4_0
85                 static List <Assembly> dynamicallyRegisteredAssemblies;
86                 static bool? batchCompilationEnabled;
87                 static FrameworkName targetFramework;
88                 static bool preStartMethodsDone;
89                 static bool preStartMethodsRunning;
90 #endif
91                 //static bool updatable; unused
92                 static Dictionary<string, PreCompilationData> precompiled;
93                 
94                 // This is here _only_ for the purpose of unit tests!
95                 internal static bool suppressDebugModeMessages;
96
97 #if SYSTEMCORE_DEP
98                 static ReaderWriterLockSlim buildCacheLock;
99 #else
100                 static ReaderWriterLock buildCacheLock;
101 #endif
102                 static ulong recursionDepth;
103
104                 internal static bool AllowReferencedAssembliesCaching {
105                         get { return allowReferencedAssembliesCaching; }
106                         set { allowReferencedAssembliesCaching = value; }
107                 }
108                 
109                 internal static bool IsPrecompiled {
110                         get { return is_precompiled; }
111                 }
112                 
113                 internal static event BuildManagerRemoveEntryEventHandler RemoveEntry {
114                         add { events.AddHandler (buildManagerRemoveEntryEvent, value); }
115                         remove { events.RemoveHandler (buildManagerRemoveEntryEvent, value); }
116                 }
117
118 #if NET_4_0
119                 internal static bool PreStartMethodsRunning {
120                         get { return preStartMethodsRunning;
121                         }
122                 }
123                 
124                 public static bool? BatchCompilationEnabled {
125                         get { return batchCompilationEnabled; }
126                         set {
127                                 if (preStartMethodsDone)
128                                         throw new InvalidOperationException ("This method cannot be called after the application's pre-start initialization stage.");
129                                 batchCompilationEnabled = value;
130                         }
131                 }
132
133                 public static FrameworkName TargetFramework {
134                         get {
135                                 if (targetFramework == null) {
136                                         CompilationSection cs = CompilationConfig;
137                                         string framework;
138                                         if (cs == null)
139                                                 framework = null;
140                                         else
141                                                 framework = cs.TargetFramework;
142                                         
143                                         if (String.IsNullOrEmpty (framework))
144                                                 targetFramework = new FrameworkName (".NETFramework,Version=v4.0");
145                                         else
146                                                 targetFramework = new FrameworkName (framework);
147                                 }
148
149                                 return targetFramework;
150                         }
151                 }
152 #endif
153                 internal static bool BatchMode {
154                         get {
155 #if NET_4_0
156                                 if (batchCompilationEnabled != null)
157                                         return (bool)batchCompilationEnabled;
158 #endif
159                                 if (!hosted)
160                                         return false; // Fix for bug #380985
161
162                                 CompilationSection cs = CompilationConfig;
163                                 if (cs == null)
164                                         return true;
165                                 
166                                 return cs.Batch;
167                         }
168                 }
169
170                 // Assemblies built from the App_Code directory
171                 public static IList CodeAssemblies {
172                         get { return AppCode_Assemblies; }
173                 }
174                 
175                 internal static CompilationSection CompilationConfig {
176                         get { return WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection; }
177                 }
178
179                 internal static bool HaveResources {
180                         get; set;
181                 }
182                 
183                 internal static IList TopLevelAssemblies {
184                         get { return TopLevel_Assemblies; }
185                 }
186                 
187                 static BuildManager ()
188                 {
189                         hosted = (AppDomain.CurrentDomain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
190                         buildCache = new Dictionary <string, BuildManagerCacheItem> (RuntimeHelpers.StringEqualityComparerCulture);
191 #if SYSTEMCORE_DEP
192                         buildCacheLock = new ReaderWriterLockSlim ();
193 #else
194                         buildCacheLock = new ReaderWriterLock ();
195 #endif
196                         referencedAssemblies = new List <Assembly> ();
197                         recursionDepth = 0;
198
199                         string appPath = HttpRuntime.AppDomainAppPath;
200                         string precomp_name = null;
201                         is_precompiled = String.IsNullOrEmpty (appPath) ? false : File.Exists ((precomp_name = Path.Combine (appPath, "PrecompiledApp.config")));
202                         if (is_precompiled)
203                                 is_precompiled = LoadPrecompilationInfo (precomp_name);
204                 }
205
206                 // Deal with precompiled sites deployed in a different virtual path
207                 static void FixVirtualPaths ()
208                 {
209                         if (precompiled == null)
210                                 return;
211                         
212                         string [] parts;
213                         int skip = -1;
214                         foreach (string vpath in precompiled.Keys) {
215                                 parts = vpath.Split ('/');
216                                 for (int i = 0; i < parts.Length; i++) {
217                                         if (String.IsNullOrEmpty (parts [i]))
218                                                 continue;
219                                         // The path must be rooted, otherwise PhysicalPath returned
220                                         // below will be relative to the current request path and
221                                         // File.Exists will return a false negative. See bug #546053
222                                         string test_path = "/" + String.Join ("/", parts, i, parts.Length - i);
223                                         VirtualPath result = GetAbsoluteVirtualPath (test_path);
224                                         if (result != null && File.Exists (result.PhysicalPath)) {
225                                                 skip = i - 1;
226                                                 break;
227                                         }
228                                 }
229                         }
230                         
231                         string app_vpath = HttpRuntime.AppDomainAppVirtualPath;
232                         if (skip == -1 || (skip == 0 && app_vpath == "/"))
233                                 return;
234
235                         if (!app_vpath.EndsWith ("/"))
236                                 app_vpath = app_vpath + "/";
237                         Dictionary<string, PreCompilationData> copy = new Dictionary<string, PreCompilationData> (precompiled);
238                         precompiled.Clear ();
239                         foreach (KeyValuePair<string,PreCompilationData> entry in copy) {
240                                 parts = entry.Key.Split ('/');
241                                 string new_path;
242                                 if (String.IsNullOrEmpty (parts [0]))
243                                         new_path = app_vpath + String.Join ("/", parts, skip + 1, parts.Length - skip - 1);
244                                 else
245                                         new_path = app_vpath + String.Join ("/", parts, skip, parts.Length - skip);
246                                 entry.Value.VirtualPath = new_path;
247                                 precompiled.Add (new_path, entry.Value);
248                         }
249                 }
250
251                 static bool LoadPrecompilationInfo (string precomp_config)
252                 {
253                         using (XmlTextReader reader = new XmlTextReader (precomp_config)) {
254                                 reader.MoveToContent ();
255                                 if (reader.Name != "precompiledApp")
256                                         return false;
257
258                                 /* unused
259                                 if (reader.HasAttributes)
260                                         while (reader.MoveToNextAttribute ())
261                                                 if (reader.Name == "updatable") {
262                                                         updatable = (reader.Value == "true");
263                                                         break;
264                                                 }
265                                 */
266                         }
267
268                         string [] compiled = Directory.GetFiles (HttpRuntime.BinDirectory, "*.compiled");
269                         foreach (string str in compiled)
270                                 LoadCompiled (str);
271
272                         FixVirtualPaths ();
273                         return true;
274                 }
275
276                 static void LoadCompiled (string filename)
277                 {
278                         using (XmlTextReader reader = new XmlTextReader (filename)) {
279                                 reader.MoveToContent ();
280                                 if (reader.Name == "preserve" && reader.HasAttributes) {
281                                         reader.MoveToNextAttribute ();
282                                         string val = reader.Value;
283                                         // 1 -> app_code subfolder - add the assembly to CodeAssemblies
284                                         // 2 -> ashx
285                                         // 3 -> ascx, aspx
286                                         // 6 -> app_code - add the assembly to CodeAssemblies
287                                         // 8 -> global.asax
288                                         // 9 -> App_GlobalResources - set the assembly for HttpContext
289                                         if (reader.Name == "resultType" && (val == "2" || val == "3" || val == "8"))
290                                                 LoadPageData (reader, true);
291                                         else if (val == "1" || val == "6") {
292                                                 PreCompilationData pd = LoadPageData (reader, false);
293                                                 CodeAssemblies.Add (Assembly.Load (pd.AssemblyFileName));
294                                         } else if (val == "9") {
295                                                 PreCompilationData pd = LoadPageData (reader, false);
296                                                 HttpContext.AppGlobalResourcesAssembly = Assembly.Load (pd.AssemblyFileName);
297                                         }
298                                 }
299                         }
300                 }
301
302                 class PreCompilationData {
303                         public string VirtualPath;
304                         public string AssemblyFileName;
305                         public string TypeName;
306                         public Type Type;
307                 }
308
309                 static PreCompilationData LoadPageData (XmlTextReader reader, bool store)
310                 {
311                         PreCompilationData pc_data = new PreCompilationData ();
312
313                         while (reader.MoveToNextAttribute ()) {
314                                 string name = reader.Name;
315                                 if (name == "virtualPath")
316                                         pc_data.VirtualPath = VirtualPathUtility.RemoveTrailingSlash (reader.Value);
317                                 else if (name == "assembly")
318                                         pc_data.AssemblyFileName = reader.Value;
319                                 else if (name == "type")
320                                         pc_data.TypeName = reader.Value;
321                         }
322                         if (store) {
323                                 if (precompiled == null)
324                                         precompiled = new Dictionary<string, PreCompilationData> (RuntimeHelpers.StringEqualityComparerCulture);
325                                 precompiled.Add (pc_data.VirtualPath, pc_data);
326                         }
327                         return pc_data;
328                 }
329
330                 static void AddAssembly (Assembly asm, List <Assembly> al)
331                 {
332                         if (al.Contains (asm))
333                                 return;
334
335                         al.Add (asm);
336                 }
337                 
338                 static void AddPathToIgnore (string vp)
339                 {
340                         if (virtualPathsToIgnore == null)
341                                 virtualPathsToIgnore = new Dictionary <string, bool> (RuntimeHelpers.StringEqualityComparerCulture);
342                         
343                         VirtualPath path = GetAbsoluteVirtualPath (vp);
344                         string vpAbsolute = path.Absolute;
345                         if (!virtualPathsToIgnore.ContainsKey (vpAbsolute)) {
346                                 virtualPathsToIgnore.Add (vpAbsolute, true);
347                                 haveVirtualPathsToIgnore = true;
348                         }
349                         
350                         string vpRelative = path.AppRelative;
351                         if (!virtualPathsToIgnore.ContainsKey (vpRelative)) {
352                                 virtualPathsToIgnore.Add (vpRelative, true);
353                                 haveVirtualPathsToIgnore = true;
354                         }
355
356                         if (!virtualPathsToIgnore.ContainsKey (vp)) {
357                                 virtualPathsToIgnore.Add (vp, true);
358                                 haveVirtualPathsToIgnore = true;
359                         }
360                 }
361
362                 internal static void AddToReferencedAssemblies (Assembly asm)
363                 {
364                         // should not be used
365                 }
366                 
367                 static void AssertVirtualPathExists (VirtualPath virtualPath)
368                 {
369                         string realpath;
370                         bool dothrow = false;
371                         
372                         if (virtualPath.IsFake) {
373                                 realpath = virtualPath.PhysicalPath;
374                                 if (!File.Exists (realpath) && !Directory.Exists (realpath))
375                                         dothrow = true;
376                         } else {
377                                 VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
378                                 string vpAbsolute = virtualPath.Absolute;
379                                 
380                                 if (!vpp.FileExists (vpAbsolute) && !vpp.DirectoryExists (vpAbsolute))
381                                         dothrow = true;
382                         }
383
384                         if (dothrow)
385                                 throw new HttpException (404, "The file '" + virtualPath + "' does not exist.", virtualPath.Absolute);
386                 }
387
388                 static void Build (VirtualPath vp)
389                 {
390                         AssertVirtualPathExists (vp);
391
392                         CompilationSection cs = CompilationConfig;
393                         lock (bigCompilationLock) {
394                                 bool entryExists;
395                                 if (HasCachedItemNoLock (vp.Absolute, out entryExists))
396                                         return;
397
398                                 if (recursionDepth == 0)
399                                         referencedAssemblies.Clear ();
400
401                                 recursionDepth++;
402                                 try {
403                                         BuildInner (vp, cs != null ? cs.Debug : false);
404                                         if (entryExists && recursionDepth <= 1)
405                                                 // We count only update builds - first time a file
406                                                 // (or a batch) is built doesn't count.
407                                                 buildCount++;
408                                 } finally {
409                                         // See http://support.microsoft.com/kb/319947
410                                         if (buildCount > cs.NumRecompilesBeforeAppRestart)
411                                                 HttpRuntime.UnloadAppDomain ();
412                                         recursionDepth--;
413                                 }
414                         }
415                 }
416
417                 // This method assumes it is being called with the big compilation lock held
418                 static void BuildInner (VirtualPath vp, bool debug)
419                 {
420                         var builder = new BuildManagerDirectoryBuilder (vp);
421                         bool recursive = recursionDepth > 1;
422                         List <BuildProviderGroup> builderGroups = builder.Build (IsSingleBuild (vp, recursive));
423                         if (builderGroups == null)
424                                 return;
425
426                         string vpabsolute = vp.Absolute;
427                         int buildHash = (vpabsolute.GetHashCode () | (int)DateTime.Now.Ticks) + (int)recursionDepth;
428                         string assemblyBaseName;
429                         AssemblyBuilder abuilder;
430                         CompilerType ct;
431                         int attempts;
432                         bool singleBuild, needMainVpBuild;
433                         CompilationException compilationError;
434                         
435                         // Each group becomes a separate assembly.
436                         foreach (BuildProviderGroup group in builderGroups) {
437                                 needMainVpBuild = false;
438                                 compilationError = null;
439                                 assemblyBaseName = null;
440                                 
441                                 if (group.Count == 1) {
442                                         if (recursive || !group.Master)
443                                                 assemblyBaseName = String.Format ("{0}_{1}.{2:x}.", group.NamePrefix, VirtualPathUtility.GetFileName (group [0].VirtualPath), buildHash);
444                                         singleBuild = true;
445                                 } else
446                                         singleBuild = false;
447                                 
448                                 if (assemblyBaseName == null)
449                                         assemblyBaseName = group.NamePrefix + "_";
450                                 
451                                 ct = group.CompilerType;
452                                 attempts = 3;
453                                 while (attempts > 0) {
454                                         abuilder = new AssemblyBuilder (vp, CreateDomProvider (ct), assemblyBaseName);
455                                         abuilder.CompilerOptions = ct.CompilerParameters;
456                                         abuilder.AddAssemblyReference (GetReferencedAssemblies () as List <Assembly>);
457                                         try {
458                                                 GenerateAssembly (abuilder, group, vp, debug);
459                                                 attempts = 0;
460                                         } catch (CompilationException ex) {
461                                                 attempts--;
462                                                 if (singleBuild)
463                                                         throw new HttpException ("Single file build failed.", ex);
464                                                 
465                                                 if (attempts == 0) {
466                                                         needMainVpBuild = true;
467                                                         compilationError = ex;
468                                                         break;
469                                                 }
470                                                 
471                                                 CompilerResults results = ex.Results;
472                                                 if (results == null)
473                                                         throw new HttpException ("No results returned from failed compilation.", ex);
474                                                 else
475                                                         RemoveFailedAssemblies (vpabsolute, ex, abuilder, group, results, debug);
476                                         }
477                                 }
478
479                                 if (needMainVpBuild) {
480                                         // One last attempt - try to build just the requested path
481                                         // if it's not built yet or just return without throwing the
482                                         // exception if it has already been built. 
483                                         if (HasCachedItemNoLock (vpabsolute)) {
484                                                 if (debug)
485                                                         DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
486                                                                                   compilationError, vpabsolute);
487                                                 return;
488                                         };
489
490                                         // This will trigger a recursive build of the requested vp,
491                                         // which means only the vp alone will be built (or not); 
492                                         Build (vp);
493                                         if (HasCachedItemNoLock (vpabsolute)) {
494                                                 if (debug)
495                                                         DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
496                                                                                   compilationError, vpabsolute);
497                                                 return;
498                                         }
499
500                                         // In theory this code is unreachable. If the recursive
501                                         // build of the main vp failed, then it should have thrown
502                                         // the build exception.
503                                         throw new HttpException ("Requested virtual path build failed.", compilationError);
504                                 }
505                         }
506                 }
507                 
508                 static CodeDomProvider CreateDomProvider (CompilerType ct)
509                 {
510                         if (codeDomProviders == null)
511                                 codeDomProviders = new Dictionary <Type, CodeDomProvider> ();
512
513                         Type type = ct.CodeDomProviderType;
514                         if (type == null) {
515                                 CompilationSection cs = CompilationConfig;
516                                 CompilerType tmp = GetDefaultCompilerTypeForLanguage (cs.DefaultLanguage, cs);
517                                 if (tmp != null)
518                                         type = tmp.CodeDomProviderType;
519                         }
520
521                         if (type == null)
522                                 return null;
523                         
524                         CodeDomProvider ret;
525                         if (codeDomProviders.TryGetValue (type, out ret))
526                                 return ret;
527
528                         ret = Activator.CreateInstance (type) as CodeDomProvider;
529                         if (ret == null)
530                                 return null;
531
532                         codeDomProviders.Add (type, ret);
533                         return ret;
534                 }               
535 #if NET_4_0
536                 internal static void CallPreStartMethods ()
537                 {
538                         if (preStartMethodsDone)
539                                 return;
540
541                         preStartMethodsRunning = true;
542                         MethodInfo mi = null;
543                         try {
544                                 List <MethodInfo> methods = LoadPreStartMethodsFromAssemblies (GetReferencedAssemblies () as List <Assembly>);
545                                 if (methods == null || methods.Count == 0)
546                                         return;
547                         
548                                 foreach (MethodInfo m in methods) {
549                                         mi = m;
550                                         m.Invoke (null, null);
551                                 }
552                         } catch (Exception ex) {
553                                 throw new HttpException (
554                                         String.Format ("The pre-application start initialization method {0} on type {1} threw an exception with the following error message: {2}",
555                                                        mi != null ? mi.Name : "UNKNOWN",
556                                                        mi != null ? mi.DeclaringType.FullName : "UNKNOWN",
557                                                        ex.Message),
558                                         ex
559                                 );
560                         } finally {
561                                 preStartMethodsRunning = false;
562                                 preStartMethodsDone = true;
563                         }
564                 }
565
566                 static List <MethodInfo> LoadPreStartMethodsFromAssemblies (List <Assembly> assemblies)
567                 {
568                         if (assemblies == null || assemblies.Count == 0)
569                                 return null;
570
571                         var ret = new List <MethodInfo> ();
572                         object[] attributes;
573                         Type type;
574                         PreApplicationStartMethodAttribute attr;
575                         
576                         foreach (Assembly asm in assemblies) {
577                                 try {
578                                         attributes = asm.GetCustomAttributes (typeof (PreApplicationStartMethodAttribute), false);
579                                         if (attributes == null || attributes.Length == 0)
580                                                 continue;
581
582                                         attr = attributes [0] as PreApplicationStartMethodAttribute;
583                                         type = attr.Type;
584                                         if (type == null)
585                                                 continue;
586                                 } catch {
587                                         continue;
588                                 }
589
590                                 MethodInfo mi;
591                                 Exception error = null;
592                                 try {
593                                         if (type.IsPublic)
594                                                 mi = type.GetMethod (attr.MethodName, BindingFlags.Static | BindingFlags.Public, null, new Type[] {}, null);
595                                         else
596                                                 mi = null;
597                                 } catch (Exception ex) {
598                                         error = ex;
599                                         mi = null;
600                                 }
601
602                                 if (mi == null)
603                                         throw new HttpException (
604                                                 String.Format (
605                                                         "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).",
606                                                         asm.FullName,
607                                                         type.FullName,
608                                                         attr.MethodName),
609                                                 error
610                                         );
611                                 
612                                 ret.Add (mi);
613                         }
614
615                         return ret;
616                 }
617                 
618                 public static Type GetGlobalAsaxType ()
619                 {
620                         Type ret = HttpApplicationFactory.AppType;
621                         if (!preStartMethodsRunning)
622                                 throw new InvalidOperationException ("This method cannot be called during the application's pre-start initialization stage.");
623                         
624                         return ret;
625                 }
626                 
627                 public static Stream CreateCachedFile (string fileName)
628                 {
629                         if (fileName != null && (fileName == String.Empty || fileName.IndexOf (Path.DirectorySeparatorChar) != -1))
630                                 throw new ArgumentException ("Value does not fall within the expected range.");
631
632                         string path = Path.Combine (HttpRuntime.CodegenDir, fileName);
633                         return new FileStream (path, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
634                 }
635
636                 public static Stream ReadCachedFile (string fileName)
637                 {
638                         if (fileName != null && (fileName == String.Empty || fileName.IndexOf (Path.DirectorySeparatorChar) != -1))
639                                 throw new ArgumentException ("Value does not fall within the expected range.");
640
641                         string path = Path.Combine (HttpRuntime.CodegenDir, fileName);
642                         if (!File.Exists (path))
643                                 return null;
644                         
645                         return new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.None);
646                 }
647
648                 [MonoDocumentationNote ("Fully implemented but no info on application pre-init stage is available yet.")]
649                 public static void AddReferencedAssembly (Assembly assembly)
650                 {
651                         if (assembly == null)
652                                 throw new ArgumentNullException ("assembly");
653
654                         Type ret = HttpApplicationFactory.AppType;
655                         if (preStartMethodsDone)
656                                 throw new InvalidOperationException ("This method cannot be called after the application's pre-start initialization stage.");
657
658                         if (dynamicallyRegisteredAssemblies == null)
659                                 dynamicallyRegisteredAssemblies = new List <Assembly> ();
660
661                         if (!dynamicallyRegisteredAssemblies.Contains (assembly))
662                                 dynamicallyRegisteredAssemblies.Add (assembly);
663                 }
664
665                 [MonoTODO ("A no-op until we use IWebObjectFactory internally. Always returns null.")]
666                 public static IWebObjectFactory GetObjectFactory (string virtualPath, bool throwIfNotFound)
667                 {
668                         return null;
669                 }
670 #endif
671                 public static object CreateInstanceFromVirtualPath (string virtualPath, Type requiredBaseType)
672                 {
673                         return CreateInstanceFromVirtualPath (GetAbsoluteVirtualPath (virtualPath), requiredBaseType);
674                 }
675
676                 internal static object CreateInstanceFromVirtualPath (VirtualPath virtualPath, Type requiredBaseType)
677                 {
678                         if (requiredBaseType == null)
679                                 throw new NullReferenceException (); // This is what MS does, but
680                                                                      // from somewhere else.
681                         
682                         Type type = GetCompiledType (virtualPath);
683                         if (type == null)
684                                 return null;
685
686                         if (!requiredBaseType.IsAssignableFrom (type))
687                                 throw new HttpException (500,
688                                                          String.Format ("Type '{0}' does not inherit from '{1}'.", type.FullName, requiredBaseType.FullName));
689
690                         return Activator.CreateInstance (type, null);
691                 }
692                 
693                 static void DescribeCompilationError (string format, CompilationException ex, params object[] parms)
694                 {
695                         StringBuilder sb = new StringBuilder ();
696                         string newline = Environment.NewLine;
697                         
698                         if (parms != null)
699                                 sb.AppendFormat (format + newline, parms);
700                         else
701                                 sb.Append (format + newline);
702
703                         CompilerResults results = ex != null ? ex.Results : null;
704                         if (results == null)
705                                 sb.Append ("No compiler error information present." + newline);
706                         else {
707                                 sb.Append ("Compiler errors:" + newline);
708                                 foreach (CompilerError error in results.Errors)
709                                         sb.Append ("  " + error.ToString () + newline);
710                         }
711
712                         if (ex != null) {
713                                 sb.Append (newline + "Exception thrown:" + newline);
714                                 sb.Append (ex.ToString ());
715                         }
716
717                         ShowDebugModeMessage (sb.ToString ());
718                 }
719                 
720                 static BuildProvider FindBuildProviderForPhysicalPath (string path, BuildProviderGroup group, HttpRequest req)
721                 {
722                         if (req == null || String.IsNullOrEmpty (path))
723                                 return null;
724
725                         foreach (BuildProvider bp in group) {
726                                 if (String.Compare (path, req.MapPath (bp.VirtualPath), RuntimeHelpers.StringComparison) == 0)
727                                         return bp;
728                         }
729                         
730                         return null;
731                 }
732                 
733                 static void GenerateAssembly (AssemblyBuilder abuilder, BuildProviderGroup group, VirtualPath vp, bool debug)
734                 {
735                         IDictionary <string, bool> deps;
736                         BuildManagerCacheItem bmci;
737                         string bvp, vpabsolute = vp.Absolute;
738                         StringBuilder sb;
739                         string newline;
740                         int failedCount = 0;
741                         
742                         if (debug) {
743                                 newline = Environment.NewLine;
744                                 sb = new StringBuilder ("Code generation for certain virtual paths in a batch failed. Those files have been removed from the batch." + newline);
745                                 sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
746                         } else {
747                                 newline = null;
748                                 sb = null;
749                         }
750                         
751                         List <BuildProvider> failedBuildProviders = null;
752                         StringComparison stringComparison = RuntimeHelpers.StringComparison;
753                         foreach (BuildProvider bp in group) {
754                                 bvp = bp.VirtualPath;
755                                 if (HasCachedItemNoLock (bvp))
756                                         continue;
757                                 
758                                 try {
759                                         bp.GenerateCode (abuilder);
760                                 } catch (Exception ex) {
761                                         if (String.Compare (bvp, vpabsolute, stringComparison) == 0) {
762                                                 if (ex is CompilationException || ex is ParseException)
763                                                         throw;
764                                                 
765                                                 throw new HttpException ("Code generation failed.", ex);
766                                         }
767                                         
768                                         if (failedBuildProviders == null)
769                                                 failedBuildProviders = new List <BuildProvider> ();
770                                         failedBuildProviders.Add (bp);
771                                         failedCount++;
772                                         if (sb != null) {
773                                                 if (failedCount > 1)
774                                                         sb.Append (newline);
775                                                 
776                                                 sb.AppendFormat ("Failed file virtual path: {0}; Exception: {1}{2}{1}", bp.VirtualPath, newline, ex);
777                                         }
778                                         continue;
779                                 }
780                                 
781                                 deps = bp.ExtractDependencies ();
782                                 if (deps != null) {
783                                         foreach (var dep in deps) {
784                                                 bmci = GetCachedItemNoLock (dep.Key);
785                                                 if (bmci == null || bmci.BuiltAssembly == null)
786                                                         continue;
787                                                 abuilder.AddAssemblyReference (bmci.BuiltAssembly);
788                                         }
789                                 }
790                         }
791
792                         if (sb != null && failedCount > 0)
793                                 ShowDebugModeMessage (sb.ToString ());
794                         
795                         if (failedBuildProviders != null) {
796                                 foreach (BuildProvider bp in failedBuildProviders)
797                                         group.Remove (bp);
798                         }
799                         
800                         foreach (Assembly asm in referencedAssemblies) {
801                                 if (asm == null)
802                                         continue;
803                                 
804                                 abuilder.AddAssemblyReference (asm);
805                         }
806                         
807                         CompilerResults results  = abuilder.BuildAssembly (vp);
808                         
809                         // No results is not an error - it is possible that the assembly builder contained only .asmx and
810                         // .ashx files which had no body, just the directive. In such case, no code unit or code file is added
811                         // to the assembly builder and, in effect, no assembly is produced but there are STILL types that need
812                         // to be added to the cache.
813                         Assembly compiledAssembly = results != null ? results.CompiledAssembly : null;
814                         bool locked = false;
815                         try {
816 #if SYSTEMCORE_DEP
817                                 buildCacheLock.EnterWriteLock ();
818 #else
819                                 buildCacheLock.AcquireWriterLock (-1);
820 #endif
821                                 locked = true;
822                                 if (compiledAssembly != null)
823                                         referencedAssemblies.Add (compiledAssembly);
824                                 
825                                 foreach (BuildProvider bp in group) {
826                                         if (HasCachedItemNoLock (bp.VirtualPath))
827                                                 continue;
828                                         
829                                         StoreInCache (bp, compiledAssembly, results);
830                                 }
831                         } finally {
832                                 if (locked) {
833 #if SYSTEMCORE_DEP
834                                         buildCacheLock.ExitWriteLock ();
835 #else
836                                         buildCacheLock.ReleaseWriterLock ();
837 #endif
838                                 }
839                         }
840                 }
841                 
842                 static VirtualPath GetAbsoluteVirtualPath (string virtualPath)
843                 {
844                         string vp;
845
846                         if (!VirtualPathUtility.IsRooted (virtualPath)) {
847                                 HttpContext ctx = HttpContext.Current;
848                                 HttpRequest req = ctx != null ? ctx.Request : null;
849                                 
850                                 if (req != null) {
851                                         string fileDir = req.FilePath;
852                                         if (!String.IsNullOrEmpty (fileDir) && String.Compare (fileDir, "/", StringComparison.Ordinal) != 0)
853                                                 fileDir = VirtualPathUtility.GetDirectory (fileDir);
854                                         else
855                                                 fileDir = "/";
856
857                                         vp = VirtualPathUtility.Combine (fileDir, virtualPath);
858                                 } else
859                                         throw new HttpException ("No context, cannot map paths.");
860                         } else
861                                 vp = virtualPath;
862
863                         return new VirtualPath (vp);
864                 }
865
866                 [MonoTODO ("Not implemented, always returns null")]
867                 public static BuildDependencySet GetCachedBuildDependencySet (HttpContext context, string virtualPath)
868                 {
869                         return null; // null is ok here until we store the dependency set in the Cache.
870                 }
871 #if NET_4_0
872                 [MonoTODO ("Not implemented, always returns null")]
873                 public static BuildDependencySet GetCachedBuildDependencySet (HttpContext context, string virtualPath, bool ensureIsUpToDate)
874                 {
875                         return null; // null is ok here until we store the dependency set in the Cache.
876                 }
877 #endif
878                 static BuildManagerCacheItem GetCachedItem (string vp)
879                 {
880                         bool locked = false;
881                         
882                         try {
883 #if SYSTEMCORE_DEP
884                                 buildCacheLock.EnterReadLock ();
885 #else
886                                 buildCacheLock.AcquireReaderLock (-1);
887 #endif
888                                 locked = true;
889                                 return GetCachedItemNoLock (vp);
890                         } finally {
891                                 if (locked) {
892 #if SYSTEMCORE_DEP
893                                         buildCacheLock.ExitReadLock ();
894 #else
895                                         buildCacheLock.ReleaseReaderLock ();
896 #endif
897                                 }
898                         }
899                 }
900
901                 static BuildManagerCacheItem GetCachedItemNoLock (string vp)
902                 {
903                         BuildManagerCacheItem ret;
904                         if (buildCache.TryGetValue (vp, out ret))
905                                 return ret;
906                         
907                         return null;
908                 }
909                 
910                 internal static Type GetCodeDomProviderType (BuildProvider provider)
911                 {
912                         CompilerType codeCompilerType;
913                         Type codeDomProviderType = null;
914
915                         codeCompilerType = provider.CodeCompilerType;
916                         if (codeCompilerType != null)
917                                 codeDomProviderType = codeCompilerType.CodeDomProviderType;
918                                 
919                         if (codeDomProviderType == null)
920                                 throw new HttpException (String.Concat ("Provider '", provider, " 'fails to specify the compiler type."));
921
922                         return codeDomProviderType;
923                 }
924
925                 static Type GetPrecompiledType (string virtualPath)
926                 {
927                         PreCompilationData pc_data;
928                         if (precompiled != null && precompiled.TryGetValue (virtualPath, out pc_data)) {
929                                 if (pc_data.Type == null) {
930                                         pc_data.Type = Type.GetType (pc_data.TypeName + ", " + pc_data.AssemblyFileName, true);
931                                 }
932                                 return pc_data.Type;
933                         }
934                         return null;
935                 }
936
937                 internal static Type GetPrecompiledApplicationType ()
938                 {
939                         if (!is_precompiled)
940                                 return null;
941
942                         Type apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath, "Global.asax"));
943                         if (apptype == null)
944                                 apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath , "global.asax"));
945                         return apptype;
946                 }
947
948                 public static Assembly GetCompiledAssembly (string virtualPath)
949                 {
950                         return GetCompiledAssembly (GetAbsoluteVirtualPath (virtualPath));
951                 }
952
953                 internal static Assembly GetCompiledAssembly (VirtualPath virtualPath)
954                 {
955                         string vpabsolute = virtualPath.Absolute;
956                         if (is_precompiled) {
957                                 Type type = GetPrecompiledType (vpabsolute);
958                                 if (type != null)
959                                         return type.Assembly;
960                         }
961                         BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
962                         if (bmci != null)
963                                 return bmci.BuiltAssembly;
964
965                         Build (virtualPath);
966                         bmci = GetCachedItem (vpabsolute);
967                         if (bmci != null)
968                                 return bmci.BuiltAssembly;
969                         
970                         return null;
971                 }
972                 
973                 public static Type GetCompiledType (string virtualPath)
974                 {
975                         return GetCompiledType (GetAbsoluteVirtualPath (virtualPath));
976                 }
977
978                 internal static Type GetCompiledType (VirtualPath virtualPath)
979                 {
980                         string vpabsolute = virtualPath.Absolute;
981                         if (is_precompiled) {
982                                 Type type = GetPrecompiledType (vpabsolute);
983                                 if (type != null)
984                                         return type;
985                         }
986                         BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
987                         if (bmci != null) {
988                                 ReferenceAssemblyInCompilation (bmci);
989                                 return bmci.Type;
990                         }
991
992                         Build (virtualPath);
993                         bmci = GetCachedItem (vpabsolute);
994                         if (bmci != null) {
995                                 ReferenceAssemblyInCompilation (bmci);
996                                 return bmci.Type;
997                         }
998
999                         return null;
1000                 }
1001
1002                 public static string GetCompiledCustomString (string virtualPath)
1003                 {
1004                         return GetCompiledCustomString (GetAbsoluteVirtualPath (virtualPath));
1005                 }
1006         
1007                 internal static string GetCompiledCustomString (VirtualPath virtualPath) 
1008                 {
1009                         string vpabsolute = virtualPath.Absolute;
1010                         BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
1011                         if (bmci != null)
1012                                 return bmci.CompiledCustomString;
1013                         
1014                         Build (virtualPath);
1015                         bmci = GetCachedItem (vpabsolute);
1016                         if (bmci != null)
1017                                 return bmci.CompiledCustomString;
1018                         
1019                         return null;
1020                 }
1021
1022                 internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection)
1023                 {
1024                         return GetDefaultCompilerTypeForLanguage (language, configSection, true);
1025                 }
1026                 
1027                 internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection, bool throwOnMissing)
1028                 {
1029                         // MS throws when accesing a Hashtable, we do here.
1030                         if (language == null || language.Length == 0)
1031                                 throw new ArgumentNullException ("language");
1032                                 
1033                         CompilationSection config;
1034                         if (configSection == null)
1035                                 config = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
1036                         else
1037                                 config = configSection;
1038                         
1039                         Compiler compiler = config.Compilers.Get (language);
1040                         CompilerParameters p;
1041                         Type type;
1042                         
1043                         if (compiler != null) {
1044                                 type = HttpApplication.LoadType (compiler.Type, true);
1045                                 p = new CompilerParameters ();
1046                                 p.CompilerOptions = compiler.CompilerOptions;
1047                                 p.WarningLevel = compiler.WarningLevel;
1048                                 SetCommonParameters (config, p, type, language);
1049                                 return new CompilerType (type, p);
1050                         }
1051
1052                         if (CodeDomProvider.IsDefinedLanguage (language)) {
1053                                 CompilerInfo info = CodeDomProvider.GetCompilerInfo (language);
1054                                 p = info.CreateDefaultCompilerParameters ();
1055                                 type = info.CodeDomProviderType;
1056                                 SetCommonParameters (config, p, type, language);
1057                                 return new CompilerType (type, p);
1058                         }
1059
1060                         if (throwOnMissing)
1061                                 throw new HttpException (String.Concat ("No compiler for language '", language, "'."));
1062
1063                         return null;
1064                 }
1065
1066                 public static ICollection GetReferencedAssemblies ()
1067                 {
1068                         if (getReferencedAssembliesInvoked)
1069                                 return configReferencedAssemblies;
1070
1071                         if (allowReferencedAssembliesCaching)
1072                                 getReferencedAssembliesInvoked = true;
1073                         
1074                         if (configReferencedAssemblies == null)
1075                                 configReferencedAssemblies = new List <Assembly> ();
1076                         else if (getReferencedAssembliesInvoked)
1077                                 configReferencedAssemblies.Clear ();
1078                         
1079                         CompilationSection compConfig = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
1080                         if (compConfig == null)
1081                                 return configReferencedAssemblies;
1082                         
1083                         bool addAssembliesInBin = false;
1084                         foreach (AssemblyInfo info in compConfig.Assemblies) {
1085                                 if (info.Assembly == "*")
1086                                         addAssembliesInBin = is_precompiled ? false : true;
1087                                 else
1088                                         LoadAssembly (info, configReferencedAssemblies);
1089                         }
1090
1091                         foreach (Assembly topLevelAssembly in TopLevelAssemblies)
1092                                 configReferencedAssemblies.Add (topLevelAssembly);
1093
1094                         foreach (string assLocation in WebConfigurationManager.ExtraAssemblies)
1095                                 LoadAssembly (assLocation, configReferencedAssemblies);
1096 #if NET_4_0
1097                         if (dynamicallyRegisteredAssemblies != null)
1098                                 foreach (Assembly registeredAssembly in dynamicallyRegisteredAssemblies)
1099                                         configReferencedAssemblies.Add (registeredAssembly);
1100 #endif
1101                         // Precompiled sites unconditionally load all assemblies from bin/ (fix for
1102                         // bug #502016)
1103                         if (is_precompiled || addAssembliesInBin) {
1104                                 foreach (string s in HttpApplication.BinDirectoryAssemblies) {
1105                                         try {
1106                                                 LoadAssembly (s, configReferencedAssemblies);
1107                                         } catch (BadImageFormatException) {
1108                                                 // ignore silently
1109                                         }
1110                                 }
1111                         }
1112                                 
1113                         return configReferencedAssemblies;
1114                 }
1115                 
1116                 // The 2 GetType() overloads work on the global.asax, App_GlobalResources, App_WebReferences or App_Browsers
1117                 public static Type GetType (string typeName, bool throwOnError)
1118                 {
1119                         return GetType (typeName, throwOnError, false);
1120                 }
1121
1122                 public static Type GetType (string typeName, bool throwOnError, bool ignoreCase)
1123                 {
1124                         Type ret = null;
1125                         try {
1126                                 foreach (Assembly asm in TopLevel_Assemblies) {
1127                                         ret = asm.GetType (typeName, throwOnError, ignoreCase);
1128                                         if (ret != null)
1129                                                 break;
1130                                 }
1131                         } catch (Exception ex) {
1132                                 throw new HttpException ("Failed to find the specified type.", ex);
1133                         }
1134                         return ret;
1135                 }
1136
1137                 public static ICollection GetVirtualPathDependencies (string virtualPath)
1138                 {
1139                         return GetVirtualPathDependencies (virtualPath, null);
1140                 }
1141
1142                 internal static ICollection GetVirtualPathDependencies (string virtualPath, BuildProvider bprovider)
1143                 {
1144                         BuildProvider provider = bprovider;
1145                         if (provider == null) {
1146                                 CompilationSection cs = CompilationConfig;
1147                                 if (cs == null)
1148                                         return null;
1149                                 provider = BuildManagerDirectoryBuilder.GetBuildProvider (virtualPath, cs.BuildProviders);
1150                         }
1151                         
1152                         if (provider == null)
1153                                 return null;
1154                         
1155                         IDictionary <string, bool> deps =  provider.ExtractDependencies ();
1156                         if (deps == null)
1157                                 return null;
1158
1159                         return (ICollection)deps.Keys;
1160                 }
1161
1162                 internal static bool HasCachedItemNoLock (string vp, out bool entryExists)
1163                 {
1164                         BuildManagerCacheItem item;
1165                         
1166                         if (buildCache.TryGetValue (vp, out item)) {
1167                                 entryExists = true;
1168                                 return item != null;
1169                         }
1170
1171                         entryExists = false;
1172                         return false;
1173                 }
1174                 
1175                 internal static bool HasCachedItemNoLock (string vp)
1176                 {
1177                         bool dummy;
1178                         return HasCachedItemNoLock (vp, out dummy);
1179                 }
1180                 
1181                 internal static bool IgnoreVirtualPath (string virtualPath)
1182                 {
1183                         if (!virtualPathsToIgnoreChecked) {
1184                                 lock (virtualPathsToIgnoreLock) {
1185                                         if (!virtualPathsToIgnoreChecked)
1186                                                 LoadVirtualPathsToIgnore ();
1187                                         virtualPathsToIgnoreChecked = true;
1188                                 }
1189                         }
1190                         
1191                         if (!haveVirtualPathsToIgnore)
1192                                 return false;
1193                         
1194                         if (virtualPathsToIgnore.ContainsKey (virtualPath))
1195                                 return true;
1196                         
1197                         return false;
1198                 }
1199
1200                 static bool IsSingleBuild (VirtualPath vp, bool recursive)
1201                 {
1202                         if (String.Compare (vp.AppRelative, "~/global.asax", StringComparison.OrdinalIgnoreCase) == 0)
1203                                 return true;
1204
1205                         if (!BatchMode)
1206                                 return true;
1207                         
1208                         return recursive;
1209                 }
1210                 
1211                 static void LoadAssembly (string path, List <Assembly> al)
1212                 {
1213                         AddAssembly (Assembly.LoadFrom (path), al);
1214                 }
1215
1216                 static void LoadAssembly (AssemblyInfo info, List <Assembly> al)
1217                 {
1218                         AddAssembly (Assembly.Load (info.Assembly), al);
1219                 }
1220                 
1221                 static void LoadVirtualPathsToIgnore ()
1222                 {
1223                         NameValueCollection appSettings = WebConfigurationManager.AppSettings;
1224                         if (appSettings == null)
1225                                 return;
1226
1227                         string pathsFromConfig = appSettings ["MonoAspnetBatchCompileIgnorePaths"];
1228                         string pathsFromFile = appSettings ["MonoAspnetBatchCompileIgnoreFromFile"];
1229
1230                         if (!String.IsNullOrEmpty (pathsFromConfig)) {
1231                                 string[] paths = pathsFromConfig.Split (virtualPathsToIgnoreSplitChars);
1232                                 string path;
1233                                 
1234                                 foreach (string p in paths) {
1235                                         path = p.Trim ();
1236                                         if (path.Length == 0)
1237                                                 continue;
1238
1239                                         AddPathToIgnore (path);
1240                                 }
1241                         }
1242
1243                         if (!String.IsNullOrEmpty (pathsFromFile)) {
1244                                 string realpath;
1245                                 HttpContext ctx = HttpContext.Current;
1246                                 HttpRequest req = ctx != null ? ctx.Request : null;
1247
1248                                 if (req == null)
1249                                         throw new HttpException ("Missing context, cannot continue.");
1250
1251                                 realpath = req.MapPath (pathsFromFile);
1252                                 if (!File.Exists (realpath))
1253                                         return;
1254
1255                                 string[] paths = File.ReadAllLines (realpath);
1256                                 if (paths == null || paths.Length == 0)
1257                                         return;
1258
1259                                 string path;
1260                                 foreach (string p in paths) {
1261                                         path = p.Trim ();
1262                                         if (path.Length == 0)
1263                                                 continue;
1264
1265                                         AddPathToIgnore (path);
1266                                 }
1267                         }
1268                 }
1269
1270                 static void OnEntryRemoved (string vp)
1271                 {
1272                         BuildManagerRemoveEntryEventHandler eh = events [buildManagerRemoveEntryEvent] as BuildManagerRemoveEntryEventHandler;
1273
1274                         if (eh != null)
1275                                 eh (new BuildManagerRemoveEntryEventArgs (vp, HttpContext.Current));
1276                 }
1277                 
1278                 static void OnVirtualPathChanged (string key, object value, CacheItemRemovedReason removedReason)
1279                 {
1280                         string virtualPath;
1281
1282                         if (StrUtils.StartsWith (key, BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX))
1283                                 virtualPath = key.Substring (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH);
1284                         else
1285                                 return;
1286                         
1287                         bool locked = false;
1288                         try {
1289 #if SYSTEMCORE_DEP
1290                                 buildCacheLock.EnterWriteLock ();
1291 #else
1292                                 buildCacheLock.AcquireWriterLock (-1);
1293 #endif
1294                                 locked = true;
1295
1296                                 if (HasCachedItemNoLock (virtualPath)) {
1297                                         buildCache [virtualPath] = null;
1298                                         OnEntryRemoved (virtualPath);
1299                                 }
1300                         } finally {
1301                                 if (locked) {
1302 #if SYSTEMCORE_DEP
1303                                         buildCacheLock.ExitWriteLock ();
1304 #else
1305                                         buildCacheLock.ReleaseWriterLock ();
1306 #endif
1307                                 }
1308                         }
1309                 }
1310                 
1311                 static void ReferenceAssemblyInCompilation (BuildManagerCacheItem bmci)
1312                 {
1313                         if (recursionDepth == 0 || referencedAssemblies.Contains (bmci.BuiltAssembly))
1314                                 return;
1315
1316                         referencedAssemblies.Add (bmci.BuiltAssembly);
1317                 }
1318                 
1319                 static void RemoveFailedAssemblies (string requestedVirtualPath, CompilationException ex, AssemblyBuilder abuilder,
1320                                                     BuildProviderGroup group, CompilerResults results, bool debug)
1321                 {
1322                         StringBuilder sb;
1323                         string newline;
1324                         
1325                         if (debug) {
1326                                 newline = Environment.NewLine;
1327                                 sb = new StringBuilder ("Compilation of certain files in a batch failed. Another attempt to compile the batch will be made." + newline);
1328                                 sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
1329                         } else {
1330                                 newline = null;
1331                                 sb = null;
1332                         }
1333                         
1334                         var failedBuildProviders = new List <BuildProvider> ();
1335                         BuildProvider bp;
1336                         HttpContext ctx = HttpContext.Current;
1337                         HttpRequest req = ctx != null ? ctx.Request : null;
1338                         bool rethrow = false;
1339                         
1340                         foreach (CompilerError error in results.Errors) {
1341                                 if (error.IsWarning)
1342                                         continue;
1343                                 
1344                                 bp = abuilder.GetBuildProviderForPhysicalFilePath (error.FileName);
1345                                 if (bp == null) {
1346                                         bp = FindBuildProviderForPhysicalPath (error.FileName, group, req);
1347                                         if (bp == null)
1348                                                 continue;
1349                                 }
1350
1351                                 if (String.Compare (bp.VirtualPath, requestedVirtualPath, StringComparison.Ordinal) == 0)
1352                                         rethrow = true;
1353
1354                                 if (!failedBuildProviders.Contains (bp)) {
1355                                         failedBuildProviders.Add (bp);
1356                                         if (sb != null)
1357                                                 sb.AppendFormat ("\t{0}{1}", bp.VirtualPath, newline);
1358                                 }
1359
1360                                 if (sb != null)
1361                                         sb.AppendFormat ("\t\t{0}{1}", error, newline);
1362                         }
1363
1364                         foreach (BuildProvider fbp in failedBuildProviders)
1365                                 group.Remove (fbp);
1366                         
1367                         if (sb != null) {
1368                                 sb.AppendFormat ("{0}The following exception has been thrown for the file(s) listed above:{0}{1}",
1369                                                  newline, ex.ToString ());
1370                                 ShowDebugModeMessage (sb.ToString ());
1371                                 sb = null;
1372                         }
1373
1374                         if (rethrow)
1375                                 throw new HttpException ("Compilation failed.", ex);
1376                 }
1377                 
1378                 static void SetCommonParameters (CompilationSection config, CompilerParameters p, Type compilerType, string language)
1379                 {
1380                         p.IncludeDebugInformation = config.Debug;
1381                         MonoSettingsSection mss = WebConfigurationManager.GetSection ("system.web/monoSettings") as MonoSettingsSection;
1382                         if (mss == null || !mss.UseCompilersCompatibility)
1383                                 return;
1384
1385                         Compiler compiler = mss.CompilersCompatibility.Get (language);
1386                         if (compiler == null)
1387                                 return;
1388
1389                         Type type = HttpApplication.LoadType (compiler.Type, false);
1390                         if (type != compilerType)
1391                                 return;
1392
1393                         p.CompilerOptions = String.Concat (p.CompilerOptions, " ", compiler.CompilerOptions);
1394                 }
1395
1396                 static void ShowDebugModeMessage (string msg)
1397                 {
1398                         if (suppressDebugModeMessages)
1399                                 return;
1400                         
1401                         Console.WriteLine ();
1402                         Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
1403                         Console.WriteLine (msg);
1404                         Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
1405                         Console.WriteLine ();
1406                 }
1407
1408                 static void StoreInCache (BuildProvider bp, Assembly compiledAssembly, CompilerResults results)
1409                 {
1410                         string virtualPath = bp.VirtualPath;
1411                         var item = new BuildManagerCacheItem (compiledAssembly, bp, results);
1412                         
1413                         if (buildCache.ContainsKey (virtualPath))
1414                                 buildCache [virtualPath] = item;
1415                         else
1416                                 buildCache.Add (virtualPath, item);
1417                         
1418                         HttpContext ctx = HttpContext.Current;
1419                         HttpRequest req = ctx != null ? ctx.Request : null;
1420                         CacheDependency dep;
1421                         
1422                         if (req != null) {
1423                                 IDictionary <string, bool> deps = bp.ExtractDependencies ();
1424                                 var files = new List <string> ();
1425                                 string physicalPath;
1426
1427                                 physicalPath = req.MapPath (virtualPath);
1428                                 if (File.Exists (physicalPath))
1429                                         files.Add (physicalPath);
1430                                 
1431                                 if (deps != null && deps.Count > 0) {
1432                                         foreach (var d in deps) {
1433                                                 physicalPath = req.MapPath (d.Key);
1434                                                 if (!File.Exists (physicalPath))
1435                                                         continue;
1436                                                 if (!files.Contains (physicalPath))
1437                                                         files.Add (physicalPath);
1438                                         }
1439                                 }
1440
1441                                 dep = new CacheDependency (files.ToArray ());
1442                         } else
1443                                 dep = null;
1444
1445                         HttpRuntime.InternalCache.Add (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX + virtualPath,
1446                                                        true,
1447                                                        dep,
1448                                                        Cache.NoAbsoluteExpiration,
1449                                                        Cache.NoSlidingExpiration,
1450                                                        CacheItemPriority.High,
1451                                                        new CacheItemRemovedCallback (OnVirtualPathChanged));
1452                 }
1453         }
1454 }
1455