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;
235 // 1 -> app_code subfolder - add the assembly to CodeAssemblies
238 // 6 -> app_code - add the assembly to CodeAssemblies
240 // 9 -> App_GlobalResources - set the assembly for HttpContext
241 if (reader.Name == "resultType" && (val == "2" || val == "3" || val == "8"))
242 LoadPageData (reader, true);
243 else if (val == "1" || val == "6") {
244 PreCompilationData pd = LoadPageData (reader, false);
245 CodeAssemblies.Add (Assembly.Load (pd.AssemblyFileName));
246 } else if (val == "9") {
247 PreCompilationData pd = LoadPageData (reader, false);
248 HttpContext.AppGlobalResourcesAssembly = Assembly.Load (pd.AssemblyFileName);
254 class PreCompilationData {
255 public string VirtualPath;
256 public string AssemblyFileName;
257 public string TypeName;
261 static PreCompilationData LoadPageData (XmlTextReader reader, bool store)
263 PreCompilationData pc_data = new PreCompilationData ();
265 while (reader.MoveToNextAttribute ()) {
266 string name = reader.Name;
267 if (name == "virtualPath")
268 pc_data.VirtualPath = VirtualPathUtility.RemoveTrailingSlash (reader.Value);
269 else if (name == "assembly")
270 pc_data.AssemblyFileName = reader.Value;
271 else if (name == "type")
272 pc_data.TypeName = reader.Value;
275 if (precompiled == null)
276 precompiled = new Dictionary<string, PreCompilationData> (comparer);
277 precompiled.Add (pc_data.VirtualPath, pc_data);
282 static void AddAssembly (Assembly asm, List <Assembly> al)
284 if (al.Contains (asm))
290 static void AddPathToIgnore (string vp)
292 if (virtualPathsToIgnore == null)
293 virtualPathsToIgnore = new Dictionary <string, bool> (comparer);
295 VirtualPath path = GetAbsoluteVirtualPath (vp);
296 string vpAbsolute = path.Absolute;
297 if (!virtualPathsToIgnore.ContainsKey (vpAbsolute)) {
298 virtualPathsToIgnore.Add (vpAbsolute, true);
299 haveVirtualPathsToIgnore = true;
302 string vpRelative = path.AppRelative;
303 if (!virtualPathsToIgnore.ContainsKey (vpRelative)) {
304 virtualPathsToIgnore.Add (vpRelative, true);
305 haveVirtualPathsToIgnore = true;
308 if (!virtualPathsToIgnore.ContainsKey (vp)) {
309 virtualPathsToIgnore.Add (vp, true);
310 haveVirtualPathsToIgnore = true;
314 internal static void AddToReferencedAssemblies (Assembly asm)
316 // should not be used
319 static void AssertVirtualPathExists (VirtualPath virtualPath)
322 bool dothrow = false;
324 if (virtualPath.IsFake) {
325 realpath = virtualPath.PhysicalPath;
326 if (!File.Exists (realpath) && !Directory.Exists (realpath))
329 VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
330 string vpAbsolute = virtualPath.Absolute;
332 if (!vpp.FileExists (vpAbsolute) && !vpp.DirectoryExists (vpAbsolute))
337 throw new HttpException (404, "The file '" + virtualPath + "' does not exist.", virtualPath.Absolute);
340 static void Build (VirtualPath vp)
342 AssertVirtualPathExists (vp);
344 CompilationSection cs = CompilationConfig;
345 lock (bigCompilationLock) {
346 if (HasCachedItemNoLock (vp.Absolute))
349 if (recursionDepth == 0)
350 referencedAssemblies.Clear ();
354 BuildInner (vp, cs != null ? cs.Debug : false);
355 if (recursionDepth <= 1)
358 // See http://support.microsoft.com/kb/319947
359 if (buildCount > cs.NumRecompilesBeforeAppRestart)
360 HttpRuntime.UnloadAppDomain ();
367 // This method assumes it is being called with the big compilation lock held
368 static void BuildInner (VirtualPath vp, bool debug)
370 var builder = new BuildManagerDirectoryBuilder (vp);
371 bool recursive = recursionDepth > 1;
372 List <BuildProviderGroup> builderGroups = builder.Build (IsSingleBuild (vp, recursive));
373 if (builderGroups == null)
376 string vpabsolute = vp.Absolute;
377 int buildHash = (vpabsolute.GetHashCode () | (int)DateTime.Now.Ticks) + (int)recursionDepth;
378 string assemblyBaseName;
379 AssemblyBuilder abuilder;
382 bool singleBuild, needMainVpBuild;
383 CompilationException compilationError;
385 // Each group becomes a separate assembly.
386 foreach (BuildProviderGroup group in builderGroups) {
387 needMainVpBuild = false;
388 compilationError = null;
389 assemblyBaseName = null;
391 if (group.Count == 1) {
392 if (recursive || !group.Master)
393 assemblyBaseName = String.Format ("{0}_{1}.{2:x}.", group.NamePrefix, VirtualPathUtility.GetFileName (group [0].VirtualPath), buildHash);
398 if (assemblyBaseName == null)
399 assemblyBaseName = group.NamePrefix + "_";
401 ct = group.CompilerType;
403 while (attempts > 0) {
404 abuilder = new AssemblyBuilder (vp, CreateDomProvider (ct), assemblyBaseName);
405 abuilder.CompilerOptions = ct.CompilerParameters;
406 abuilder.AddAssemblyReference (GetReferencedAssemblies () as List <Assembly>);
408 GenerateAssembly (abuilder, group, vp, debug);
410 } catch (CompilationException ex) {
413 throw new HttpException ("Single file build failed.", ex);
416 needMainVpBuild = true;
417 compilationError = ex;
421 CompilerResults results = ex.Results;
423 throw new HttpException ("No results returned from failed compilation.", ex);
425 RemoveFailedAssemblies (vpabsolute, ex, abuilder, group, results, debug);
429 if (needMainVpBuild) {
430 // One last attempt - try to build just the requested path
431 // if it's not built yet or just return without throwing the
432 // exception if it has already been built.
433 if (HasCachedItemNoLock (vpabsolute)) {
435 DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
436 compilationError, vpabsolute);
440 // This will trigger a recursive build of the requested vp,
441 // which means only the vp alone will be built (or not);
443 if (HasCachedItemNoLock (vpabsolute)) {
445 DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
446 compilationError, vpabsolute);
450 // In theory this code is unreachable. If the recursive
451 // build of the main vp failed, then it should have thrown
452 // the build exception.
453 throw new HttpException ("Requested virtual path build failed.", compilationError);
458 static CodeDomProvider CreateDomProvider (CompilerType ct)
460 if (codeDomProviders == null)
461 codeDomProviders = new Dictionary <Type, CodeDomProvider> ();
463 Type type = ct.CodeDomProviderType;
465 CompilationSection cs = CompilationConfig;
466 CompilerType tmp = GetDefaultCompilerTypeForLanguage (cs.DefaultLanguage, cs);
468 type = tmp.CodeDomProviderType;
475 if (codeDomProviders.TryGetValue (type, out ret))
478 ret = Activator.CreateInstance (type) as CodeDomProvider;
482 codeDomProviders.Add (type, ret);
486 public static object CreateInstanceFromVirtualPath (string virtualPath, Type requiredBaseType)
488 return CreateInstanceFromVirtualPath (GetAbsoluteVirtualPath (virtualPath), requiredBaseType);
491 internal static object CreateInstanceFromVirtualPath (VirtualPath virtualPath, Type requiredBaseType)
493 if (requiredBaseType == null)
494 throw new NullReferenceException (); // This is what MS does, but
495 // from somewhere else.
497 Type type = GetCompiledType (virtualPath);
501 if (!requiredBaseType.IsAssignableFrom (type))
502 throw new HttpException (500,
503 String.Format ("Type '{0}' does not inherit from '{1}'.", type.FullName, requiredBaseType.FullName));
505 return Activator.CreateInstance (type, null);
508 static void DescribeCompilationError (string format, CompilationException ex, params object[] parms)
510 StringBuilder sb = new StringBuilder ();
511 string newline = Environment.NewLine;
514 sb.AppendFormat (format + newline, parms);
516 sb.Append (format + newline);
518 CompilerResults results = ex != null ? ex.Results : null;
520 sb.Append ("No compiler error information present." + newline);
522 sb.Append ("Compiler errors:" + newline);
523 foreach (CompilerError error in results.Errors)
524 sb.Append (" " + error.ToString () + newline);
528 sb.Append (newline + "Exception thrown:" + newline);
529 sb.Append (ex.ToString ());
532 ShowDebugModeMessage (sb.ToString ());
535 static BuildProvider FindBuildProviderForPhysicalPath (string path, BuildProviderGroup group, HttpRequest req)
537 if (req == null || String.IsNullOrEmpty (path))
540 foreach (BuildProvider bp in group) {
541 if (String.Compare (path, req.MapPath (bp.VirtualPath), stringComparer) == 0)
548 static void GenerateAssembly (AssemblyBuilder abuilder, BuildProviderGroup group, VirtualPath vp, bool debug)
550 IDictionary <string, bool> deps;
551 BuildManagerCacheItem bmci;
552 string bvp, vpabsolute = vp.Absolute;
558 newline = Environment.NewLine;
559 sb = new StringBuilder ("Code generation for certain virtual paths in a batch failed. Those files have been removed from the batch." + newline);
560 sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
566 List <BuildProvider> failedBuildProviders = null;
567 foreach (BuildProvider bp in group) {
568 bvp = bp.VirtualPath;
569 if (HasCachedItemNoLock (bvp))
573 bp.GenerateCode (abuilder);
574 } catch (Exception ex) {
575 if (String.Compare (bvp, vpabsolute, stringComparer) == 0) {
576 if (ex is CompilationException || ex is ParseException)
579 throw new HttpException ("Code generation failed.", ex);
582 if (failedBuildProviders == null)
583 failedBuildProviders = new List <BuildProvider> ();
584 failedBuildProviders.Add (bp);
590 sb.AppendFormat ("Failed file virtual path: {0}; Exception: {1}{2}{1}", bp.VirtualPath, newline, ex);
595 deps = bp.ExtractDependencies ();
597 foreach (var dep in deps) {
598 bmci = GetCachedItemNoLock (dep.Key);
599 if (bmci == null || bmci.BuiltAssembly == null)
601 abuilder.AddAssemblyReference (bmci.BuiltAssembly);
606 if (sb != null && failedCount > 0)
607 ShowDebugModeMessage (sb.ToString ());
609 if (failedBuildProviders != null) {
610 foreach (BuildProvider bp in failedBuildProviders)
614 foreach (Assembly asm in referencedAssemblies) {
618 abuilder.AddAssemblyReference (asm);
621 CompilerResults results = abuilder.BuildAssembly (vp);
623 // No results is not an error - it is possible that the assembly builder contained only .asmx and
624 // .ashx files which had no body, just the directive. In such case, no code unit or code file is added
625 // to the assembly builder and, in effect, no assembly is produced but there are STILL types that need
626 // to be added to the cache.
627 Assembly compiledAssembly = results != null ? results.CompiledAssembly : null;
631 buildCacheLock.EnterWriteLock ();
633 buildCacheLock.AcquireWriterLock (-1);
636 if (compiledAssembly != null)
637 referencedAssemblies.Add (compiledAssembly);
639 foreach (BuildProvider bp in group) {
640 if (HasCachedItemNoLock (bp.VirtualPath))
643 StoreInCache (bp, compiledAssembly, results);
648 buildCacheLock.ExitWriteLock ();
650 buildCacheLock.ReleaseWriterLock ();
656 static VirtualPath GetAbsoluteVirtualPath (string virtualPath)
660 if (!VirtualPathUtility.IsRooted (virtualPath)) {
661 HttpContext ctx = HttpContext.Current;
662 HttpRequest req = ctx != null ? ctx.Request : null;
665 string fileDir = req.FilePath;
666 if (!String.IsNullOrEmpty (fileDir) && String.Compare (fileDir, "/", StringComparison.Ordinal) != 0)
667 fileDir = VirtualPathUtility.GetDirectory (fileDir);
671 vp = VirtualPathUtility.Combine (fileDir, virtualPath);
673 throw new HttpException ("No context, cannot map paths.");
677 return new VirtualPath (vp);
680 [MonoTODO ("Not implemented, always returns null")]
681 public static BuildDependencySet GetCachedBuildDependencySet (HttpContext context, string virtualPath)
683 return null; // null is ok here until we store the dependency set in the Cache.
686 static BuildManagerCacheItem GetCachedItem (string vp)
692 buildCacheLock.EnterReadLock ();
694 buildCacheLock.AcquireReaderLock (-1);
697 return GetCachedItemNoLock (vp);
701 buildCacheLock.ExitReadLock ();
703 buildCacheLock.ReleaseReaderLock ();
709 static BuildManagerCacheItem GetCachedItemNoLock (string vp)
711 BuildManagerCacheItem ret;
712 if (buildCache.TryGetValue (vp, out ret))
718 internal static Type GetCodeDomProviderType (BuildProvider provider)
720 CompilerType codeCompilerType;
721 Type codeDomProviderType = null;
723 codeCompilerType = provider.CodeCompilerType;
724 if (codeCompilerType != null)
725 codeDomProviderType = codeCompilerType.CodeDomProviderType;
727 if (codeDomProviderType == null)
728 throw new HttpException (String.Concat ("Provider '", provider, " 'fails to specify the compiler type."));
730 return codeDomProviderType;
733 static Type GetPrecompiledType (string virtualPath)
735 PreCompilationData pc_data;
736 if (precompiled != null && precompiled.TryGetValue (virtualPath, out pc_data)) {
737 if (pc_data.Type == null) {
738 pc_data.Type = Type.GetType (pc_data.TypeName + ", " + pc_data.AssemblyFileName, true);
745 internal static Type GetPrecompiledApplicationType ()
750 Type apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath, "Global.asax"));
752 apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath , "global.asax"));
756 public static Assembly GetCompiledAssembly (string virtualPath)
758 return GetCompiledAssembly (GetAbsoluteVirtualPath (virtualPath));
761 internal static Assembly GetCompiledAssembly (VirtualPath virtualPath)
763 string vpabsolute = virtualPath.Absolute;
764 if (is_precompiled) {
765 Type type = GetPrecompiledType (vpabsolute);
767 return type.Assembly;
769 BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
771 return bmci.BuiltAssembly;
774 bmci = GetCachedItem (vpabsolute);
776 return bmci.BuiltAssembly;
781 public static Type GetCompiledType (string virtualPath)
783 return GetCompiledType (GetAbsoluteVirtualPath (virtualPath));
786 internal static Type GetCompiledType (VirtualPath virtualPath)
788 string vpabsolute = virtualPath.Absolute;
789 if (is_precompiled) {
790 Type type = GetPrecompiledType (vpabsolute);
794 BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
796 ReferenceAssemblyInCompilation (bmci);
801 bmci = GetCachedItem (vpabsolute);
803 ReferenceAssemblyInCompilation (bmci);
810 public static string GetCompiledCustomString (string virtualPath)
812 return GetCompiledCustomString (GetAbsoluteVirtualPath (virtualPath));
815 internal static string GetCompiledCustomString (VirtualPath virtualPath)
817 string vpabsolute = virtualPath.Absolute;
818 BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
820 return bmci.CompiledCustomString;
823 bmci = GetCachedItem (vpabsolute);
825 return bmci.CompiledCustomString;
830 internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection)
832 return GetDefaultCompilerTypeForLanguage (language, configSection, true);
835 internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection, bool throwOnMissing)
837 // MS throws when accesing a Hashtable, we do here.
838 if (language == null || language.Length == 0)
839 throw new ArgumentNullException ("language");
841 CompilationSection config;
842 if (configSection == null)
843 config = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
845 config = configSection;
847 Compiler compiler = config.Compilers.Get (language);
848 CompilerParameters p;
851 if (compiler != null) {
852 type = HttpApplication.LoadType (compiler.Type, true);
853 p = new CompilerParameters ();
854 p.CompilerOptions = compiler.CompilerOptions;
855 p.WarningLevel = compiler.WarningLevel;
856 SetCommonParameters (config, p, type, language);
857 return new CompilerType (type, p);
860 if (CodeDomProvider.IsDefinedLanguage (language)) {
861 CompilerInfo info = CodeDomProvider.GetCompilerInfo (language);
862 p = info.CreateDefaultCompilerParameters ();
863 type = info.CodeDomProviderType;
864 SetCommonParameters (config, p, type, language);
865 return new CompilerType (type, p);
869 throw new HttpException (String.Concat ("No compiler for language '", language, "'."));
874 public static ICollection GetReferencedAssemblies ()
876 List <Assembly> al = new List <Assembly> ();
878 CompilationSection compConfig = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
879 if (compConfig == null)
882 bool addAssembliesInBin = false;
883 foreach (AssemblyInfo info in compConfig.Assemblies) {
884 if (info.Assembly == "*")
885 addAssembliesInBin = is_precompiled ? false : true;
887 LoadAssembly (info, al);
890 foreach (Assembly topLevelAssembly in TopLevelAssemblies)
891 al.Add (topLevelAssembly);
893 foreach (string assLocation in WebConfigurationManager.ExtraAssemblies)
894 LoadAssembly (assLocation, al);
897 // Precompiled sites unconditionally load all assemblies from bin/ (fix for
899 if (is_precompiled || addAssembliesInBin) {
900 foreach (string s in HttpApplication.BinDirectoryAssemblies) {
902 LoadAssembly (s, al);
903 } catch (BadImageFormatException) {
912 // The 2 GetType() overloads work on the global.asax, App_GlobalResources, App_WebReferences or App_Browsers
913 public static Type GetType (string typeName, bool throwOnError)
915 return GetType (typeName, throwOnError, false);
918 public static Type GetType (string typeName, bool throwOnError, bool ignoreCase)
922 foreach (Assembly asm in TopLevel_Assemblies) {
923 ret = asm.GetType (typeName, throwOnError, ignoreCase);
927 } catch (Exception ex) {
928 throw new HttpException ("Failed to find the specified type.", ex);
933 public static IDictionary <string, bool> GetVirtualPathDependencies (string virtualPath)
935 return GetVirtualPathDependencies (virtualPath, null);
938 internal static IDictionary <string, bool> GetVirtualPathDependencies (string virtualPath, BuildProvider bprovider)
940 BuildProvider provider = bprovider;
941 if (provider == null) {
942 CompilationSection cs = CompilationConfig;
945 provider = BuildManagerDirectoryBuilder.GetBuildProvider (virtualPath, cs.BuildProviders);
948 if (provider == null)
951 return provider.ExtractDependencies ();
954 internal static bool HasCachedItemNoLock (string vp)
956 return buildCache.ContainsKey (vp);
959 internal static bool IgnoreVirtualPath (string virtualPath)
961 if (!haveVirtualPathsToIgnore)
964 if (virtualPathsToIgnore.ContainsKey (virtualPath))
970 static bool IsSingleBuild (VirtualPath vp, bool recursive)
972 if (String.Compare (vp.AppRelative, "~/global.asax", StringComparison.OrdinalIgnoreCase) == 0)
981 static void LoadAssembly (string path, List <Assembly> al)
983 AddAssembly (Assembly.LoadFrom (path), al);
986 static void LoadAssembly (AssemblyInfo info, List <Assembly> al)
988 AddAssembly (Assembly.Load (info.Assembly), al);
991 static void LoadVirtualPathsToIgnore ()
993 NameValueCollection appSettings = WebConfigurationManager.AppSettings;
994 if (appSettings == null)
997 string pathsFromConfig = appSettings ["MonoAspnetBatchCompileIgnorePaths"];
998 string pathsFromFile = appSettings ["MonoAspnetBatchCompileIgnoreFromFile"];
1000 if (!String.IsNullOrEmpty (pathsFromConfig)) {
1001 string[] paths = pathsFromConfig.Split (virtualPathsToIgnoreSplitChars);
1004 foreach (string p in paths) {
1006 if (path.Length == 0)
1009 AddPathToIgnore (path);
1013 if (!String.IsNullOrEmpty (pathsFromFile)) {
1015 HttpContext ctx = HttpContext.Current;
1016 HttpRequest req = ctx != null ? ctx.Request : null;
1019 throw new HttpException ("Missing context, cannot continue.");
1021 realpath = req.MapPath (pathsFromFile);
1022 if (!File.Exists (realpath))
1025 string[] paths = File.ReadAllLines (realpath);
1026 if (paths == null || paths.Length == 0)
1030 foreach (string p in paths) {
1032 if (path.Length == 0)
1035 AddPathToIgnore (path);
1040 static void OnEntryRemoved (string vp)
1042 BuildManagerRemoveEntryEventHandler eh = events [buildManagerRemoveEntryEvent] as BuildManagerRemoveEntryEventHandler;
1045 eh (new BuildManagerRemoveEntryEventArgs (vp, HttpContext.Current));
1048 static void OnVirtualPathChanged (string key, object value, CacheItemRemovedReason removedReason)
1052 if (StrUtils.StartsWith (key, BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX))
1053 virtualPath = key.Substring (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH);
1057 bool locked = false;
1060 buildCacheLock.EnterWriteLock ();
1062 buildCacheLock.AcquireWriterLock (-1);
1066 if (HasCachedItemNoLock (virtualPath)) {
1067 buildCache.Remove (virtualPath);
1068 OnEntryRemoved (virtualPath);
1073 buildCacheLock.ExitWriteLock ();
1075 buildCacheLock.ReleaseWriterLock ();
1081 static void ReferenceAssemblyInCompilation (BuildManagerCacheItem bmci)
1083 if (recursionDepth == 0 || referencedAssemblies.Contains (bmci.BuiltAssembly))
1086 referencedAssemblies.Add (bmci.BuiltAssembly);
1089 static void RemoveFailedAssemblies (string requestedVirtualPath, CompilationException ex, AssemblyBuilder abuilder,
1090 BuildProviderGroup group, CompilerResults results, bool debug)
1096 newline = Environment.NewLine;
1097 sb = new StringBuilder ("Compilation of certain files in a batch failed. Another attempt to compile the batch will be made." + newline);
1098 sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
1104 var failedBuildProviders = new List <BuildProvider> ();
1106 HttpContext ctx = HttpContext.Current;
1107 HttpRequest req = ctx != null ? ctx.Request : null;
1108 bool rethrow = false;
1110 foreach (CompilerError error in results.Errors) {
1111 if (error.IsWarning)
1114 bp = abuilder.GetBuildProviderForPhysicalFilePath (error.FileName);
1116 bp = FindBuildProviderForPhysicalPath (error.FileName, group, req);
1121 if (String.Compare (bp.VirtualPath, requestedVirtualPath, StringComparison.Ordinal) == 0)
1124 if (!failedBuildProviders.Contains (bp)) {
1125 failedBuildProviders.Add (bp);
1127 sb.AppendFormat ("\t{0}{1}", bp.VirtualPath, newline);
1131 sb.AppendFormat ("\t\t{0}{1}", error, newline);
1134 foreach (BuildProvider fbp in failedBuildProviders)
1138 sb.AppendFormat ("{0}The following exception has been thrown for the file(s) listed above:{0}{1}",
1139 newline, ex.ToString ());
1140 ShowDebugModeMessage (sb.ToString ());
1145 throw new HttpException ("Compilation failed.", ex);
1148 static void SetCommonParameters (CompilationSection config, CompilerParameters p, Type compilerType, string language)
1150 p.IncludeDebugInformation = config.Debug;
1151 MonoSettingsSection mss = WebConfigurationManager.GetSection ("system.web/monoSettings") as MonoSettingsSection;
1152 if (mss == null || !mss.UseCompilersCompatibility)
1155 Compiler compiler = mss.CompilersCompatibility.Get (language);
1156 if (compiler == null)
1159 Type type = HttpApplication.LoadType (compiler.Type, false);
1160 if (type != compilerType)
1163 p.CompilerOptions = String.Concat (p.CompilerOptions, " ", compiler.CompilerOptions);
1166 static void ShowDebugModeMessage (string msg)
1168 if (suppressDebugModeMessages)
1171 Console.WriteLine ();
1172 Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
1173 Console.WriteLine (msg);
1174 Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
1175 Console.WriteLine ();
1178 static void StoreInCache (BuildProvider bp, Assembly compiledAssembly, CompilerResults results)
1180 buildCache.Add (bp.VirtualPath, new BuildManagerCacheItem (compiledAssembly, bp, results));
1182 HttpContext ctx = HttpContext.Current;
1183 HttpRequest req = ctx != null ? ctx.Request : null;
1184 CacheDependency dep;
1185 string virtualPath = bp.VirtualPath;
1188 IDictionary <string, bool> deps = bp.ExtractDependencies ();
1189 var files = new List <string> ();
1190 string physicalPath;
1192 physicalPath = req.MapPath (virtualPath);
1193 if (File.Exists (physicalPath))
1194 files.Add (physicalPath);
1196 if (deps != null && deps.Count > 0) {
1197 foreach (var d in deps) {
1198 physicalPath = req.MapPath (d.Key);
1199 if (!File.Exists (physicalPath))
1201 if (!files.Contains (physicalPath))
1202 files.Add (physicalPath);
1206 dep = new CacheDependency (files.ToArray ());
1210 HttpRuntime.InternalCache.Add (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX + virtualPath,
1213 Cache.NoAbsoluteExpiration,
1214 Cache.NoSlidingExpiration,
1215 CacheItemPriority.High,
1216 new CacheItemRemovedCallback (OnVirtualPathChanged));