AssemblyBuilder.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. //
  2. // System.Web.Compilation.AssemblyBuilder
  3. //
  4. // Authors:
  5. // Chris Toshok ([email protected])
  6. // Gonzalo Paniagua Javier ([email protected])
  7. // Marek Habersack ([email protected])
  8. //
  9. // (C) 2006-2008 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.IO;
  38. using System.Security.Cryptography;
  39. using System.Reflection;
  40. using System.Text;
  41. using System.Web.Configuration;
  42. using System.Web.Util;
  43. using System.Web.Hosting;
  44. namespace System.Web.Compilation
  45. {
  46. class CompileUnitPartialType
  47. {
  48. public readonly CodeCompileUnit Unit;
  49. public readonly CodeNamespace ParentNamespace;
  50. public readonly CodeTypeDeclaration PartialType;
  51. string typeName;
  52. public string TypeName {
  53. get {
  54. if (typeName == null) {
  55. if (ParentNamespace == null || PartialType == null)
  56. return null;
  57. typeName = ParentNamespace.Name;
  58. if (String.IsNullOrEmpty (typeName))
  59. typeName = PartialType.Name;
  60. else
  61. typeName += "." + PartialType.Name;
  62. }
  63. return typeName;
  64. }
  65. }
  66. public CompileUnitPartialType (CodeCompileUnit unit, CodeNamespace parentNamespace, CodeTypeDeclaration type)
  67. {
  68. this.Unit = unit;
  69. this.ParentNamespace = parentNamespace;
  70. this.PartialType = type;
  71. }
  72. }
  73. public class AssemblyBuilder
  74. {
  75. struct CodeUnit
  76. {
  77. public readonly BuildProvider BuildProvider;
  78. public readonly CodeCompileUnit Unit;
  79. public CodeUnit (BuildProvider bp, CodeCompileUnit unit)
  80. {
  81. this.BuildProvider = bp;
  82. this.Unit = unit;
  83. }
  84. }
  85. interface ICodePragmaGenerator
  86. {
  87. int ReserveSpace (string filename);
  88. void DecorateFile (string path, string filename, MD5 checksum, Encoding enc);
  89. }
  90. class CSharpCodePragmaGenerator : ICodePragmaGenerator
  91. {
  92. // Copied from CSharpCodeGenerator.cs
  93. string QuoteSnippetString (string value)
  94. {
  95. // FIXME: this is weird, but works.
  96. string output = value.Replace ("\\", "\\\\");
  97. output = output.Replace ("\"", "\\\"");
  98. output = output.Replace ("\t", "\\t");
  99. output = output.Replace ("\r", "\\r");
  100. output = output.Replace ("\n", "\\n");
  101. return "\"" + output + "\"";
  102. }
  103. string ChecksumToHex (MD5 checksum)
  104. {
  105. var ret = new StringBuilder ();
  106. foreach (byte b in checksum.Hash)
  107. ret.Append (b.ToString ("X2"));
  108. return ret.ToString ();
  109. }
  110. const int pragmaChecksumStaticCount = 23;
  111. const int pragmaLineStaticCount = 8;
  112. const int md5ChecksumCount = 32;
  113. public int ReserveSpace (string filename)
  114. {
  115. return pragmaChecksumStaticCount +
  116. pragmaLineStaticCount +
  117. md5ChecksumCount +
  118. (QuoteSnippetString (filename).Length * 2) +
  119. (Environment.NewLine.Length * 3) +
  120. BaseCompiler.HashMD5.ToString ("B").Length;
  121. }
  122. public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
  123. {
  124. string newline = Environment.NewLine;
  125. var sb = new StringBuilder ();
  126. sb.AppendFormat ("#pragma checksum {0} \"{1}\" \"{2}\"{3}{3}",
  127. QuoteSnippetString (filename),
  128. BaseCompiler.HashMD5.ToString ("B"),
  129. ChecksumToHex (checksum),
  130. newline);
  131. sb.AppendFormat ("#line 1 {0}{1}", QuoteSnippetString (filename), newline);
  132. byte[] bytes = enc.GetBytes (sb.ToString ());
  133. using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
  134. fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
  135. fs.Write (bytes, 0, bytes.Length);
  136. bytes = null;
  137. sb.Length = 0;
  138. sb.AppendFormat ("{0}#line default{0}#line hidden{0}", newline);
  139. bytes = Encoding.UTF8.GetBytes (sb.ToString ());
  140. fs.Seek (0, SeekOrigin.End);
  141. fs.Write (bytes, 0, bytes.Length);
  142. }
  143. sb = null;
  144. bytes = null;
  145. }
  146. }
  147. class VBCodePragmaGenerator : ICodePragmaGenerator
  148. {
  149. const int pragmaExternalSourceCount = 21;
  150. public int ReserveSpace (string filename)
  151. {
  152. return pragmaExternalSourceCount +
  153. filename.Length +
  154. (Environment.NewLine.Length);
  155. }
  156. public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
  157. {
  158. string newline = Environment.NewLine;
  159. var sb = new StringBuilder ();
  160. sb.AppendFormat ("#ExternalSource(\"{0}\",1){1}", filename, newline);
  161. byte[] bytes = enc.GetBytes (sb.ToString ());
  162. using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
  163. fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
  164. fs.Write (bytes, 0, bytes.Length);
  165. bytes = null;
  166. sb.Length = 0;
  167. sb.AppendFormat ("{0}#End ExternalSource{0}", newline);
  168. bytes = enc.GetBytes (sb.ToString ());
  169. fs.Seek (0, SeekOrigin.End);
  170. fs.Write (bytes, 0, bytes.Length);
  171. }
  172. sb = null;
  173. bytes = null;
  174. }
  175. }
  176. const string DEFAULT_ASSEMBLY_BASE_NAME = "App_Web_";
  177. const int COPY_BUFFER_SIZE = 8192;
  178. static bool KeepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
  179. CodeDomProvider provider;
  180. CompilerParameters parameters;
  181. Dictionary <string, bool> code_files;
  182. Dictionary <string, List <CompileUnitPartialType>> partial_types;
  183. Dictionary <string, BuildProvider> path_to_buildprovider;
  184. List <CodeUnit> units;
  185. List <string> source_files;
  186. List <Assembly> referenced_assemblies;
  187. Dictionary <string, string> resource_files;
  188. TempFileCollection temp_files;
  189. string outputFilesPrefix;
  190. string outputAssemblyPrefix;
  191. string outputAssemblyName;
  192. internal AssemblyBuilder (CodeDomProvider provider)
  193. : this (null, provider, DEFAULT_ASSEMBLY_BASE_NAME)
  194. {}
  195. internal AssemblyBuilder (CodeDomProvider provider, string assemblyBaseName)
  196. : this (null, provider, assemblyBaseName)
  197. {}
  198. internal AssemblyBuilder (VirtualPath virtualPath, CodeDomProvider provider)
  199. : this (virtualPath, provider, DEFAULT_ASSEMBLY_BASE_NAME)
  200. {}
  201. internal AssemblyBuilder (VirtualPath virtualPath, CodeDomProvider provider, string assemblyBaseName)
  202. {
  203. this.provider = provider;
  204. this.outputFilesPrefix = assemblyBaseName ?? DEFAULT_ASSEMBLY_BASE_NAME;
  205. units = new List <CodeUnit> ();
  206. CompilationSection section;
  207. section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
  208. string tempdir = section.TempDirectory;
  209. if (String.IsNullOrEmpty (tempdir))
  210. tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
  211. if (!KeepFiles)
  212. KeepFiles = section.Debug;
  213. temp_files = new TempFileCollection (tempdir, KeepFiles);
  214. }
  215. internal string OutputFilesPrefix {
  216. get {
  217. if (outputFilesPrefix == null)
  218. outputFilesPrefix = DEFAULT_ASSEMBLY_BASE_NAME;
  219. return outputFilesPrefix;
  220. }
  221. set {
  222. if (String.IsNullOrEmpty (value))
  223. outputFilesPrefix = DEFAULT_ASSEMBLY_BASE_NAME;
  224. else
  225. outputFilesPrefix = value;
  226. outputAssemblyPrefix = null;
  227. outputAssemblyName = null;
  228. }
  229. }
  230. internal string OutputAssemblyPrefix {
  231. get {
  232. if (outputAssemblyPrefix == null) {
  233. string basePath = temp_files.BasePath;
  234. string baseName = Path.GetFileName (basePath);
  235. string baseDir = Path.GetDirectoryName (basePath);
  236. outputAssemblyPrefix = Path.Combine (baseDir, String.Concat (OutputFilesPrefix, baseName));
  237. }
  238. return outputAssemblyPrefix;
  239. }
  240. }
  241. internal string OutputAssemblyName {
  242. get {
  243. if (outputAssemblyName == null)
  244. outputAssemblyName = OutputAssemblyPrefix + ".dll";
  245. return outputAssemblyName;
  246. }
  247. }
  248. internal TempFileCollection TempFiles {
  249. get { return temp_files; }
  250. }
  251. internal CompilerParameters CompilerOptions {
  252. get { return parameters; }
  253. set { parameters = value; }
  254. }
  255. CodeUnit[] GetUnitsAsArray ()
  256. {
  257. CodeUnit[] result = new CodeUnit [units.Count];
  258. units.CopyTo (result, 0);
  259. return result;
  260. }
  261. internal Dictionary <string, List <CompileUnitPartialType>> PartialTypes {
  262. get {
  263. if (partial_types == null)
  264. partial_types = new Dictionary <string, List <CompileUnitPartialType>> ();
  265. return partial_types;
  266. }
  267. }
  268. Dictionary <string, bool> CodeFiles {
  269. get {
  270. if (code_files == null)
  271. code_files = new Dictionary <string, bool> ();
  272. return code_files;
  273. }
  274. }
  275. List <string> SourceFiles {
  276. get {
  277. if (source_files == null)
  278. source_files = new List <string> ();
  279. return source_files;
  280. }
  281. }
  282. Dictionary <string, string> ResourceFiles {
  283. get {
  284. if (resource_files == null)
  285. resource_files = new Dictionary <string, string> ();
  286. return resource_files;
  287. }
  288. }
  289. internal BuildProvider GetBuildProviderForPhysicalFilePath (string path)
  290. {
  291. if (String.IsNullOrEmpty (path) || path_to_buildprovider == null || path_to_buildprovider.Count == 0)
  292. return null;
  293. BuildProvider ret;
  294. if (path_to_buildprovider.TryGetValue (path, out ret))
  295. return ret;
  296. return null;
  297. }
  298. public void AddAssemblyReference (Assembly a)
  299. {
  300. if (a == null)
  301. throw new ArgumentNullException ("a");
  302. List <Assembly> assemblies = ReferencedAssemblies;
  303. if (assemblies.Contains (a))
  304. return;
  305. assemblies.Add (a);
  306. }
  307. internal void AddAssemblyReference (string assemblyLocation)
  308. {
  309. try {
  310. Assembly asm = Assembly.LoadFrom (assemblyLocation);
  311. if (asm == null)
  312. return;
  313. AddAssemblyReference (asm);
  314. } catch {
  315. // ignore, it will come up later
  316. }
  317. }
  318. internal void AddAssemblyReference (ICollection asmcoll)
  319. {
  320. if (asmcoll == null || asmcoll.Count == 0)
  321. return;
  322. Assembly asm;
  323. foreach (object o in asmcoll) {
  324. asm = o as Assembly;
  325. if (asm == null)
  326. continue;
  327. AddAssemblyReference (asm);
  328. }
  329. }
  330. internal void AddAssemblyReference (List <Assembly> asmlist)
  331. {
  332. if (asmlist == null)
  333. return;
  334. foreach (Assembly a in asmlist) {
  335. if (a == null)
  336. continue;
  337. AddAssemblyReference (a);
  338. }
  339. }
  340. internal void AddCodeCompileUnit (CodeCompileUnit compileUnit)
  341. {
  342. if (compileUnit == null)
  343. throw new ArgumentNullException ("compileUnit");
  344. units.Add (CheckForPartialTypes (new CodeUnit (null, compileUnit)));
  345. }
  346. public void AddCodeCompileUnit (BuildProvider buildProvider, CodeCompileUnit compileUnit)
  347. {
  348. if (buildProvider == null)
  349. throw new ArgumentNullException ("buildProvider");
  350. if (compileUnit == null)
  351. throw new ArgumentNullException ("compileUnit");
  352. units.Add (CheckForPartialTypes (new CodeUnit (buildProvider, compileUnit)));
  353. }
  354. void AddPathToBuilderMap (string path, BuildProvider bp)
  355. {
  356. if (path_to_buildprovider == null)
  357. path_to_buildprovider = new Dictionary <string, BuildProvider> ();
  358. if (path_to_buildprovider.ContainsKey (path))
  359. return;
  360. path_to_buildprovider.Add (path, bp);
  361. }
  362. public TextWriter CreateCodeFile (BuildProvider buildProvider)
  363. {
  364. if (buildProvider == null)
  365. throw new ArgumentNullException ("buildProvider");
  366. // Generate a file name with the correct source language extension
  367. string filename = GetTempFilePhysicalPath (provider.FileExtension);
  368. SourceFiles.Add (filename);
  369. AddPathToBuilderMap (filename, buildProvider);
  370. return new StreamWriter (File.OpenWrite (filename));
  371. }
  372. internal void AddCodeFile (string path)
  373. {
  374. AddCodeFile (path, null, false);
  375. }
  376. internal void AddCodeFile (string path, BuildProvider bp)
  377. {
  378. AddCodeFile (path, bp, false);
  379. }
  380. // The kludge of using ICodePragmaGenerator for C# and VB code files is bad, but
  381. // it's better than allowing for potential DoS while reading a file with arbitrary
  382. // size in memory for use with the CodeSnippetCompileUnit class.
  383. // Files with extensions other than .cs and .vb use CodeSnippetCompileUnit.
  384. internal void AddCodeFile (string path, BuildProvider bp, bool isVirtual)
  385. {
  386. if (String.IsNullOrEmpty (path))
  387. return;
  388. Dictionary <string, bool> codeFiles = CodeFiles;
  389. if (codeFiles.ContainsKey (path))
  390. return;
  391. codeFiles.Add (path, true);
  392. string extension = Path.GetExtension (path);
  393. if (extension == null || extension.Length == 0)
  394. return; // maybe better to throw an exception here?
  395. extension = extension.Substring (1);
  396. string filename = GetTempFilePhysicalPath (extension);
  397. ICodePragmaGenerator pragmaGenerator;
  398. switch (extension.ToLowerInvariant ()) {
  399. case "cs":
  400. pragmaGenerator = new CSharpCodePragmaGenerator ();
  401. break;
  402. case "vb":
  403. pragmaGenerator = new VBCodePragmaGenerator ();
  404. break;
  405. default:
  406. pragmaGenerator = null;
  407. break;
  408. }
  409. if (isVirtual) {
  410. VirtualFile vf = HostingEnvironment.VirtualPathProvider.GetFile (path);
  411. if (vf == null)
  412. throw new HttpException (404, "Virtual file '" + path + "' does not exist.");
  413. if (vf is DefaultVirtualFile)
  414. path = HostingEnvironment.MapPath (path);
  415. CopyFileWithChecksum (vf.Open (), filename, path, pragmaGenerator);
  416. } else
  417. CopyFileWithChecksum (path, filename, path, pragmaGenerator);
  418. if (pragmaGenerator != null) {
  419. if (bp != null)
  420. AddPathToBuilderMap (filename, bp);
  421. SourceFiles.Add (filename);
  422. }
  423. }
  424. void CopyFileWithChecksum (string input, string to, string from, ICodePragmaGenerator pragmaGenerator)
  425. {
  426. CopyFileWithChecksum (new FileStream (input, FileMode.Open, FileAccess.Read), to, from, pragmaGenerator);
  427. }
  428. void CopyFileWithChecksum (Stream input, string to, string from, ICodePragmaGenerator pragmaGenerator)
  429. {
  430. if (pragmaGenerator == null) {
  431. // This is BAD, BAD, BAD! CodeDOM API is really no good in this
  432. // instance.
  433. string filedata;
  434. using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
  435. filedata = sr.ReadToEnd ();
  436. }
  437. var snippet = new CodeSnippetCompileUnit (filedata);
  438. snippet.LinePragma = new CodeLinePragma (from, 1);
  439. filedata = null;
  440. AddCodeCompileUnit (snippet);
  441. snippet = null;
  442. return;
  443. }
  444. MD5 checksum = MD5.Create ();
  445. using (FileStream fs = new FileStream (to, FileMode.Create, FileAccess.Write)) {
  446. using (StreamWriter sw = new StreamWriter (fs, Encoding.UTF8)) {
  447. using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
  448. int count = pragmaGenerator.ReserveSpace (from);
  449. char[] src;
  450. if (count > COPY_BUFFER_SIZE)
  451. src = new char [count];
  452. else
  453. src = new char [COPY_BUFFER_SIZE];
  454. sw.Write (src, 0, count);
  455. do {
  456. count = sr.Read (src, 0, COPY_BUFFER_SIZE);
  457. if (count == 0) {
  458. UpdateChecksum (src, 0, checksum, true);
  459. break;
  460. }
  461. sw.Write (src, 0, count);
  462. UpdateChecksum (src, count, checksum, false);
  463. } while (true);
  464. src = null;
  465. }
  466. }
  467. }
  468. pragmaGenerator.DecorateFile (to, from, checksum, Encoding.UTF8);
  469. }
  470. void UpdateChecksum (char[] buf, int count, MD5 checksum, bool final)
  471. {
  472. byte[] input = Encoding.UTF8.GetBytes (buf, 0, count);
  473. if (final)
  474. checksum.TransformFinalBlock (input, 0, input.Length);
  475. else
  476. checksum.TransformBlock (input, 0, input.Length, input, 0);
  477. input = null;
  478. }
  479. public Stream CreateEmbeddedResource (BuildProvider buildProvider, string name)
  480. {
  481. if (buildProvider == null)
  482. throw new ArgumentNullException ("buildProvider");
  483. if (name == null || name == "")
  484. throw new ArgumentNullException ("name");
  485. string filename = GetTempFilePhysicalPath ("resource");
  486. Stream stream = File.OpenWrite (filename);
  487. ResourceFiles [name] = filename;
  488. return stream;
  489. }
  490. [MonoTODO ("Not implemented, does nothing")]
  491. public void GenerateTypeFactory (string typeName)
  492. {
  493. // Do nothing by now.
  494. }
  495. public string GetTempFilePhysicalPath (string extension)
  496. {
  497. if (extension == null)
  498. throw new ArgumentNullException ("extension");
  499. string newFileName = OutputAssemblyPrefix + "_" + temp_files.Count + "." + extension;
  500. temp_files.AddFile (newFileName, KeepFiles);
  501. return newFileName;
  502. }
  503. public CodeDomProvider CodeDomProvider {
  504. get { return provider; }
  505. }
  506. List <Assembly> ReferencedAssemblies {
  507. get {
  508. if (referenced_assemblies == null)
  509. referenced_assemblies = new List <Assembly> ();
  510. return referenced_assemblies;
  511. }
  512. }
  513. CodeUnit CheckForPartialTypes (CodeUnit codeUnit)
  514. {
  515. CodeTypeDeclarationCollection types;
  516. CompileUnitPartialType partialType;
  517. string partialTypeName;
  518. List <CompileUnitPartialType> tmp;
  519. Dictionary <string, List <CompileUnitPartialType>> partialTypes = PartialTypes;
  520. foreach (CodeNamespace ns in codeUnit.Unit.Namespaces) {
  521. if (ns == null)
  522. continue;
  523. types = ns.Types;
  524. if (types == null || types.Count == 0)
  525. continue;
  526. foreach (CodeTypeDeclaration type in types) {
  527. if (type == null)
  528. continue;
  529. if (type.IsPartial) {
  530. partialType = new CompileUnitPartialType (codeUnit.Unit, ns, type);
  531. partialTypeName = partialType.TypeName;
  532. if (!partialTypes.TryGetValue (partialTypeName, out tmp)) {
  533. tmp = new List <CompileUnitPartialType> (1);
  534. partialTypes.Add (partialTypeName, tmp);
  535. }
  536. tmp.Add (partialType);
  537. }
  538. }
  539. }
  540. return codeUnit;
  541. }
  542. void ProcessPartialTypes ()
  543. {
  544. Dictionary <string, List <CompileUnitPartialType>> partialTypes = PartialTypes;
  545. if (partialTypes.Count == 0)
  546. return;
  547. foreach (KeyValuePair <string, List <CompileUnitPartialType>> kvp in partialTypes)
  548. ProcessType (kvp.Value);
  549. }
  550. void ProcessType (List <CompileUnitPartialType> typeList)
  551. {
  552. CompileUnitPartialType[] types = new CompileUnitPartialType [typeList.Count];
  553. int counter = 0;
  554. foreach (CompileUnitPartialType type in typeList) {
  555. if (counter == 0) {
  556. types [0] = type;
  557. counter++;
  558. continue;
  559. }
  560. for (int i = 0; i < counter; i++)
  561. CompareTypes (types [i], type);
  562. types [counter++] = type;
  563. }
  564. }
  565. void CompareTypes (CompileUnitPartialType source, CompileUnitPartialType target)
  566. {
  567. CodeTypeDeclaration sourceType = source.PartialType;
  568. CodeTypeMemberCollection targetMembers = target.PartialType.Members;
  569. List <CodeTypeMember> membersToRemove = new List <CodeTypeMember> ();
  570. foreach (CodeTypeMember member in targetMembers) {
  571. if (TypeHasMember (sourceType, member))
  572. membersToRemove.Add (member);
  573. }
  574. foreach (CodeTypeMember member in membersToRemove)
  575. targetMembers.Remove (member);
  576. }
  577. bool TypeHasMember (CodeTypeDeclaration type, CodeTypeMember member)
  578. {
  579. if (type == null || member == null)
  580. return false;
  581. return (FindMemberByName (type, member.Name) != null);
  582. }
  583. CodeTypeMember FindMemberByName (CodeTypeDeclaration type, string name)
  584. {
  585. foreach (CodeTypeMember m in type.Members) {
  586. if (m == null || m.Name != name)
  587. continue;
  588. return m;
  589. }
  590. return null;
  591. }
  592. internal CompilerResults BuildAssembly ()
  593. {
  594. return BuildAssembly (null, CompilerOptions);
  595. }
  596. internal CompilerResults BuildAssembly (VirtualPath virtualPath)
  597. {
  598. return BuildAssembly (virtualPath, CompilerOptions);
  599. }
  600. internal CompilerResults BuildAssembly (CompilerParameters options)
  601. {
  602. return BuildAssembly (null, options);
  603. }
  604. internal CompilerResults BuildAssembly (VirtualPath virtualPath, CompilerParameters options)
  605. {
  606. if (options == null)
  607. throw new ArgumentNullException ("options");
  608. options.TempFiles = temp_files;
  609. if (options.OutputAssembly == null)
  610. options.OutputAssembly = OutputAssemblyName;
  611. ProcessPartialTypes ();
  612. CompilerResults results;
  613. CodeUnit [] units = GetUnitsAsArray ();
  614. // Since we may have some source files and some code
  615. // units, we generate code from all of them and then
  616. // compile the assembly from the set of temporary source
  617. // files. This also facilates possible debugging for the
  618. // end user, since they get the code beforehand.
  619. List <string> files = SourceFiles;
  620. Dictionary <string, string> resources = ResourceFiles;
  621. if (units.Length == 0 && files.Count == 0 && resources.Count == 0 && options.EmbeddedResources.Count == 0)
  622. return null;
  623. if (options.IncludeDebugInformation) {
  624. string compilerOptions = options.CompilerOptions;
  625. if (String.IsNullOrEmpty (compilerOptions))
  626. compilerOptions = "/d:DEBUG";
  627. else if (compilerOptions.IndexOf ("d:DEBUG", StringComparison.OrdinalIgnoreCase) == -1)
  628. compilerOptions += " /d:DEBUG";
  629. options.CompilerOptions = compilerOptions;
  630. }
  631. string filename;
  632. StreamWriter sw = null;
  633. foreach (CodeUnit unit in units) {
  634. filename = GetTempFilePhysicalPath (provider.FileExtension);
  635. try {
  636. sw = new StreamWriter (File.OpenWrite (filename), Encoding.UTF8);
  637. provider.GenerateCodeFromCompileUnit (unit.Unit, sw, null);
  638. files.Add (filename);
  639. } catch {
  640. throw;
  641. } finally {
  642. if (sw != null) {
  643. sw.Flush ();
  644. sw.Close ();
  645. }
  646. }
  647. if (unit.BuildProvider != null)
  648. AddPathToBuilderMap (filename, unit.BuildProvider);
  649. }
  650. foreach (KeyValuePair <string, string> de in resources)
  651. options.EmbeddedResources.Add (de.Value);
  652. AddAssemblyReference (BuildManager.GetReferencedAssemblies ());
  653. foreach (Assembly refasm in ReferencedAssemblies) {
  654. string path = new Uri (refasm.CodeBase).LocalPath;
  655. options.ReferencedAssemblies.Add (path);
  656. }
  657. results = provider.CompileAssemblyFromFile (options, files.ToArray ());
  658. if (results.NativeCompilerReturnValue != 0) {
  659. string fileText = null;
  660. try {
  661. using (StreamReader sr = File.OpenText (results.Errors [0].FileName)) {
  662. fileText = sr.ReadToEnd ();
  663. }
  664. } catch (Exception) {}
  665. #if DEBUG
  666. Console.WriteLine ("********************************************************************");
  667. Console.WriteLine ("Compilation failed.");
  668. Console.WriteLine ("Output:");
  669. foreach (string s in results.Output)
  670. Console.WriteLine (" " + s);
  671. Console.WriteLine ("\nErrors:");
  672. foreach (CompilerError err in results.Errors)
  673. Console.WriteLine (err);
  674. Console.WriteLine ("File name: {0}", results.Errors [0].FileName);
  675. Console.WriteLine ("File text:\n{0}\n", fileText);
  676. Console.WriteLine ("********************************************************************");
  677. #endif
  678. throw new CompilationException (virtualPath != null ? virtualPath.Original : String.Empty, results, fileText);
  679. }
  680. Assembly assembly = results.CompiledAssembly;
  681. if (assembly == null) {
  682. if (!File.Exists (options.OutputAssembly)) {
  683. results.TempFiles.Delete ();
  684. throw new CompilationException (virtualPath != null ? virtualPath.Original : String.Empty, results.Errors,
  685. "No assembly returned after compilation!?");
  686. }
  687. try {
  688. results.CompiledAssembly = Assembly.LoadFrom (options.OutputAssembly);
  689. } catch (Exception ex) {
  690. results.TempFiles.Delete ();
  691. throw new HttpException ("Unable to load compiled assembly", ex);
  692. }
  693. }
  694. if (!KeepFiles)
  695. results.TempFiles.Delete ();
  696. return results;
  697. }
  698. }
  699. }