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