2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AssemblyBuilder.cs
1 //
2 // System.Web.Compilation.AssemblyBuilder
3 //
4 // Authors:
5 //      Chris Toshok (toshok@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Marek Habersack (mhabersack@novell.com)
8 //
9 // (C) 2006-2008 Novell, Inc (http://www.novell.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 #if NET_2_0
34
35 using System;
36 using System.CodeDom;
37 using System.CodeDom.Compiler;
38 using System.Collections;
39 using System.Collections.Generic;
40 using System.Collections.Specialized;
41 using System.IO;
42 using System.Security.Cryptography;
43 using System.Reflection;
44 using System.Text;
45 using System.Web.Configuration;
46 using System.Web.Util;
47 using System.Web.Hosting;
48
49 namespace System.Web.Compilation {
50         class CompileUnitPartialType
51         {
52                 public readonly CodeCompileUnit Unit;
53                 public readonly CodeNamespace ParentNamespace;
54                 public readonly CodeTypeDeclaration PartialType;
55
56                 string typeName;
57                 
58                 public string TypeName {
59                         get {
60                                 if (typeName == null) {
61                                         if (ParentNamespace == null || PartialType == null)
62                                                 return null;
63                                         
64                                         typeName = ParentNamespace.Name;
65                                         if (String.IsNullOrEmpty (typeName))
66                                                 typeName = PartialType.Name;
67                                         else
68                                                 typeName += "." + PartialType.Name;
69                                 }
70
71                                 return typeName;
72                         }
73                 }
74                 
75                 public CompileUnitPartialType (CodeCompileUnit unit, CodeNamespace parentNamespace, CodeTypeDeclaration type)
76                 {
77                         this.Unit = unit;
78                         this.ParentNamespace = parentNamespace;
79                         this.PartialType = type;
80                 }
81         }
82         
83         public class AssemblyBuilder
84         {
85                 struct CodeUnit
86                 {
87                         public readonly BuildProvider BuildProvider;
88                         public readonly CodeCompileUnit Unit;
89
90                         public CodeUnit (BuildProvider bp, CodeCompileUnit unit)
91                         {
92                                 this.BuildProvider = bp;
93                                 this.Unit = unit;
94                         }
95                 }
96
97                 interface ICodePragmaGenerator
98                 {
99                         int ReserveSpace (string filename);
100                         void DecorateFile (string path, string filename, MD5 checksum, Encoding enc);
101                 }
102
103                 class CSharpCodePragmaGenerator : ICodePragmaGenerator
104                 {
105                         // Copied from CSharpCodeGenerator.cs
106                         string QuoteSnippetString (string value)
107                         {
108                                 // FIXME: this is weird, but works.
109                                 string output = value.Replace ("\\", "\\\\");
110                                 output = output.Replace ("\"", "\\\"");
111                                 output = output.Replace ("\t", "\\t");
112                                 output = output.Replace ("\r", "\\r");
113                                 output = output.Replace ("\n", "\\n");
114                                 
115                                 return "\"" + output + "\"";
116                         }
117
118                         string ChecksumToHex (MD5 checksum)
119                         {
120                                 var ret = new StringBuilder ();
121                                 foreach (byte b in checksum.Hash)
122                                         ret.Append (b.ToString ("X2"));
123
124                                 return ret.ToString ();
125                         }
126
127                         const int pragmaChecksumStaticCount = 23;
128                         const int pragmaLineStaticCount = 8;
129                         const int md5ChecksumCount = 32;
130                         
131                         public int ReserveSpace (string filename) 
132                         {
133                                 return pragmaChecksumStaticCount +
134                                         pragmaLineStaticCount +
135                                         md5ChecksumCount +
136                                         (QuoteSnippetString (filename).Length * 2) +
137                                         (Environment.NewLine.Length * 3) +
138                                         BaseCompiler.HashMD5.ToString ("B").Length;
139                         }
140                         
141                         public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
142                         {
143                                 string newline = Environment.NewLine;
144                                 var sb = new StringBuilder ();
145                                 
146                                 sb.AppendFormat ("#pragma checksum {0} \"{1}\" \"{2}\"{3}{3}",
147                                                  QuoteSnippetString (filename),
148                                                  BaseCompiler.HashMD5.ToString ("B"),
149                                                  ChecksumToHex (checksum),
150                                                  newline);
151                                 sb.AppendFormat ("#line 1 {0}{1}", QuoteSnippetString (filename), newline);
152
153                                 byte[] bytes = enc.GetBytes (sb.ToString ());
154                                 using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
155                                         fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
156                                         fs.Write (bytes, 0, bytes.Length);
157                                         bytes = null;
158                                 
159                                         sb.Length = 0;
160                                         sb.AppendFormat ("{0}#line default{0}#line hidden{0}", newline);
161                                         bytes = Encoding.UTF8.GetBytes (sb.ToString ());
162                                 
163                                         fs.Seek (0, SeekOrigin.End);
164                                         fs.Write (bytes, 0, bytes.Length);
165                                 }
166                                 
167                                 sb = null;
168                                 bytes = null;
169                         }
170                 }
171
172                 class VBCodePragmaGenerator : ICodePragmaGenerator
173                 {
174                         const int pragmaExternalSourceCount = 21;
175                         public int ReserveSpace (string filename)
176                         {
177                                 return pragmaExternalSourceCount +
178                                         filename.Length +
179                                         (Environment.NewLine.Length);
180                         }
181                         
182                         public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
183                         {
184                                 string newline = Environment.NewLine;
185                                 var sb = new StringBuilder ();
186
187                                 sb.AppendFormat ("#ExternalSource(\"{0}\",1){1}", filename, newline);
188                                 byte[] bytes = enc.GetBytes (sb.ToString ());
189                                 using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
190                                         fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
191                                         fs.Write (bytes, 0, bytes.Length);
192                                         bytes = null;
193
194                                         sb.Length = 0;
195                                         sb.AppendFormat ("{0}#End ExternalSource{0}", newline);
196                                         bytes = enc.GetBytes (sb.ToString ());
197                                         fs.Seek (0, SeekOrigin.End);
198                                         fs.Write (bytes, 0, bytes.Length);
199                                 }
200                                 sb = null;
201                                 bytes = null;
202                         }
203                 }
204                 
205                 const string DEFAULT_ASSEMBLY_BASE_NAME = "App_Web_";
206                 const int COPY_BUFFER_SIZE = 8192;
207                 
208                 static bool KeepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
209                 
210                 CodeDomProvider provider;
211                 CompilerParameters parameters;
212
213                 Dictionary <string, bool> code_files;
214                 Dictionary <string, List <CompileUnitPartialType>> partial_types;
215                 Dictionary <string, BuildProvider> path_to_buildprovider;
216                 List <CodeUnit> units;
217                 List <string> source_files;
218                 List <Assembly> referenced_assemblies;
219                 Dictionary <string, string> resource_files;
220                 TempFileCollection temp_files;
221                 string outputFilesPrefix;
222                 string outputAssemblyPrefix;
223                 string outputAssemblyName;
224                 
225                 internal AssemblyBuilder (CodeDomProvider provider)
226                 : this (null, provider, DEFAULT_ASSEMBLY_BASE_NAME)
227                 {}
228
229                 internal AssemblyBuilder (CodeDomProvider provider, string assemblyBaseName)
230                 : this (null, provider, assemblyBaseName)
231                 {}
232
233                 internal AssemblyBuilder (VirtualPath virtualPath, CodeDomProvider provider)
234                 : this (virtualPath, provider, DEFAULT_ASSEMBLY_BASE_NAME)
235                 {}
236                 
237                 internal AssemblyBuilder (VirtualPath virtualPath, CodeDomProvider provider, string assemblyBaseName)
238                 {
239                         this.provider = provider;
240                         this.outputFilesPrefix = assemblyBaseName ?? DEFAULT_ASSEMBLY_BASE_NAME;
241                         
242                         units = new List <CodeUnit> ();
243
244                         CompilationSection section;
245                         section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
246                         string tempdir = section.TempDirectory;
247                         if (String.IsNullOrEmpty (tempdir))
248                                 tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
249
250                         if (!KeepFiles)
251                                 KeepFiles = section.Debug;
252                         
253                         temp_files = new TempFileCollection (tempdir, KeepFiles);
254                 }
255
256                 internal string OutputFilesPrefix {
257                         get {
258                                 if (outputFilesPrefix == null)
259                                         outputFilesPrefix = DEFAULT_ASSEMBLY_BASE_NAME;
260
261                                 return outputFilesPrefix;
262                         }
263
264                         set {
265                                 if (String.IsNullOrEmpty (value))
266                                         outputFilesPrefix = DEFAULT_ASSEMBLY_BASE_NAME;
267                                 else
268                                         outputFilesPrefix = value;
269                                 outputAssemblyPrefix = null;
270                                 outputAssemblyName = null;
271                         }
272                 }
273                 
274                 internal string OutputAssemblyPrefix {
275                         get {
276                                 if (outputAssemblyPrefix == null) {
277                                         string basePath = temp_files.BasePath;
278                                         string baseName = Path.GetFileName (basePath);
279                                         string baseDir = Path.GetDirectoryName (basePath);
280
281                                         outputAssemblyPrefix = Path.Combine (baseDir, String.Concat (OutputFilesPrefix, baseName));
282                                 }
283
284                                 return outputAssemblyPrefix;
285                         }
286                 }
287
288                 internal string OutputAssemblyName {
289                         get {
290                                 if (outputAssemblyName == null)
291                                         outputAssemblyName = OutputAssemblyPrefix + ".dll";
292
293                                 return outputAssemblyName;
294                         }
295                 }
296                 
297                 internal TempFileCollection TempFiles {
298                         get { return temp_files; }
299                 }
300
301                 internal CompilerParameters CompilerOptions {
302                         get { return parameters; }
303                         set { parameters = value; }
304                 }
305                 
306                 CodeUnit[] GetUnitsAsArray ()
307                 {
308                         CodeUnit[] result = new CodeUnit [units.Count];
309                         units.CopyTo (result, 0);
310                         return result;
311                 }
312                 
313                 internal Dictionary <string, List <CompileUnitPartialType>> PartialTypes {
314                         get {
315                                 if (partial_types == null)
316                                         partial_types = new Dictionary <string, List <CompileUnitPartialType>> ();
317                                 return partial_types;
318                         }
319                 }
320                 
321                 Dictionary <string, bool> CodeFiles {
322                         get {
323                                 if (code_files == null)
324                                         code_files = new Dictionary <string, bool> ();
325                                 return code_files;
326                         }
327                 }
328                 
329                 List <string> SourceFiles {
330                         get {
331                                 if (source_files == null)
332                                         source_files = new List <string> ();
333                                 return source_files;
334                         }
335                 }
336
337                 Dictionary <string, string> ResourceFiles {
338                         get {
339                                 if (resource_files == null)
340                                         resource_files = new Dictionary <string, string> ();
341                                 return resource_files;
342                         }
343                 }
344
345                 internal BuildProvider GetBuildProviderForPhysicalFilePath (string path)
346                 {
347                         if (String.IsNullOrEmpty (path) || path_to_buildprovider == null || path_to_buildprovider.Count == 0)
348                                 return null;
349
350                         BuildProvider ret;
351                         if (path_to_buildprovider.TryGetValue (path, out ret))
352                                 return ret;
353
354                         return null;
355                 }
356                 
357                 public void AddAssemblyReference (Assembly a)
358                 {
359                         if (a == null)
360                                 throw new ArgumentNullException ("a");
361
362                         List <Assembly> assemblies = ReferencedAssemblies;
363                         
364                         if (assemblies.Contains (a))
365                                 return;
366                         
367                         assemblies.Add (a);
368                 }
369
370                 internal void AddAssemblyReference (string assemblyLocation)
371                 {
372                         try {
373                                 Assembly asm = Assembly.LoadFrom (assemblyLocation);
374                                 if (asm == null)
375                                         return;
376
377                                 AddAssemblyReference (asm);
378                         } catch {
379                                 // ignore, it will come up later
380                         }
381                 }
382
383                 internal void AddAssemblyReference (ICollection asmcoll)
384                 {
385                         if (asmcoll == null || asmcoll.Count == 0)
386                                 return;
387
388                         Assembly asm;
389                         foreach (object o in asmcoll) {
390                                 asm = o as Assembly;
391                                 if (asm == null)
392                                         continue;
393
394                                 AddAssemblyReference (asm);
395                         }
396                 }
397                 
398                 internal void AddAssemblyReference (List <Assembly> asmlist)
399                 {
400                         if (asmlist == null)
401                                 return;
402                         
403                         foreach (Assembly a in asmlist) {
404                                 if (a == null)
405                                         continue;
406
407                                 AddAssemblyReference (a);
408                         }
409                 }
410                 
411                 internal void AddCodeCompileUnit (CodeCompileUnit compileUnit)
412                 {
413                         if (compileUnit == null)
414                                 throw new ArgumentNullException ("compileUnit");
415                         units.Add (CheckForPartialTypes (new CodeUnit (null, compileUnit)));
416                 }
417                                 
418                 public void AddCodeCompileUnit (BuildProvider buildProvider, CodeCompileUnit compileUnit)
419                 {
420                         if (buildProvider == null)
421                                 throw new ArgumentNullException ("buildProvider");
422
423                         if (compileUnit == null)
424                                 throw new ArgumentNullException ("compileUnit");
425
426                         units.Add (CheckForPartialTypes (new CodeUnit (buildProvider, compileUnit)));
427                 }
428
429                 void AddPathToBuilderMap (string path, BuildProvider bp)
430                 {
431                         if (path_to_buildprovider == null)
432                                 path_to_buildprovider = new Dictionary <string, BuildProvider> ();
433
434                         if (path_to_buildprovider.ContainsKey (path))
435                                 return;
436
437                         path_to_buildprovider.Add (path, bp);
438                 }
439                 
440                 public TextWriter CreateCodeFile (BuildProvider buildProvider)
441                 {
442                         if (buildProvider == null)
443                                 throw new ArgumentNullException ("buildProvider");
444
445                         // Generate a file name with the correct source language extension
446                         string filename = GetTempFilePhysicalPath (provider.FileExtension);
447                         SourceFiles.Add (filename);
448                         AddPathToBuilderMap (filename, buildProvider);
449                         return new StreamWriter (File.OpenWrite (filename));
450                 }
451
452                 internal void AddCodeFile (string path)
453                 {
454                         AddCodeFile (path, null, false);
455                 }
456
457                 internal void AddCodeFile (string path, BuildProvider bp)
458                 {
459                         AddCodeFile (path, bp, false);
460                 }
461
462                 // The kludge of using ICodePragmaGenerator for C# and VB code files is bad, but
463                 // it's better than allowing for potential DoS while reading a file with arbitrary
464                 // size in memory for use with the CodeSnippetCompileUnit class.
465                 // Files with extensions other than .cs and .vb use CodeSnippetCompileUnit.
466                 internal void AddCodeFile (string path, BuildProvider bp, bool isVirtual)
467                 {
468                         if (String.IsNullOrEmpty (path))
469                                 return;
470
471                         Dictionary <string, bool> codeFiles = CodeFiles;
472                         if (codeFiles.ContainsKey (path))
473                                 return;
474                         
475                         codeFiles.Add (path, true);
476                         
477                         string extension = Path.GetExtension (path);
478                         if (extension == null || extension.Length == 0)
479                                 return; // maybe better to throw an exception here?
480                         extension = extension.Substring (1);
481                         string filename = GetTempFilePhysicalPath (extension);
482                         ICodePragmaGenerator pragmaGenerator;
483                         
484                         switch (extension.ToLower ()) {
485                                 case "cs":
486                                         pragmaGenerator = new CSharpCodePragmaGenerator ();
487                                         break;
488
489                                 case "vb":
490                                         pragmaGenerator = new VBCodePragmaGenerator ();
491                                         break;
492
493                                 default:
494                                         pragmaGenerator = null;
495                                         break;
496                         }
497                         
498                         if (isVirtual) {
499                                 VirtualFile vf = HostingEnvironment.VirtualPathProvider.GetFile (path);
500                                 if (vf == null)
501                                         throw new HttpException (404, "Virtual file '" + path + "' does not exist.");
502
503                                 if (vf is DefaultVirtualFile)
504                                         path = HostingEnvironment.MapPath (path);
505                                 CopyFileWithChecksum (vf.Open (), filename, path, pragmaGenerator);
506                         } else
507                                 CopyFileWithChecksum (path, filename, path, pragmaGenerator);
508
509                         if (pragmaGenerator != null) {
510                                 if (bp != null)
511                                         AddPathToBuilderMap (filename, bp);
512                         
513                                 SourceFiles.Add (filename);
514                         }
515                 }
516
517                 void CopyFileWithChecksum (string input, string to, string from, ICodePragmaGenerator pragmaGenerator)
518                 {
519                         CopyFileWithChecksum (new FileStream (input, FileMode.Open, FileAccess.Read), to, from, pragmaGenerator);
520                 }
521                 
522                 void CopyFileWithChecksum (Stream input, string to, string from, ICodePragmaGenerator pragmaGenerator)
523                 {
524                         if (pragmaGenerator == null) {
525                                 // This is BAD, BAD, BAD! CodeDOM API is really no good in this
526                                 // instance.
527                                 string filedata;
528                                 using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
529                                         filedata = sr.ReadToEnd ();
530                                 }
531
532                                 var snippet = new CodeSnippetCompileUnit (filedata);
533                                 snippet.LinePragma = new CodeLinePragma (from, 1);
534                                 filedata = null;
535                                 AddCodeCompileUnit (snippet);
536                                 snippet = null;
537                                 
538                                 return;
539                         }
540                         
541                         MD5 checksum = MD5.Create ();
542                         using (FileStream fs = new FileStream (to, FileMode.Create, FileAccess.Write)) {
543                                 using (StreamWriter sw = new StreamWriter (fs, Encoding.UTF8)) {
544                                         using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
545                                                 int count = pragmaGenerator.ReserveSpace (from);
546                                                 char[] src;
547                                                 
548                                                 if (count > COPY_BUFFER_SIZE)
549                                                         src = new char [count];
550                                                 else
551                                                         src = new char [COPY_BUFFER_SIZE];
552
553                                                 sw.Write (src, 0, count);
554                                                 do {
555                                                         count = sr.Read (src, 0, COPY_BUFFER_SIZE);
556                                                         if (count == 0) {
557                                                                 UpdateChecksum (src, 0, checksum, true);
558                                                                 break;
559                                                         }
560                                                 
561                                                         sw.Write (src, 0, count);
562                                                         UpdateChecksum (src, count, checksum, false);
563                                                 } while (true);
564                                                 src = null;
565                                         }
566                                 }
567                         }
568                         pragmaGenerator.DecorateFile (to, from, checksum, Encoding.UTF8);
569                 }
570
571                 void UpdateChecksum (char[] buf, int count, MD5 checksum, bool final)
572                 {
573                         byte[] input = Encoding.UTF8.GetBytes (buf, 0, count);
574
575                         if (final)
576                                 checksum.TransformFinalBlock (input, 0, input.Length);
577                         else
578                                 checksum.TransformBlock (input, 0, input.Length, input, 0);
579                         input = null;
580                 }
581                 
582                 public Stream CreateEmbeddedResource (BuildProvider buildProvider, string name)
583                 {
584                         if (buildProvider == null)
585                                 throw new ArgumentNullException ("buildProvider");
586
587                         if (name == null || name == "")
588                                 throw new ArgumentNullException ("name");
589
590                         string filename = GetTempFilePhysicalPath ("resource");
591                         Stream stream = File.OpenWrite (filename);
592                         ResourceFiles [name] = filename;
593                         return stream;
594                 }
595
596                 [MonoTODO ("Not implemented, does nothing")]
597                 public void GenerateTypeFactory (string typeName)
598                 {
599                         // Do nothing by now.
600                 }
601
602                 public string GetTempFilePhysicalPath (string extension)
603                 {
604                         if (extension == null)
605                                 throw new ArgumentNullException ("extension");
606
607                         string newFileName = OutputAssemblyPrefix + "_" + temp_files.Count + "." + extension;
608                         temp_files.AddFile (newFileName, KeepFiles);
609
610                         return newFileName;
611                 }
612
613                 public CodeDomProvider CodeDomProvider {
614                         get { return provider; }
615                 }
616
617                 List <Assembly> ReferencedAssemblies {
618                         get {
619                                 if (referenced_assemblies == null)
620                                         referenced_assemblies = new List <Assembly> ();
621
622                                 return referenced_assemblies;
623                         }
624                 }
625                 
626                 CodeUnit CheckForPartialTypes (CodeUnit codeUnit)
627                 {
628                         CodeTypeDeclarationCollection types;
629                         CompileUnitPartialType partialType;
630                         string partialTypeName;
631                         List <CompileUnitPartialType> tmp;
632                         Dictionary <string, List <CompileUnitPartialType>> partialTypes = PartialTypes;
633                         
634                         foreach (CodeNamespace ns in codeUnit.Unit.Namespaces) {
635                                 if (ns == null)
636                                         continue;
637                                 types = ns.Types;
638                                 if (types == null || types.Count == 0)
639                                         continue;
640
641                                 foreach (CodeTypeDeclaration type in types) {
642                                         if (type == null)
643                                                 continue;
644
645                                         if (type.IsPartial) {
646                                                 partialType = new CompileUnitPartialType (codeUnit.Unit, ns, type);
647                                                 partialTypeName = partialType.TypeName;
648                                                 
649                                                 if (!partialTypes.TryGetValue (partialTypeName, out tmp)) {
650                                                         tmp = new List <CompileUnitPartialType> (1);
651                                                         partialTypes.Add (partialTypeName, tmp);
652                                                 }
653                                                 tmp.Add (partialType);
654                                         }
655                                 }
656                         }
657                                                 
658                         return codeUnit;
659                 }
660                 
661                 void ProcessPartialTypes ()
662                 {
663                         Dictionary <string, List <CompileUnitPartialType>> partialTypes = PartialTypes;
664                         if (partialTypes.Count == 0)
665                                 return;
666                         
667                         foreach (KeyValuePair <string, List <CompileUnitPartialType>> kvp in partialTypes)
668                                 ProcessType (kvp.Value);
669                 }
670
671                 void ProcessType (List <CompileUnitPartialType> typeList)
672                 {
673                         CompileUnitPartialType[] types = new CompileUnitPartialType [typeList.Count];
674                         int counter = 0;
675                         
676                         foreach (CompileUnitPartialType type in typeList) {
677                                 if (counter == 0) {
678                                         types [0] = type;
679                                         counter++;
680                                         continue;
681                                 }
682
683                                 for (int i = 0; i < counter; i++)
684                                         CompareTypes (types [i], type);
685                                 types [counter++] = type;
686                         }
687                 }
688
689                 void CompareTypes (CompileUnitPartialType source, CompileUnitPartialType target)
690                 {
691                         CodeTypeDeclaration sourceType = source.PartialType;
692                         CodeTypeMemberCollection targetMembers = target.PartialType.Members;
693                         List <CodeTypeMember> membersToRemove = new List <CodeTypeMember> ();
694                         
695                         foreach (CodeTypeMember member in targetMembers) {
696                                 if (TypeHasMember (sourceType, member))
697                                         membersToRemove.Add (member);
698                         }
699
700                         foreach (CodeTypeMember member in membersToRemove)
701                                 targetMembers.Remove (member);
702                 }
703
704                 bool TypeHasMember (CodeTypeDeclaration type, CodeTypeMember member)
705                 {
706                         if (type == null || member == null)
707                                 return false;
708
709                         return (FindMemberByName (type, member.Name) != null);
710                 }
711
712                 CodeTypeMember FindMemberByName (CodeTypeDeclaration type, string name)
713                 {
714                         foreach (CodeTypeMember m in type.Members) {
715                                 if (m == null || m.Name != name)
716                                         continue;
717                                 return m;
718                         }
719
720                         return null;
721                 }
722
723                 internal CompilerResults BuildAssembly ()
724                 {
725                         return BuildAssembly (null, CompilerOptions);
726                 }
727                 
728                 internal CompilerResults BuildAssembly (VirtualPath virtualPath)
729                 {
730                         return BuildAssembly (virtualPath, CompilerOptions);
731                 }
732                 
733                 internal CompilerResults BuildAssembly (CompilerParameters options)
734                 {
735                         return BuildAssembly (null, options);
736                 }
737                 
738                 internal CompilerResults BuildAssembly (VirtualPath virtualPath, CompilerParameters options)
739                 {
740                         if (options == null)
741                                 throw new ArgumentNullException ("options");
742
743                         options.TempFiles = temp_files;
744                         if (options.OutputAssembly == null)
745                                 options.OutputAssembly = OutputAssemblyName;
746
747                         ProcessPartialTypes ();
748                         
749                         CompilerResults results;
750                         CodeUnit [] units = GetUnitsAsArray ();
751
752                         // Since we may have some source files and some code
753                         // units, we generate code from all of them and then
754                         // compile the assembly from the set of temporary source
755                         // files. This also facilates possible debugging for the
756                         // end user, since they get the code beforehand.
757                         List <string> files = SourceFiles;
758                         Dictionary <string, string> resources = ResourceFiles;
759
760                         if (units.Length == 0 && files.Count == 0 && resources.Count == 0 && options.EmbeddedResources.Count == 0)
761                                 return null;
762
763                         if (options.IncludeDebugInformation) {
764                                 string compilerOptions = options.CompilerOptions;
765                                 if (String.IsNullOrEmpty (compilerOptions))
766                                         compilerOptions = "/d:DEBUG";
767                                 else if (compilerOptions.IndexOf ("d:DEBUG", StringComparison.OrdinalIgnoreCase) == -1)
768                                         compilerOptions += " /d:DEBUG";
769                                 
770                                 options.CompilerOptions = compilerOptions;
771                         }
772                         
773                         string filename;
774                         StreamWriter sw = null;
775                         
776                         foreach (CodeUnit unit in units) {
777                                 filename = GetTempFilePhysicalPath (provider.FileExtension);
778                                 try {
779                                         sw = new StreamWriter (File.OpenWrite (filename), Encoding.UTF8);
780                                         provider.GenerateCodeFromCompileUnit (unit.Unit, sw, null);
781                                         files.Add (filename);
782                                 } catch {
783                                         throw;
784                                 } finally {
785                                         if (sw != null) {
786                                                 sw.Flush ();
787                                                 sw.Close ();
788                                         }
789                                 }
790
791                                 if (unit.BuildProvider != null)
792                                         AddPathToBuilderMap (filename, unit.BuildProvider);
793                         }
794
795                         foreach (KeyValuePair <string, string> de in resources)
796                                 options.EmbeddedResources.Add (de.Value);
797
798                         AddAssemblyReference (BuildManager.GetReferencedAssemblies ());
799                         foreach (Assembly refasm in ReferencedAssemblies) {
800                                 string path = new Uri (refasm.CodeBase).LocalPath;
801                                 options.ReferencedAssemblies.Add (path);
802                         }
803                         
804                         results = provider.CompileAssemblyFromFile (options, files.ToArray ());
805
806                         if (results.NativeCompilerReturnValue != 0) {
807                                 string fileText = null;
808                                 try {
809                                         using (StreamReader sr = File.OpenText (results.Errors [0].FileName)) {
810                                                 fileText = sr.ReadToEnd ();
811                                         }
812                                 } catch (Exception) {}
813                                 
814 #if DEBUG
815                                 Console.WriteLine ("********************************************************************");
816                                 Console.WriteLine ("Compilation failed.");
817                                 Console.WriteLine ("Output:");
818                                 foreach (string s in results.Output)
819                                         Console.WriteLine ("  " + s);
820                                 Console.WriteLine ("\nErrors:");
821                                 foreach (CompilerError err in results.Errors)
822                                         Console.WriteLine (err);
823                                 Console.WriteLine ("File name: {0}", results.Errors [0].FileName);
824                                 Console.WriteLine ("File text:\n{0}\n", fileText);
825                                 Console.WriteLine ("********************************************************************");
826 #endif
827                                 
828                                 throw new CompilationException (virtualPath != null ? virtualPath.Original : String.Empty, results, fileText);
829                         }
830                         
831                         Assembly assembly = results.CompiledAssembly;
832                         if (assembly == null) {
833                                 if (!File.Exists (options.OutputAssembly)) {
834                                         results.TempFiles.Delete ();
835                                         throw new CompilationException (virtualPath != null ? virtualPath.Original : String.Empty, results.Errors,
836                                                 "No assembly returned after compilation!?");
837                                 }
838
839                                 try {
840                                         results.CompiledAssembly = Assembly.LoadFrom (options.OutputAssembly);
841                                 } catch (Exception ex) {
842                                         results.TempFiles.Delete ();
843                                         throw new HttpException ("Unable to load compiled assembly", ex);
844                                 }
845                         }
846
847                         if (!KeepFiles)
848                                 results.TempFiles.Delete ();
849                         return results;
850                 }
851         }
852 }
853 #endif
854