BuildManager.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266
  1. //
  2. // System.Web.Compilation.BuildManager
  3. //
  4. // Authors:
  5. // Chris Toshok ([email protected])
  6. // Gonzalo Paniagua Javier ([email protected])
  7. // Marek Habersack ([email protected])
  8. //
  9. // (C) 2006-2009 Novell, Inc (http://www.novell.com)
  10. //
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. using System;
  32. using System.CodeDom;
  33. using System.CodeDom.Compiler;
  34. using System.Collections;
  35. using System.Collections.Generic;
  36. using System.Collections.Specialized;
  37. using System.ComponentModel;
  38. using System.IO;
  39. using System.Reflection;
  40. using System.Text;
  41. using System.Threading;
  42. using System.Xml;
  43. using System.Web;
  44. using System.Web.Caching;
  45. using System.Web.Configuration;
  46. using System.Web.Hosting;
  47. using System.Web.Util;
  48. namespace System.Web.Compilation
  49. {
  50. public sealed class BuildManager
  51. {
  52. internal const string FAKE_VIRTUAL_PATH_PREFIX = "/@@MonoFakeVirtualPath@@";
  53. const string BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX = "@@Build_Manager@@";
  54. static int BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH = BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX.Length;
  55. static readonly object bigCompilationLock = new object ();
  56. static readonly object virtualPathsToIgnoreLock = new object ();
  57. static readonly char[] virtualPathsToIgnoreSplitChars = {','};
  58. static EventHandlerList events = new EventHandlerList ();
  59. static object buildManagerRemoveEntryEvent = new object ();
  60. static bool hosted;
  61. static Dictionary <string, bool> virtualPathsToIgnore;
  62. static bool virtualPathsToIgnoreChecked;
  63. static bool haveVirtualPathsToIgnore;
  64. static List <Assembly> AppCode_Assemblies = new List<Assembly>();
  65. static List <Assembly> TopLevel_Assemblies = new List<Assembly>();
  66. static Dictionary <Type, CodeDomProvider> codeDomProviders;
  67. static Dictionary <string, BuildManagerCacheItem> buildCache;
  68. static List <Assembly> referencedAssemblies;
  69. static int buildCount;
  70. static bool is_precompiled;
  71. //static bool updatable; unused
  72. static Dictionary<string, PreCompilationData> precompiled;
  73. // This is here _only_ for the purpose of unit tests!
  74. internal static bool suppressDebugModeMessages;
  75. #if SYSTEMCORE_DEP
  76. static ReaderWriterLockSlim buildCacheLock;
  77. #else
  78. static ReaderWriterLock buildCacheLock;
  79. #endif
  80. static ulong recursionDepth;
  81. internal static bool IsPrecompiled {
  82. get { return is_precompiled; }
  83. }
  84. internal static event BuildManagerRemoveEntryEventHandler RemoveEntry {
  85. add { events.AddHandler (buildManagerRemoveEntryEvent, value); }
  86. remove { events.RemoveHandler (buildManagerRemoveEntryEvent, value); }
  87. }
  88. internal static bool BatchMode {
  89. get {
  90. if (!hosted)
  91. return false; // Fix for bug #380985
  92. CompilationSection cs = CompilationConfig;
  93. if (cs == null)
  94. return true;
  95. return cs.Batch;
  96. }
  97. }
  98. // Assemblies built from the App_Code directory
  99. public static IList CodeAssemblies {
  100. get { return AppCode_Assemblies; }
  101. }
  102. internal static CompilationSection CompilationConfig {
  103. get { return WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection; }
  104. }
  105. internal static bool HaveResources {
  106. get; set;
  107. }
  108. internal static IList TopLevelAssemblies {
  109. get { return TopLevel_Assemblies; }
  110. }
  111. static BuildManager ()
  112. {
  113. hosted = (AppDomain.CurrentDomain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
  114. buildCache = new Dictionary <string, BuildManagerCacheItem> (RuntimeHelpers.StringEqualityComparerCulture);
  115. #if SYSTEMCORE_DEP
  116. buildCacheLock = new ReaderWriterLockSlim ();
  117. #else
  118. buildCacheLock = new ReaderWriterLock ();
  119. #endif
  120. referencedAssemblies = new List <Assembly> ();
  121. recursionDepth = 0;
  122. string appPath = HttpRuntime.AppDomainAppPath;
  123. string precomp_name = null;
  124. is_precompiled = String.IsNullOrEmpty (appPath) ? false : File.Exists ((precomp_name = Path.Combine (appPath, "PrecompiledApp.config")));
  125. if (is_precompiled)
  126. is_precompiled = LoadPrecompilationInfo (precomp_name);
  127. }
  128. // Deal with precompiled sites deployed in a different virtual path
  129. static void FixVirtualPaths ()
  130. {
  131. if (precompiled == null)
  132. return;
  133. string [] parts;
  134. int skip = -1;
  135. foreach (string vpath in precompiled.Keys) {
  136. parts = vpath.Split ('/');
  137. for (int i = 0; i < parts.Length; i++) {
  138. if (String.IsNullOrEmpty (parts [i]))
  139. continue;
  140. // The path must be rooted, otherwise PhysicalPath returned
  141. // below will be relative to the current request path and
  142. // File.Exists will return a false negative. See bug #546053
  143. string test_path = "/" + String.Join ("/", parts, i, parts.Length - i);
  144. VirtualPath result = GetAbsoluteVirtualPath (test_path);
  145. if (result != null && File.Exists (result.PhysicalPath)) {
  146. skip = i - 1;
  147. break;
  148. }
  149. }
  150. }
  151. string app_vpath = HttpRuntime.AppDomainAppVirtualPath;
  152. if (skip == -1 || (skip == 0 && app_vpath == "/"))
  153. return;
  154. if (!app_vpath.EndsWith ("/"))
  155. app_vpath = app_vpath + "/";
  156. Dictionary<string, PreCompilationData> copy = new Dictionary<string, PreCompilationData> (precompiled);
  157. precompiled.Clear ();
  158. foreach (KeyValuePair<string,PreCompilationData> entry in copy) {
  159. parts = entry.Key.Split ('/');
  160. string new_path;
  161. if (String.IsNullOrEmpty (parts [0]))
  162. new_path = app_vpath + String.Join ("/", parts, skip + 1, parts.Length - skip - 1);
  163. else
  164. new_path = app_vpath + String.Join ("/", parts, skip, parts.Length - skip);
  165. entry.Value.VirtualPath = new_path;
  166. precompiled.Add (new_path, entry.Value);
  167. }
  168. }
  169. static bool LoadPrecompilationInfo (string precomp_config)
  170. {
  171. using (XmlTextReader reader = new XmlTextReader (precomp_config)) {
  172. reader.MoveToContent ();
  173. if (reader.Name != "precompiledApp")
  174. return false;
  175. /* unused
  176. if (reader.HasAttributes)
  177. while (reader.MoveToNextAttribute ())
  178. if (reader.Name == "updatable") {
  179. updatable = (reader.Value == "true");
  180. break;
  181. }
  182. */
  183. }
  184. string [] compiled = Directory.GetFiles (HttpRuntime.BinDirectory, "*.compiled");
  185. foreach (string str in compiled)
  186. LoadCompiled (str);
  187. FixVirtualPaths ();
  188. return true;
  189. }
  190. static void LoadCompiled (string filename)
  191. {
  192. using (XmlTextReader reader = new XmlTextReader (filename)) {
  193. reader.MoveToContent ();
  194. if (reader.Name == "preserve" && reader.HasAttributes) {
  195. reader.MoveToNextAttribute ();
  196. string val = reader.Value;
  197. // 1 -> app_code subfolder - add the assembly to CodeAssemblies
  198. // 2 -> ashx
  199. // 3 -> ascx, aspx
  200. // 6 -> app_code - add the assembly to CodeAssemblies
  201. // 8 -> global.asax
  202. // 9 -> App_GlobalResources - set the assembly for HttpContext
  203. if (reader.Name == "resultType" && (val == "2" || val == "3" || val == "8"))
  204. LoadPageData (reader, true);
  205. else if (val == "1" || val == "6") {
  206. PreCompilationData pd = LoadPageData (reader, false);
  207. CodeAssemblies.Add (Assembly.Load (pd.AssemblyFileName));
  208. } else if (val == "9") {
  209. PreCompilationData pd = LoadPageData (reader, false);
  210. HttpContext.AppGlobalResourcesAssembly = Assembly.Load (pd.AssemblyFileName);
  211. }
  212. }
  213. }
  214. }
  215. class PreCompilationData {
  216. public string VirtualPath;
  217. public string AssemblyFileName;
  218. public string TypeName;
  219. public Type Type;
  220. }
  221. static PreCompilationData LoadPageData (XmlTextReader reader, bool store)
  222. {
  223. PreCompilationData pc_data = new PreCompilationData ();
  224. while (reader.MoveToNextAttribute ()) {
  225. string name = reader.Name;
  226. if (name == "virtualPath")
  227. pc_data.VirtualPath = VirtualPathUtility.RemoveTrailingSlash (reader.Value);
  228. else if (name == "assembly")
  229. pc_data.AssemblyFileName = reader.Value;
  230. else if (name == "type")
  231. pc_data.TypeName = reader.Value;
  232. }
  233. if (store) {
  234. if (precompiled == null)
  235. precompiled = new Dictionary<string, PreCompilationData> (RuntimeHelpers.StringEqualityComparerCulture);
  236. precompiled.Add (pc_data.VirtualPath, pc_data);
  237. }
  238. return pc_data;
  239. }
  240. static void AddAssembly (Assembly asm, List <Assembly> al)
  241. {
  242. if (al.Contains (asm))
  243. return;
  244. al.Add (asm);
  245. }
  246. static void AddPathToIgnore (string vp)
  247. {
  248. if (virtualPathsToIgnore == null)
  249. virtualPathsToIgnore = new Dictionary <string, bool> (RuntimeHelpers.StringEqualityComparerCulture);
  250. VirtualPath path = GetAbsoluteVirtualPath (vp);
  251. string vpAbsolute = path.Absolute;
  252. if (!virtualPathsToIgnore.ContainsKey (vpAbsolute)) {
  253. virtualPathsToIgnore.Add (vpAbsolute, true);
  254. haveVirtualPathsToIgnore = true;
  255. }
  256. string vpRelative = path.AppRelative;
  257. if (!virtualPathsToIgnore.ContainsKey (vpRelative)) {
  258. virtualPathsToIgnore.Add (vpRelative, true);
  259. haveVirtualPathsToIgnore = true;
  260. }
  261. if (!virtualPathsToIgnore.ContainsKey (vp)) {
  262. virtualPathsToIgnore.Add (vp, true);
  263. haveVirtualPathsToIgnore = true;
  264. }
  265. }
  266. internal static void AddToReferencedAssemblies (Assembly asm)
  267. {
  268. // should not be used
  269. }
  270. static void AssertVirtualPathExists (VirtualPath virtualPath)
  271. {
  272. string realpath;
  273. bool dothrow = false;
  274. if (virtualPath.IsFake) {
  275. realpath = virtualPath.PhysicalPath;
  276. if (!File.Exists (realpath) && !Directory.Exists (realpath))
  277. dothrow = true;
  278. } else {
  279. VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
  280. string vpAbsolute = virtualPath.Absolute;
  281. if (!vpp.FileExists (vpAbsolute) && !vpp.DirectoryExists (vpAbsolute))
  282. dothrow = true;
  283. }
  284. if (dothrow)
  285. throw new HttpException (404, "The file '" + virtualPath + "' does not exist.", virtualPath.Absolute);
  286. }
  287. static void Build (VirtualPath vp)
  288. {
  289. AssertVirtualPathExists (vp);
  290. CompilationSection cs = CompilationConfig;
  291. lock (bigCompilationLock) {
  292. bool entryExists;
  293. if (HasCachedItemNoLock (vp.Absolute, out entryExists))
  294. return;
  295. if (recursionDepth == 0)
  296. referencedAssemblies.Clear ();
  297. recursionDepth++;
  298. try {
  299. BuildInner (vp, cs != null ? cs.Debug : false);
  300. if (entryExists && recursionDepth <= 1)
  301. // We count only update builds - first time a file
  302. // (or a batch) is built doesn't count.
  303. buildCount++;
  304. } finally {
  305. // See http://support.microsoft.com/kb/319947
  306. if (buildCount > cs.NumRecompilesBeforeAppRestart)
  307. HttpRuntime.UnloadAppDomain ();
  308. recursionDepth--;
  309. }
  310. }
  311. }
  312. // This method assumes it is being called with the big compilation lock held
  313. static void BuildInner (VirtualPath vp, bool debug)
  314. {
  315. var builder = new BuildManagerDirectoryBuilder (vp);
  316. bool recursive = recursionDepth > 1;
  317. List <BuildProviderGroup> builderGroups = builder.Build (IsSingleBuild (vp, recursive));
  318. if (builderGroups == null)
  319. return;
  320. string vpabsolute = vp.Absolute;
  321. int buildHash = (vpabsolute.GetHashCode () | (int)DateTime.Now.Ticks) + (int)recursionDepth;
  322. string assemblyBaseName;
  323. AssemblyBuilder abuilder;
  324. CompilerType ct;
  325. int attempts;
  326. bool singleBuild, needMainVpBuild;
  327. CompilationException compilationError;
  328. // Each group becomes a separate assembly.
  329. foreach (BuildProviderGroup group in builderGroups) {
  330. needMainVpBuild = false;
  331. compilationError = null;
  332. assemblyBaseName = null;
  333. if (group.Count == 1) {
  334. if (recursive || !group.Master)
  335. assemblyBaseName = String.Format ("{0}_{1}.{2:x}.", group.NamePrefix, VirtualPathUtility.GetFileName (group [0].VirtualPath), buildHash);
  336. singleBuild = true;
  337. } else
  338. singleBuild = false;
  339. if (assemblyBaseName == null)
  340. assemblyBaseName = group.NamePrefix + "_";
  341. ct = group.CompilerType;
  342. attempts = 3;
  343. while (attempts > 0) {
  344. abuilder = new AssemblyBuilder (vp, CreateDomProvider (ct), assemblyBaseName);
  345. abuilder.CompilerOptions = ct.CompilerParameters;
  346. abuilder.AddAssemblyReference (GetReferencedAssemblies () as List <Assembly>);
  347. try {
  348. GenerateAssembly (abuilder, group, vp, debug);
  349. attempts = 0;
  350. } catch (CompilationException ex) {
  351. attempts--;
  352. if (singleBuild)
  353. throw new HttpException ("Single file build failed.", ex);
  354. if (attempts == 0) {
  355. needMainVpBuild = true;
  356. compilationError = ex;
  357. break;
  358. }
  359. CompilerResults results = ex.Results;
  360. if (results == null)
  361. throw new HttpException ("No results returned from failed compilation.", ex);
  362. else
  363. RemoveFailedAssemblies (vpabsolute, ex, abuilder, group, results, debug);
  364. }
  365. }
  366. if (needMainVpBuild) {
  367. // One last attempt - try to build just the requested path
  368. // if it's not built yet or just return without throwing the
  369. // exception if it has already been built.
  370. if (HasCachedItemNoLock (vpabsolute)) {
  371. if (debug)
  372. DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
  373. compilationError, vpabsolute);
  374. return;
  375. };
  376. // This will trigger a recursive build of the requested vp,
  377. // which means only the vp alone will be built (or not);
  378. Build (vp);
  379. if (HasCachedItemNoLock (vpabsolute)) {
  380. if (debug)
  381. DescribeCompilationError ("Path '{0}' built successfully, but a compilation exception has been thrown for other files:",
  382. compilationError, vpabsolute);
  383. return;
  384. }
  385. // In theory this code is unreachable. If the recursive
  386. // build of the main vp failed, then it should have thrown
  387. // the build exception.
  388. throw new HttpException ("Requested virtual path build failed.", compilationError);
  389. }
  390. }
  391. }
  392. static CodeDomProvider CreateDomProvider (CompilerType ct)
  393. {
  394. if (codeDomProviders == null)
  395. codeDomProviders = new Dictionary <Type, CodeDomProvider> ();
  396. Type type = ct.CodeDomProviderType;
  397. if (type == null) {
  398. CompilationSection cs = CompilationConfig;
  399. CompilerType tmp = GetDefaultCompilerTypeForLanguage (cs.DefaultLanguage, cs);
  400. if (tmp != null)
  401. type = tmp.CodeDomProviderType;
  402. }
  403. if (type == null)
  404. return null;
  405. CodeDomProvider ret;
  406. if (codeDomProviders.TryGetValue (type, out ret))
  407. return ret;
  408. ret = Activator.CreateInstance (type) as CodeDomProvider;
  409. if (ret == null)
  410. return null;
  411. codeDomProviders.Add (type, ret);
  412. return ret;
  413. }
  414. #if NET_4_0
  415. public static Stream CreateCachedFile (string fileName)
  416. {
  417. if (fileName != null && (fileName == String.Empty || fileName.IndexOf (Path.DirectorySeparatorChar) != -1))
  418. throw new ArgumentException ("Value does not fall within the expected range.");
  419. string path = Path.Combine (HttpRuntime.CodegenDir, fileName);
  420. return new FileStream (path, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
  421. }
  422. public static Stream ReadCachedFile (string fileName)
  423. {
  424. if (fileName != null && (fileName == String.Empty || fileName.IndexOf (Path.DirectorySeparatorChar) != -1))
  425. throw new ArgumentException ("Value does not fall within the expected range.");
  426. string path = Path.Combine (HttpRuntime.CodegenDir, fileName);
  427. if (!File.Exists (path))
  428. return null;
  429. return new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.None);
  430. }
  431. #endif
  432. public static object CreateInstanceFromVirtualPath (string virtualPath, Type requiredBaseType)
  433. {
  434. return CreateInstanceFromVirtualPath (GetAbsoluteVirtualPath (virtualPath), requiredBaseType);
  435. }
  436. internal static object CreateInstanceFromVirtualPath (VirtualPath virtualPath, Type requiredBaseType)
  437. {
  438. if (requiredBaseType == null)
  439. throw new NullReferenceException (); // This is what MS does, but
  440. // from somewhere else.
  441. Type type = GetCompiledType (virtualPath);
  442. if (type == null)
  443. return null;
  444. if (!requiredBaseType.IsAssignableFrom (type))
  445. throw new HttpException (500,
  446. String.Format ("Type '{0}' does not inherit from '{1}'.", type.FullName, requiredBaseType.FullName));
  447. return Activator.CreateInstance (type, null);
  448. }
  449. static void DescribeCompilationError (string format, CompilationException ex, params object[] parms)
  450. {
  451. StringBuilder sb = new StringBuilder ();
  452. string newline = Environment.NewLine;
  453. if (parms != null)
  454. sb.AppendFormat (format + newline, parms);
  455. else
  456. sb.Append (format + newline);
  457. CompilerResults results = ex != null ? ex.Results : null;
  458. if (results == null)
  459. sb.Append ("No compiler error information present." + newline);
  460. else {
  461. sb.Append ("Compiler errors:" + newline);
  462. foreach (CompilerError error in results.Errors)
  463. sb.Append (" " + error.ToString () + newline);
  464. }
  465. if (ex != null) {
  466. sb.Append (newline + "Exception thrown:" + newline);
  467. sb.Append (ex.ToString ());
  468. }
  469. ShowDebugModeMessage (sb.ToString ());
  470. }
  471. static BuildProvider FindBuildProviderForPhysicalPath (string path, BuildProviderGroup group, HttpRequest req)
  472. {
  473. if (req == null || String.IsNullOrEmpty (path))
  474. return null;
  475. foreach (BuildProvider bp in group) {
  476. if (String.Compare (path, req.MapPath (bp.VirtualPath), RuntimeHelpers.StringComparison) == 0)
  477. return bp;
  478. }
  479. return null;
  480. }
  481. static void GenerateAssembly (AssemblyBuilder abuilder, BuildProviderGroup group, VirtualPath vp, bool debug)
  482. {
  483. IDictionary <string, bool> deps;
  484. BuildManagerCacheItem bmci;
  485. string bvp, vpabsolute = vp.Absolute;
  486. StringBuilder sb;
  487. string newline;
  488. int failedCount = 0;
  489. if (debug) {
  490. newline = Environment.NewLine;
  491. sb = new StringBuilder ("Code generation for certain virtual paths in a batch failed. Those files have been removed from the batch." + newline);
  492. sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
  493. } else {
  494. newline = null;
  495. sb = null;
  496. }
  497. List <BuildProvider> failedBuildProviders = null;
  498. StringComparison stringComparison = RuntimeHelpers.StringComparison;
  499. foreach (BuildProvider bp in group) {
  500. bvp = bp.VirtualPath;
  501. if (HasCachedItemNoLock (bvp))
  502. continue;
  503. try {
  504. bp.GenerateCode (abuilder);
  505. } catch (Exception ex) {
  506. if (String.Compare (bvp, vpabsolute, stringComparison) == 0) {
  507. if (ex is CompilationException || ex is ParseException)
  508. throw;
  509. throw new HttpException ("Code generation failed.", ex);
  510. }
  511. if (failedBuildProviders == null)
  512. failedBuildProviders = new List <BuildProvider> ();
  513. failedBuildProviders.Add (bp);
  514. failedCount++;
  515. if (sb != null) {
  516. if (failedCount > 1)
  517. sb.Append (newline);
  518. sb.AppendFormat ("Failed file virtual path: {0}; Exception: {1}{2}{1}", bp.VirtualPath, newline, ex);
  519. }
  520. continue;
  521. }
  522. deps = bp.ExtractDependencies ();
  523. if (deps != null) {
  524. foreach (var dep in deps) {
  525. bmci = GetCachedItemNoLock (dep.Key);
  526. if (bmci == null || bmci.BuiltAssembly == null)
  527. continue;
  528. abuilder.AddAssemblyReference (bmci.BuiltAssembly);
  529. }
  530. }
  531. }
  532. if (sb != null && failedCount > 0)
  533. ShowDebugModeMessage (sb.ToString ());
  534. if (failedBuildProviders != null) {
  535. foreach (BuildProvider bp in failedBuildProviders)
  536. group.Remove (bp);
  537. }
  538. foreach (Assembly asm in referencedAssemblies) {
  539. if (asm == null)
  540. continue;
  541. abuilder.AddAssemblyReference (asm);
  542. }
  543. CompilerResults results = abuilder.BuildAssembly (vp);
  544. // No results is not an error - it is possible that the assembly builder contained only .asmx and
  545. // .ashx files which had no body, just the directive. In such case, no code unit or code file is added
  546. // to the assembly builder and, in effect, no assembly is produced but there are STILL types that need
  547. // to be added to the cache.
  548. Assembly compiledAssembly = results != null ? results.CompiledAssembly : null;
  549. bool locked = false;
  550. try {
  551. #if SYSTEMCORE_DEP
  552. buildCacheLock.EnterWriteLock ();
  553. #else
  554. buildCacheLock.AcquireWriterLock (-1);
  555. #endif
  556. locked = true;
  557. if (compiledAssembly != null)
  558. referencedAssemblies.Add (compiledAssembly);
  559. foreach (BuildProvider bp in group) {
  560. if (HasCachedItemNoLock (bp.VirtualPath))
  561. continue;
  562. StoreInCache (bp, compiledAssembly, results);
  563. }
  564. } finally {
  565. if (locked) {
  566. #if SYSTEMCORE_DEP
  567. buildCacheLock.ExitWriteLock ();
  568. #else
  569. buildCacheLock.ReleaseWriterLock ();
  570. #endif
  571. }
  572. }
  573. }
  574. static VirtualPath GetAbsoluteVirtualPath (string virtualPath)
  575. {
  576. string vp;
  577. if (!VirtualPathUtility.IsRooted (virtualPath)) {
  578. HttpContext ctx = HttpContext.Current;
  579. HttpRequest req = ctx != null ? ctx.Request : null;
  580. if (req != null) {
  581. string fileDir = req.FilePath;
  582. if (!String.IsNullOrEmpty (fileDir) && String.Compare (fileDir, "/", StringComparison.Ordinal) != 0)
  583. fileDir = VirtualPathUtility.GetDirectory (fileDir);
  584. else
  585. fileDir = "/";
  586. vp = VirtualPathUtility.Combine (fileDir, virtualPath);
  587. } else
  588. throw new HttpException ("No context, cannot map paths.");
  589. } else
  590. vp = virtualPath;
  591. return new VirtualPath (vp);
  592. }
  593. [MonoTODO ("Not implemented, always returns null")]
  594. public static BuildDependencySet GetCachedBuildDependencySet (HttpContext context, string virtualPath)
  595. {
  596. return null; // null is ok here until we store the dependency set in the Cache.
  597. }
  598. static BuildManagerCacheItem GetCachedItem (string vp)
  599. {
  600. bool locked = false;
  601. try {
  602. #if SYSTEMCORE_DEP
  603. buildCacheLock.EnterReadLock ();
  604. #else
  605. buildCacheLock.AcquireReaderLock (-1);
  606. #endif
  607. locked = true;
  608. return GetCachedItemNoLock (vp);
  609. } finally {
  610. if (locked) {
  611. #if SYSTEMCORE_DEP
  612. buildCacheLock.ExitReadLock ();
  613. #else
  614. buildCacheLock.ReleaseReaderLock ();
  615. #endif
  616. }
  617. }
  618. }
  619. static BuildManagerCacheItem GetCachedItemNoLock (string vp)
  620. {
  621. BuildManagerCacheItem ret;
  622. if (buildCache.TryGetValue (vp, out ret))
  623. return ret;
  624. return null;
  625. }
  626. internal static Type GetCodeDomProviderType (BuildProvider provider)
  627. {
  628. CompilerType codeCompilerType;
  629. Type codeDomProviderType = null;
  630. codeCompilerType = provider.CodeCompilerType;
  631. if (codeCompilerType != null)
  632. codeDomProviderType = codeCompilerType.CodeDomProviderType;
  633. if (codeDomProviderType == null)
  634. throw new HttpException (String.Concat ("Provider '", provider, " 'fails to specify the compiler type."));
  635. return codeDomProviderType;
  636. }
  637. static Type GetPrecompiledType (string virtualPath)
  638. {
  639. PreCompilationData pc_data;
  640. if (precompiled != null && precompiled.TryGetValue (virtualPath, out pc_data)) {
  641. if (pc_data.Type == null) {
  642. pc_data.Type = Type.GetType (pc_data.TypeName + ", " + pc_data.AssemblyFileName, true);
  643. }
  644. return pc_data.Type;
  645. }
  646. return null;
  647. }
  648. internal static Type GetPrecompiledApplicationType ()
  649. {
  650. if (!is_precompiled)
  651. return null;
  652. Type apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath, "Global.asax"));
  653. if (apptype == null)
  654. apptype = GetPrecompiledType (VirtualPathUtility.Combine (HttpRuntime.AppDomainAppVirtualPath , "global.asax"));
  655. return apptype;
  656. }
  657. public static Assembly GetCompiledAssembly (string virtualPath)
  658. {
  659. return GetCompiledAssembly (GetAbsoluteVirtualPath (virtualPath));
  660. }
  661. internal static Assembly GetCompiledAssembly (VirtualPath virtualPath)
  662. {
  663. string vpabsolute = virtualPath.Absolute;
  664. if (is_precompiled) {
  665. Type type = GetPrecompiledType (vpabsolute);
  666. if (type != null)
  667. return type.Assembly;
  668. }
  669. BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
  670. if (bmci != null)
  671. return bmci.BuiltAssembly;
  672. Build (virtualPath);
  673. bmci = GetCachedItem (vpabsolute);
  674. if (bmci != null)
  675. return bmci.BuiltAssembly;
  676. return null;
  677. }
  678. public static Type GetCompiledType (string virtualPath)
  679. {
  680. return GetCompiledType (GetAbsoluteVirtualPath (virtualPath));
  681. }
  682. internal static Type GetCompiledType (VirtualPath virtualPath)
  683. {
  684. string vpabsolute = virtualPath.Absolute;
  685. if (is_precompiled) {
  686. Type type = GetPrecompiledType (vpabsolute);
  687. if (type != null)
  688. return type;
  689. }
  690. BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
  691. if (bmci != null) {
  692. ReferenceAssemblyInCompilation (bmci);
  693. return bmci.Type;
  694. }
  695. Build (virtualPath);
  696. bmci = GetCachedItem (vpabsolute);
  697. if (bmci != null) {
  698. ReferenceAssemblyInCompilation (bmci);
  699. return bmci.Type;
  700. }
  701. return null;
  702. }
  703. public static string GetCompiledCustomString (string virtualPath)
  704. {
  705. return GetCompiledCustomString (GetAbsoluteVirtualPath (virtualPath));
  706. }
  707. internal static string GetCompiledCustomString (VirtualPath virtualPath)
  708. {
  709. string vpabsolute = virtualPath.Absolute;
  710. BuildManagerCacheItem bmci = GetCachedItem (vpabsolute);
  711. if (bmci != null)
  712. return bmci.CompiledCustomString;
  713. Build (virtualPath);
  714. bmci = GetCachedItem (vpabsolute);
  715. if (bmci != null)
  716. return bmci.CompiledCustomString;
  717. return null;
  718. }
  719. internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection)
  720. {
  721. return GetDefaultCompilerTypeForLanguage (language, configSection, true);
  722. }
  723. internal static CompilerType GetDefaultCompilerTypeForLanguage (string language, CompilationSection configSection, bool throwOnMissing)
  724. {
  725. // MS throws when accesing a Hashtable, we do here.
  726. if (language == null || language.Length == 0)
  727. throw new ArgumentNullException ("language");
  728. CompilationSection config;
  729. if (configSection == null)
  730. config = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
  731. else
  732. config = configSection;
  733. Compiler compiler = config.Compilers.Get (language);
  734. CompilerParameters p;
  735. Type type;
  736. if (compiler != null) {
  737. type = HttpApplication.LoadType (compiler.Type, true);
  738. p = new CompilerParameters ();
  739. p.CompilerOptions = compiler.CompilerOptions;
  740. p.WarningLevel = compiler.WarningLevel;
  741. SetCommonParameters (config, p, type, language);
  742. return new CompilerType (type, p);
  743. }
  744. if (CodeDomProvider.IsDefinedLanguage (language)) {
  745. CompilerInfo info = CodeDomProvider.GetCompilerInfo (language);
  746. p = info.CreateDefaultCompilerParameters ();
  747. type = info.CodeDomProviderType;
  748. SetCommonParameters (config, p, type, language);
  749. return new CompilerType (type, p);
  750. }
  751. if (throwOnMissing)
  752. throw new HttpException (String.Concat ("No compiler for language '", language, "'."));
  753. return null;
  754. }
  755. public static ICollection GetReferencedAssemblies ()
  756. {
  757. List <Assembly> al = new List <Assembly> ();
  758. CompilationSection compConfig = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
  759. if (compConfig == null)
  760. return al;
  761. bool addAssembliesInBin = false;
  762. foreach (AssemblyInfo info in compConfig.Assemblies) {
  763. if (info.Assembly == "*")
  764. addAssembliesInBin = is_precompiled ? false : true;
  765. else
  766. LoadAssembly (info, al);
  767. }
  768. foreach (Assembly topLevelAssembly in TopLevelAssemblies)
  769. al.Add (topLevelAssembly);
  770. foreach (string assLocation in WebConfigurationManager.ExtraAssemblies)
  771. LoadAssembly (assLocation, al);
  772. // Precompiled sites unconditionally load all assemblies from bin/ (fix for
  773. // bug #502016)
  774. if (is_precompiled || addAssembliesInBin) {
  775. foreach (string s in HttpApplication.BinDirectoryAssemblies) {
  776. try {
  777. LoadAssembly (s, al);
  778. } catch (BadImageFormatException) {
  779. // ignore silently
  780. }
  781. }
  782. }
  783. return al;
  784. }
  785. // The 2 GetType() overloads work on the global.asax, App_GlobalResources, App_WebReferences or App_Browsers
  786. public static Type GetType (string typeName, bool throwOnError)
  787. {
  788. return GetType (typeName, throwOnError, false);
  789. }
  790. public static Type GetType (string typeName, bool throwOnError, bool ignoreCase)
  791. {
  792. Type ret = null;
  793. try {
  794. foreach (Assembly asm in TopLevel_Assemblies) {
  795. ret = asm.GetType (typeName, throwOnError, ignoreCase);
  796. if (ret != null)
  797. break;
  798. }
  799. } catch (Exception ex) {
  800. throw new HttpException ("Failed to find the specified type.", ex);
  801. }
  802. return ret;
  803. }
  804. public static ICollection GetVirtualPathDependencies (string virtualPath)
  805. {
  806. return GetVirtualPathDependencies (virtualPath, null);
  807. }
  808. internal static ICollection GetVirtualPathDependencies (string virtualPath, BuildProvider bprovider)
  809. {
  810. BuildProvider provider = bprovider;
  811. if (provider == null) {
  812. CompilationSection cs = CompilationConfig;
  813. if (cs == null)
  814. return null;
  815. provider = BuildManagerDirectoryBuilder.GetBuildProvider (virtualPath, cs.BuildProviders);
  816. }
  817. if (provider == null)
  818. return null;
  819. IDictionary <string, bool> deps = provider.ExtractDependencies ();
  820. if (deps == null)
  821. return null;
  822. return (ICollection)deps.Keys;
  823. }
  824. internal static bool HasCachedItemNoLock (string vp, out bool entryExists)
  825. {
  826. BuildManagerCacheItem item;
  827. if (buildCache.TryGetValue (vp, out item)) {
  828. entryExists = true;
  829. return item != null;
  830. }
  831. entryExists = false;
  832. return false;
  833. }
  834. internal static bool HasCachedItemNoLock (string vp)
  835. {
  836. bool dummy;
  837. return HasCachedItemNoLock (vp, out dummy);
  838. }
  839. internal static bool IgnoreVirtualPath (string virtualPath)
  840. {
  841. if (!virtualPathsToIgnoreChecked) {
  842. lock (virtualPathsToIgnoreLock) {
  843. if (!virtualPathsToIgnoreChecked)
  844. LoadVirtualPathsToIgnore ();
  845. virtualPathsToIgnoreChecked = true;
  846. }
  847. }
  848. if (!haveVirtualPathsToIgnore)
  849. return false;
  850. if (virtualPathsToIgnore.ContainsKey (virtualPath))
  851. return true;
  852. return false;
  853. }
  854. static bool IsSingleBuild (VirtualPath vp, bool recursive)
  855. {
  856. if (String.Compare (vp.AppRelative, "~/global.asax", StringComparison.OrdinalIgnoreCase) == 0)
  857. return true;
  858. if (!BatchMode)
  859. return true;
  860. return recursive;
  861. }
  862. static void LoadAssembly (string path, List <Assembly> al)
  863. {
  864. AddAssembly (Assembly.LoadFrom (path), al);
  865. }
  866. static void LoadAssembly (AssemblyInfo info, List <Assembly> al)
  867. {
  868. AddAssembly (Assembly.Load (info.Assembly), al);
  869. }
  870. static void LoadVirtualPathsToIgnore ()
  871. {
  872. NameValueCollection appSettings = WebConfigurationManager.AppSettings;
  873. if (appSettings == null)
  874. return;
  875. string pathsFromConfig = appSettings ["MonoAspnetBatchCompileIgnorePaths"];
  876. string pathsFromFile = appSettings ["MonoAspnetBatchCompileIgnoreFromFile"];
  877. if (!String.IsNullOrEmpty (pathsFromConfig)) {
  878. string[] paths = pathsFromConfig.Split (virtualPathsToIgnoreSplitChars);
  879. string path;
  880. foreach (string p in paths) {
  881. path = p.Trim ();
  882. if (path.Length == 0)
  883. continue;
  884. AddPathToIgnore (path);
  885. }
  886. }
  887. if (!String.IsNullOrEmpty (pathsFromFile)) {
  888. string realpath;
  889. HttpContext ctx = HttpContext.Current;
  890. HttpRequest req = ctx != null ? ctx.Request : null;
  891. if (req == null)
  892. throw new HttpException ("Missing context, cannot continue.");
  893. realpath = req.MapPath (pathsFromFile);
  894. if (!File.Exists (realpath))
  895. return;
  896. string[] paths = File.ReadAllLines (realpath);
  897. if (paths == null || paths.Length == 0)
  898. return;
  899. string path;
  900. foreach (string p in paths) {
  901. path = p.Trim ();
  902. if (path.Length == 0)
  903. continue;
  904. AddPathToIgnore (path);
  905. }
  906. }
  907. }
  908. static void OnEntryRemoved (string vp)
  909. {
  910. BuildManagerRemoveEntryEventHandler eh = events [buildManagerRemoveEntryEvent] as BuildManagerRemoveEntryEventHandler;
  911. if (eh != null)
  912. eh (new BuildManagerRemoveEntryEventArgs (vp, HttpContext.Current));
  913. }
  914. static void OnVirtualPathChanged (string key, object value, CacheItemRemovedReason removedReason)
  915. {
  916. string virtualPath;
  917. if (StrUtils.StartsWith (key, BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX))
  918. virtualPath = key.Substring (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX_LENGTH);
  919. else
  920. return;
  921. bool locked = false;
  922. try {
  923. #if SYSTEMCORE_DEP
  924. buildCacheLock.EnterWriteLock ();
  925. #else
  926. buildCacheLock.AcquireWriterLock (-1);
  927. #endif
  928. locked = true;
  929. if (HasCachedItemNoLock (virtualPath)) {
  930. buildCache [virtualPath] = null;
  931. OnEntryRemoved (virtualPath);
  932. }
  933. } finally {
  934. if (locked) {
  935. #if SYSTEMCORE_DEP
  936. buildCacheLock.ExitWriteLock ();
  937. #else
  938. buildCacheLock.ReleaseWriterLock ();
  939. #endif
  940. }
  941. }
  942. }
  943. static void ReferenceAssemblyInCompilation (BuildManagerCacheItem bmci)
  944. {
  945. if (recursionDepth == 0 || referencedAssemblies.Contains (bmci.BuiltAssembly))
  946. return;
  947. referencedAssemblies.Add (bmci.BuiltAssembly);
  948. }
  949. static void RemoveFailedAssemblies (string requestedVirtualPath, CompilationException ex, AssemblyBuilder abuilder,
  950. BuildProviderGroup group, CompilerResults results, bool debug)
  951. {
  952. StringBuilder sb;
  953. string newline;
  954. if (debug) {
  955. newline = Environment.NewLine;
  956. sb = new StringBuilder ("Compilation of certain files in a batch failed. Another attempt to compile the batch will be made." + newline);
  957. sb.Append ("Since you're running in debug mode, here's some more information about the error:" + newline);
  958. } else {
  959. newline = null;
  960. sb = null;
  961. }
  962. var failedBuildProviders = new List <BuildProvider> ();
  963. BuildProvider bp;
  964. HttpContext ctx = HttpContext.Current;
  965. HttpRequest req = ctx != null ? ctx.Request : null;
  966. bool rethrow = false;
  967. foreach (CompilerError error in results.Errors) {
  968. if (error.IsWarning)
  969. continue;
  970. bp = abuilder.GetBuildProviderForPhysicalFilePath (error.FileName);
  971. if (bp == null) {
  972. bp = FindBuildProviderForPhysicalPath (error.FileName, group, req);
  973. if (bp == null)
  974. continue;
  975. }
  976. if (String.Compare (bp.VirtualPath, requestedVirtualPath, StringComparison.Ordinal) == 0)
  977. rethrow = true;
  978. if (!failedBuildProviders.Contains (bp)) {
  979. failedBuildProviders.Add (bp);
  980. if (sb != null)
  981. sb.AppendFormat ("\t{0}{1}", bp.VirtualPath, newline);
  982. }
  983. if (sb != null)
  984. sb.AppendFormat ("\t\t{0}{1}", error, newline);
  985. }
  986. foreach (BuildProvider fbp in failedBuildProviders)
  987. group.Remove (fbp);
  988. if (sb != null) {
  989. sb.AppendFormat ("{0}The following exception has been thrown for the file(s) listed above:{0}{1}",
  990. newline, ex.ToString ());
  991. ShowDebugModeMessage (sb.ToString ());
  992. sb = null;
  993. }
  994. if (rethrow)
  995. throw new HttpException ("Compilation failed.", ex);
  996. }
  997. static void SetCommonParameters (CompilationSection config, CompilerParameters p, Type compilerType, string language)
  998. {
  999. p.IncludeDebugInformation = config.Debug;
  1000. MonoSettingsSection mss = WebConfigurationManager.GetSection ("system.web/monoSettings") as MonoSettingsSection;
  1001. if (mss == null || !mss.UseCompilersCompatibility)
  1002. return;
  1003. Compiler compiler = mss.CompilersCompatibility.Get (language);
  1004. if (compiler == null)
  1005. return;
  1006. Type type = HttpApplication.LoadType (compiler.Type, false);
  1007. if (type != compilerType)
  1008. return;
  1009. p.CompilerOptions = String.Concat (p.CompilerOptions, " ", compiler.CompilerOptions);
  1010. }
  1011. static void ShowDebugModeMessage (string msg)
  1012. {
  1013. if (suppressDebugModeMessages)
  1014. return;
  1015. Console.WriteLine ();
  1016. Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
  1017. Console.WriteLine (msg);
  1018. Console.WriteLine ("******* DEBUG MODE MESSAGE *******");
  1019. Console.WriteLine ();
  1020. }
  1021. static void StoreInCache (BuildProvider bp, Assembly compiledAssembly, CompilerResults results)
  1022. {
  1023. string virtualPath = bp.VirtualPath;
  1024. var item = new BuildManagerCacheItem (compiledAssembly, bp, results);
  1025. if (buildCache.ContainsKey (virtualPath))
  1026. buildCache [virtualPath] = item;
  1027. else
  1028. buildCache.Add (virtualPath, item);
  1029. HttpContext ctx = HttpContext.Current;
  1030. HttpRequest req = ctx != null ? ctx.Request : null;
  1031. CacheDependency dep;
  1032. if (req != null) {
  1033. IDictionary <string, bool> deps = bp.ExtractDependencies ();
  1034. var files = new List <string> ();
  1035. string physicalPath;
  1036. physicalPath = req.MapPath (virtualPath);
  1037. if (File.Exists (physicalPath))
  1038. files.Add (physicalPath);
  1039. if (deps != null && deps.Count > 0) {
  1040. foreach (var d in deps) {
  1041. physicalPath = req.MapPath (d.Key);
  1042. if (!File.Exists (physicalPath))
  1043. continue;
  1044. if (!files.Contains (physicalPath))
  1045. files.Add (physicalPath);
  1046. }
  1047. }
  1048. dep = new CacheDependency (files.ToArray ());
  1049. } else
  1050. dep = null;
  1051. HttpRuntime.InternalCache.Add (BUILD_MANAGER_VIRTUAL_PATH_CACHE_PREFIX + virtualPath,
  1052. true,
  1053. dep,
  1054. Cache.NoAbsoluteExpiration,
  1055. Cache.NoSlidingExpiration,
  1056. CacheItemPriority.High,
  1057. new CacheItemRemovedCallback (OnVirtualPathChanged));
  1058. }
  1059. }
  1060. }