[asp.net] Avoid passing duplicate assemblies on mcs command line.
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AppCodeCompiler.cs
1 //
2 // System.Web.Compilation.AppCodeCompiler: A compiler for the App_Code folder
3 //
4 // Authors:
5 //   Marek Habersack (grendello@gmail.com)
6 //
7 // (C) 2006 Marek Habersack
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.CodeDom;
33 using System.CodeDom.Compiler;
34 using System.Configuration;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.Collections.Specialized;
38 using System.Globalization;
39 using System.IO;
40 using System.Reflection;
41 using System.Web;
42 using System.Web.Configuration;
43 using System.Web.Profile;
44 using System.Web.Util;
45
46 namespace System.Web.Compilation
47 {
48         class AssemblyPathResolver
49         {
50                 static Dictionary <string, string> assemblyCache;
51
52                 static AssemblyPathResolver ()
53                 {
54                         assemblyCache = new Dictionary <string, string> ();
55                 }
56
57                 public static string GetAssemblyPath (string assemblyName)
58                 {
59                         lock (assemblyCache) {
60                                 if (assemblyCache.ContainsKey (assemblyName))
61                                         return assemblyCache [assemblyName];
62
63                                 Assembly asm = null;
64                                 Exception error = null;
65                                 if (assemblyName.IndexOf (',') != -1) {
66                                         try {
67                                                 asm = Assembly.Load (assemblyName);
68                                         } catch (Exception e) {
69                                                 error = e;
70                                         }
71                                 }
72
73                                 if (asm == null) {
74                                         try {
75                                                 asm = Assembly.LoadWithPartialName (assemblyName);
76                                         } catch (Exception e) {
77                                                 error = e;
78                                         }
79                                 }
80                         
81                                 if (asm == null)
82                                         throw new HttpException (String.Format ("Unable to find assembly {0}", assemblyName), error);
83
84                                 string path = new Uri (asm.CodeBase).LocalPath;
85                                 assemblyCache.Add (assemblyName, path);
86                                 return path;
87                         }
88                 }
89         }
90         
91         internal class AppCodeAssembly
92         {
93                 List<string> files;
94                 List<CodeCompileUnit> units;
95                 
96                 string name;
97                 string path;
98                 bool validAssembly;
99                 string outputAssemblyName;
100
101                 public string OutputAssemblyName
102                 {
103                         get {
104                                 return outputAssemblyName;
105                         }
106                 }
107                 
108                 public bool IsValid
109                 {
110                         get { return validAssembly; }
111                 }
112
113                 public string SourcePath
114                 {
115                         get { return path; }
116                 }
117
118 // temporary
119                 public string Name
120                 {
121                         get { return name; }
122                 }
123                 
124                 public List<string> Files
125                 {
126                         get { return files; }
127                 }
128 // temporary
129                 
130                 public AppCodeAssembly (string name, string path)
131                 {
132                         this.files = new List<string> ();
133                         this.units = new List<CodeCompileUnit> ();
134                         this.validAssembly = true;
135                         this.name = name;
136                         this.path = path;
137                 }
138
139                 public void AddFile (string path)
140                 {
141                         files.Add (path);
142                 }
143
144                 public void AddUnit (CodeCompileUnit unit)
145                 {
146                         units.Add (unit);
147                 }
148                 
149                 object OnCreateTemporaryAssemblyFile (string path)
150                 {
151                         FileStream f = new FileStream (path, FileMode.CreateNew);
152                         f.Close ();
153                         return path;
154                 }
155                 
156                 // Build and add the assembly to the BuildManager's
157                 // CodeAssemblies collection
158                 public void Build (string[] binAssemblies)
159                 {
160                         Type compilerProvider = null;
161                         CompilerInfo compilerInfo = null, cit;
162                         string extension, language, cpfile = null;
163                         List<string> knownfiles = new List<string>();
164                         List<string> unknownfiles = new List<string>();
165                         
166                         // First make sure all the files are in the same
167                         // language
168                         bool known = false;
169                         foreach (string f in files) {
170                                 known = true;
171                                 language = null;
172                                 
173                                 extension = Path.GetExtension (f);
174                                 if (String.IsNullOrEmpty (extension) || !CodeDomProvider.IsDefinedExtension (extension))
175                                         known = false;
176                                 if (known) {
177                                         language = CodeDomProvider.GetLanguageFromExtension(extension);
178                                         if (!CodeDomProvider.IsDefinedLanguage (language))
179                                                 known = false;
180                                 }
181                                 if (!known || language == null) {
182                                         unknownfiles.Add (f);
183                                         continue;
184                                 }
185                                 
186                                 cit = CodeDomProvider.GetCompilerInfo (language);
187                                 if (cit == null || !cit.IsCodeDomProviderTypeValid)
188                                         continue;
189                                 if (compilerProvider == null) {
190                                         cpfile = f;
191                                         compilerProvider = cit.CodeDomProviderType;
192                                         compilerInfo = cit;
193                                 } else if (compilerProvider != cit.CodeDomProviderType)
194                                         throw new HttpException (
195                                                 String.Format (
196                                                         "Files {0} and {1} are in different languages - they cannot be compiled into the same assembly",
197                                                         Path.GetFileName (cpfile),
198                                                         Path.GetFileName (f)));
199                                 knownfiles.Add (f);
200                         }
201
202                         CodeDomProvider provider = null;
203                         CompilationSection compilationSection = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
204                         if (compilerInfo == null) {
205                                 if (!CodeDomProvider.IsDefinedLanguage (compilationSection.DefaultLanguage))
206                                         throw new HttpException ("Failed to retrieve default source language");
207                                 compilerInfo = CodeDomProvider.GetCompilerInfo (compilationSection.DefaultLanguage);
208                                 if (compilerInfo == null || !compilerInfo.IsCodeDomProviderTypeValid)
209                                         throw new HttpException ("Internal error while initializing application");
210                         }
211
212                         provider = compilerInfo.CreateProvider ();
213                         if (provider == null)
214                                 throw new HttpException ("A code provider error occurred while initializing application.");
215
216                         AssemblyBuilder abuilder = new AssemblyBuilder (provider);
217                         foreach (string file in knownfiles)
218                                 abuilder.AddCodeFile (file);
219                         foreach (CodeCompileUnit unit in units)
220                                 abuilder.AddCodeCompileUnit (unit);
221                         
222                         BuildProvider bprovider;
223                         CompilerParameters parameters = compilerInfo.CreateDefaultCompilerParameters ();
224                         parameters.IncludeDebugInformation = compilationSection.Debug;
225                         
226                         if (binAssemblies != null && binAssemblies.Length > 0) {
227                                 StringCollection parmRefAsm = parameters.ReferencedAssemblies;
228                                 foreach (string binAsm in binAssemblies) {
229                                         if (parmRefAsm.Contains (binAsm))
230                                                 continue;
231                                         
232                                         parmRefAsm.Add (binAsm);
233                                 }
234                         }
235                         
236                         if (compilationSection != null) {
237                                 foreach (AssemblyInfo ai in compilationSection.Assemblies)
238                                         if (ai.Assembly != "*") {
239                                                 try {
240                                                         parameters.ReferencedAssemblies.Add (
241                                                                 AssemblyPathResolver.GetAssemblyPath (ai.Assembly));
242                                                 } catch (Exception ex) {
243                                                         throw new HttpException (
244                                                                 String.Format ("Could not find assembly {0}.", ai.Assembly),
245                                                                 ex);
246                                                 }
247                                         }
248                                 
249                                 BuildProviderCollection buildProviders = compilationSection.BuildProviders;
250                                 
251                                 foreach (string file in unknownfiles) {
252                                         bprovider = GetBuildProviderFor (file, buildProviders);
253                                         if (bprovider == null)
254                                                 continue;
255                                         bprovider.GenerateCode (abuilder);
256                                 }
257                         }
258
259                         if (knownfiles.Count == 0 && unknownfiles.Count == 0 && units.Count == 0)
260                                 return;
261                         
262                         outputAssemblyName = (string)FileUtils.CreateTemporaryFile (
263                                 AppDomain.CurrentDomain.SetupInformation.DynamicBase,
264                                 name, "dll", OnCreateTemporaryAssemblyFile);
265                         parameters.OutputAssembly = outputAssemblyName;
266                         foreach (Assembly a in BuildManager.TopLevelAssemblies)
267                                 parameters.ReferencedAssemblies.Add (a.Location);
268                         CompilerResults results = abuilder.BuildAssembly (parameters);
269                         if (results == null)
270                                 return;
271                         
272                         if (results.NativeCompilerReturnValue == 0) {
273                                 BuildManager.CodeAssemblies.Add (results.CompiledAssembly);
274                                 BuildManager.TopLevelAssemblies.Add (results.CompiledAssembly);
275                                 HttpRuntime.WritePreservationFile (results.CompiledAssembly, name);
276                         } else {
277                                 if (HttpContext.Current.IsCustomErrorEnabled)
278                                         throw new HttpException ("An error occurred while initializing application.");
279                                 throw new CompilationException (null, results.Errors, null);
280                         }
281                 }
282                 
283                 VirtualPath PhysicalToVirtual (string file)
284                 {
285                         return new VirtualPath (file.Replace (HttpRuntime.AppDomainAppPath, "/").Replace (Path.DirectorySeparatorChar, '/'));
286                 }
287                 
288                 BuildProvider GetBuildProviderFor (string file, BuildProviderCollection buildProviders)
289                 {
290                         if (file == null || file.Length == 0 || buildProviders == null || buildProviders.Count == 0)
291                                 return null;
292
293                         BuildProvider ret = buildProviders.GetProviderInstanceForExtension (Path.GetExtension (file));
294                         if (ret != null && IsCorrectBuilderType (ret)) {
295                                 ret.SetVirtualPath (PhysicalToVirtual (file));
296                                 return ret;
297                         }
298                                 
299                         return null;
300                 }
301
302                 bool IsCorrectBuilderType (BuildProvider bp)
303                 {
304                         if (bp == null)
305                                 return false;
306                         Type type;
307                         object[] attrs;
308
309                         type = bp.GetType ();
310                         attrs = type.GetCustomAttributes (true);
311                         if (attrs == null)
312                                 return false;
313                         
314                         BuildProviderAppliesToAttribute bpAppliesTo;
315                         bool attributeFound = false;
316                         foreach (object attr in attrs) {
317                                 bpAppliesTo = attr as BuildProviderAppliesToAttribute;
318                                 if (bpAppliesTo == null)
319                                         continue;
320                                 attributeFound = true;
321                                 if ((bpAppliesTo.AppliesTo & BuildProviderAppliesTo.All) == BuildProviderAppliesTo.All ||
322                                     (bpAppliesTo.AppliesTo & BuildProviderAppliesTo.Code) == BuildProviderAppliesTo.Code)
323                                         return true;
324                         }
325
326                         if (attributeFound)
327                                 return false;
328                         return true;
329                 }
330                 
331         }
332         
333         internal class AppCodeCompiler
334         {
335                 static bool _alreadyCompiled;
336                 internal static string DefaultAppCodeAssemblyName;
337                 
338                 // A dictionary that contains an entry per an assembly that will
339                 // be produced by compiling App_Code. There's one main assembly
340                 // and an optional number of assemblies as defined by the
341                 // codeSubDirectories sub-element of the compilation element in
342                 // the system.web section of the app's config file.
343                 // Each entry's value is an AppCodeAssembly instance.
344                 //
345                 // Assemblies are named as follows:
346                 //
347                 //  1. main assembly: App_Code.{HASH}
348                 //  2. subdir assemblies: App_SubCode_{DirName}.{HASH}
349                 //
350                 // If any of the assemblies contains files that would be
351                 // compiled with different compilers, a System.Web.HttpException
352                 // is thrown.
353                 //
354                 // Files for which there is no explicit builder are ignored
355                 // silently
356                 //
357                 // Files for which exist BuildProviders but which have no
358                 // unambiguous language assigned to them (e.g. .wsdl files), are
359                 // built using the default website compiler.
360                 List<AppCodeAssembly> assemblies;
361                 string providerTypeName = null;
362                 
363                 public AppCodeCompiler ()
364                 {
365                         assemblies = new List<AppCodeAssembly>();
366                 }
367
368                 bool ProcessAppCodeDir (string appCode, AppCodeAssembly defasm)
369                 {
370                         // First process the codeSubDirectories
371                         CompilationSection cs = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
372                         
373                         if (cs != null) {
374                                 string aname;
375                                 for (int i = 0; i < cs.CodeSubDirectories.Count; i++) {
376                                         aname = String.Concat ("App_SubCode_", cs.CodeSubDirectories[i].DirectoryName);
377                                         assemblies.Add (new AppCodeAssembly (
378                                                                 aname,
379                                                                 Path.Combine (appCode, cs.CodeSubDirectories[i].DirectoryName)));
380                                 }
381                         }
382                         
383                         return CollectFiles (appCode, defasm);
384                 }
385
386                 CodeTypeReference GetProfilePropertyType (string type)
387                 {
388                         if (String.IsNullOrEmpty (type))
389                                 throw new ArgumentException ("String size cannot be 0", "type");
390                         return new CodeTypeReference (type);
391                 }
392
393                 string FindProviderTypeName (ProfileSection ps, string providerName)
394                 {
395                         if (ps.Providers == null || ps.Providers.Count == 0)
396                                 return null;
397                         
398                         ProviderSettings pset = ps.Providers [providerName];
399                         if (pset == null)
400                                 return null;
401                         return pset.Type;
402                 }
403                 
404                 void GetProfileProviderAttribute (ProfileSection ps, CodeAttributeDeclarationCollection collection,
405                                                   string providerName)
406                 {
407                         if (String.IsNullOrEmpty (providerName))
408                                 providerTypeName = FindProviderTypeName (ps, ps.DefaultProvider);
409                         else
410                                 providerTypeName = FindProviderTypeName (ps, providerName);
411                         if (providerTypeName == null)
412                                 throw new HttpException (String.Format ("Profile provider type not defined: {0}",
413                                                                         providerName));
414                         
415                         collection.Add (
416                                 new CodeAttributeDeclaration (
417                                         "ProfileProvider",
418                                         new CodeAttributeArgument (
419                                                 new CodePrimitiveExpression (providerTypeName)
420                                         )
421                                 )
422                         );
423                 }
424
425                 void GetProfileSettingsSerializeAsAttribute (ProfileSection ps, CodeAttributeDeclarationCollection collection,
426                                                              SerializationMode mode)
427                 {
428                         string parameter = String.Concat ("SettingsSerializeAs.", mode.ToString ());
429                         collection.Add (
430                                 new CodeAttributeDeclaration (
431                                         "SettingsSerializeAs",
432                                         new CodeAttributeArgument (
433                                                 new CodeSnippetExpression (parameter)
434                                         )
435                                 )
436                         );
437                                         
438                 }
439
440                 void AddProfileClassGetProfileMethod (CodeTypeDeclaration profileClass)
441                 {
442                         CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
443                                 new CodeTypeReferenceExpression (typeof (System.Web.Profile.ProfileBase)),
444                                 "Create");
445                         CodeMethodInvokeExpression minvoke = new CodeMethodInvokeExpression (
446                                 mref,
447                                 new CodeExpression[] { new CodeVariableReferenceExpression ("username") }
448                         );
449                         CodeCastExpression cast = new CodeCastExpression ();
450                         cast.TargetType = new CodeTypeReference ("ProfileCommon");
451                         cast.Expression = minvoke;
452                         
453                         CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
454                         ret.Expression = cast;
455                         
456                         CodeMemberMethod method = new CodeMemberMethod ();
457                         method.Name = "GetProfile";
458                         method.ReturnType = new CodeTypeReference ("ProfileCommon");
459                         method.Parameters.Add (new CodeParameterDeclarationExpression("System.String", "username"));
460                         method.Statements.Add (ret);
461                         method.Attributes = MemberAttributes.Public;
462                         
463                         profileClass.Members.Add (method);
464                 }
465                 
466                 void AddProfileClassProperty (ProfileSection ps, CodeTypeDeclaration profileClass, ProfilePropertySettings pset)
467                 {
468                         string name = pset.Name;
469                         if (String.IsNullOrEmpty (name))
470                                 throw new HttpException ("Profile property 'Name' attribute cannot be null.");
471                         CodeMemberProperty property = new CodeMemberProperty ();
472                         string typeName = pset.Type;
473                         if (typeName == "string")
474                                 typeName = "System.String";
475                         property.Name = name;
476                         property.Type = GetProfilePropertyType (typeName);
477                         property.Attributes = MemberAttributes.Public;
478                         
479                         CodeAttributeDeclarationCollection collection = new CodeAttributeDeclarationCollection();
480                         GetProfileProviderAttribute (ps, collection, pset.Provider);
481                         GetProfileSettingsSerializeAsAttribute (ps, collection, pset.SerializeAs);
482
483                         property.CustomAttributes = collection;
484                         CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
485                         CodeCastExpression cast = new CodeCastExpression ();
486                         ret.Expression = cast;
487
488                         CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
489                                 new CodeThisReferenceExpression (),
490                                 "GetPropertyValue");
491                         CodeMethodInvokeExpression minvoke = new CodeMethodInvokeExpression (
492                                 mref,
493                                 new CodeExpression[] { new CodePrimitiveExpression (name) }
494                         );
495                         cast.TargetType = new CodeTypeReference (typeName);
496                         cast.Expression = minvoke;
497                         property.GetStatements.Add (ret);
498
499                         if (!pset.ReadOnly) {
500                                 mref = new CodeMethodReferenceExpression (
501                                         new CodeThisReferenceExpression (),
502                                         "SetPropertyValue");
503                                 minvoke = new CodeMethodInvokeExpression (
504                                         mref,
505                                         new CodeExpression[] { new CodePrimitiveExpression (name), new CodeSnippetExpression ("value") }
506                                 );
507                                 property.SetStatements.Add (minvoke);
508                         }
509                         
510                         
511                         profileClass.Members.Add (property);
512                 }
513
514                 void AddProfileClassGroupProperty (string groupName, string memberName, CodeTypeDeclaration profileClass)
515                 {                       
516                         CodeMemberProperty property = new CodeMemberProperty ();
517                         property.Name = memberName;
518                         property.Type = new CodeTypeReference (groupName);
519                         property.Attributes = MemberAttributes.Public;
520
521                         CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
522                         CodeCastExpression cast = new CodeCastExpression ();
523                         ret.Expression = cast;
524
525                         CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
526                                 new CodeThisReferenceExpression (),
527                                 "GetProfileGroup");
528                         CodeMethodInvokeExpression minvoke = new CodeMethodInvokeExpression (
529                                 mref,
530                                 new CodeExpression[] { new CodePrimitiveExpression (memberName) }
531                         );
532                         cast.TargetType = new CodeTypeReference (groupName);
533                         cast.Expression = minvoke;
534                         property.GetStatements.Add (ret);
535                         
536                         profileClass.Members.Add (property);
537                 }
538                 
539                 void BuildProfileClass (ProfileSection ps, string className, ProfilePropertySettingsCollection psc,
540                                         CodeNamespace ns, string baseClass, bool baseIsGlobal,
541                                         SortedList <string, string> groupProperties)
542                 {
543                         CodeTypeDeclaration profileClass = new CodeTypeDeclaration (className);
544                         CodeTypeReference cref = new CodeTypeReference (baseClass);
545                         if (baseIsGlobal)
546                                 cref.Options |= CodeTypeReferenceOptions.GlobalReference;
547                         profileClass.BaseTypes.Add (cref);
548                         profileClass.TypeAttributes = TypeAttributes.Public;
549                         ns.Types.Add (profileClass);
550                         
551                         foreach (ProfilePropertySettings pset in psc)
552                                 AddProfileClassProperty (ps, profileClass, pset);
553                         if (groupProperties != null && groupProperties.Count > 0)
554                                 foreach (KeyValuePair <string, string> group in groupProperties)
555                                         AddProfileClassGroupProperty (group.Key, group.Value, profileClass);
556                         AddProfileClassGetProfileMethod (profileClass);
557                 }
558
559                 string MakeGroupName (string name)
560                 {
561                         return String.Concat ("ProfileGroup", name);
562                 }
563                 
564                 // FIXME: there should be some validation of syntactic correctness of the member/class name
565                 // for the groups/properties. For now it's left to the compiler to report errors.
566                 //
567                 // CodeGenerator.IsValidLanguageIndependentIdentifier (id) - use that
568                 //
569                 bool ProcessCustomProfile (ProfileSection ps, AppCodeAssembly defasm)
570                 {
571                         CodeCompileUnit unit = new CodeCompileUnit ();
572                         CodeNamespace ns = new CodeNamespace (null);
573                         unit.Namespaces.Add (ns);
574                         defasm.AddUnit (unit);
575                         
576                         ns.Imports.Add (new CodeNamespaceImport ("System"));
577                         ns.Imports.Add (new CodeNamespaceImport ("System.Configuration"));
578                         ns.Imports.Add (new CodeNamespaceImport ("System.Web"));
579                         ns.Imports.Add (new CodeNamespaceImport ("System.Web.Profile"));
580                         
581                         RootProfilePropertySettingsCollection props = ps.PropertySettings;
582                         if (props == null)
583                                 return true;
584
585                         SortedList<string, string> groupProperties = new SortedList<string, string> ();
586                         string groupName;
587                         foreach (ProfileGroupSettings pgs in props.GroupSettings) {
588                                 groupName = MakeGroupName (pgs.Name);
589                                 groupProperties.Add (groupName, pgs.Name);
590                                 BuildProfileClass (ps, groupName, pgs.PropertySettings, ns,
591                                                    "System.Web.Profile.ProfileGroupBase", true, null);
592                         }
593                         
594                         string baseType = ps.Inherits;
595                         if (String.IsNullOrEmpty (baseType))
596                                 baseType = "System.Web.Profile.ProfileBase";
597                         else {
598                                 string[] parts = baseType.Split (new char[] {','});
599                                 if (parts.Length > 1)
600                                         baseType = parts [0].Trim ();
601                         }
602                         
603                         bool baseIsGlobal;
604                         if (baseType.IndexOf ('.') != -1)
605                                 baseIsGlobal = true;
606                         else
607                                 baseIsGlobal = false;
608                         
609                         BuildProfileClass (ps, "ProfileCommon", props, ns, baseType, baseIsGlobal, groupProperties);
610                         return true;
611                 }
612
613 //              void PutCustomProfileInContext (HttpContext context, string assemblyName)
614 //              {
615 //                      Type type = Type.GetType (String.Format ("ProfileCommon, {0}",
616 //                                                               Path.GetFileNameWithoutExtension (assemblyName)));
617 //                      ProfileBase pb = Activator.CreateInstance (type) as ProfileBase;
618 //                      if (pb != null)
619 //                              context.Profile = pb;
620 //              }
621
622                 public static bool HaveCustomProfile (ProfileSection ps)
623                 {
624                         if (ps == null || !ps.Enabled)
625                                 return false;
626
627                         RootProfilePropertySettingsCollection props = ps.PropertySettings;
628                         ProfileGroupSettingsCollection groups = props != null ? props.GroupSettings : null;
629                         
630                         if (!String.IsNullOrEmpty (ps.Inherits) || (props != null && props.Count > 0) || (groups != null && groups.Count > 0))
631                                 return true;
632
633                         return false;
634                 }
635                 
636                 public void Compile ()
637                 {
638                         if (_alreadyCompiled)
639                                 return;
640                         
641                         string appCode = Path.Combine (HttpRuntime.AppDomainAppPath, "App_Code");
642                         ProfileSection ps = WebConfigurationManager.GetWebApplicationSection ("system.web/profile") as ProfileSection;
643                         bool haveAppCodeDir = Directory.Exists (appCode);
644                         bool haveCustomProfile = HaveCustomProfile (ps);
645                         
646                         if (!haveAppCodeDir && !haveCustomProfile)
647                                 return;
648
649                         AppCodeAssembly defasm = new AppCodeAssembly ("App_Code", appCode);
650                         assemblies.Add (defasm);
651
652                         bool haveCode = false;
653                         if (haveAppCodeDir)
654                                 haveCode = ProcessAppCodeDir (appCode, defasm);
655                         if (haveCustomProfile)
656                                 if (ProcessCustomProfile (ps, defasm))
657                                         haveCode = true;
658
659                         if (!haveCode)
660                                 return;
661                         
662                         HttpRuntime.EnableAssemblyMapping (true);
663                         string[] binAssemblies = HttpApplication.BinDirectoryAssemblies;
664                         
665                         foreach (AppCodeAssembly aca in assemblies)
666                                 aca.Build (binAssemblies);
667                         _alreadyCompiled = true;
668                         DefaultAppCodeAssemblyName = Path.GetFileNameWithoutExtension (defasm.OutputAssemblyName);
669
670                         RunAppInitialize ();
671                         
672                         if (haveCustomProfile && providerTypeName != null) {
673                                 if (Type.GetType (providerTypeName, false) == null) {
674                                         foreach (Assembly asm in BuildManager.TopLevelAssemblies) {
675                                                 if (asm == null)
676                                                         continue;
677                                                 
678                                                 if (asm.GetType (providerTypeName, false) != null)
679                                                         return;
680                                         }
681                                 } else
682                                         return;
683
684                                 Exception noTypeException = null;
685                                 Type ptype = null;
686                                 
687                                 try {
688                                         ptype = HttpApplication.LoadTypeFromBin (providerTypeName);
689                                 } catch (Exception ex) {
690                                         noTypeException = ex;
691                                 }
692
693                                 if (ptype == null)
694                                         throw new HttpException (String.Format ("Profile provider type not found: {0}", providerTypeName), noTypeException);
695                         }
696                 }
697
698                 // Documented (sort of...) briefly in:
699                 //
700                 //   http://quickstarts.asp.net/QuickStartv20/aspnet/doc/extensibility.aspx
701                 //   http://msdn2.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx
702                 void RunAppInitialize ()
703                 {
704                         MethodInfo mi = null, tmi;
705                         Type[] types;
706                         
707                         foreach (Assembly asm in BuildManager.CodeAssemblies) {
708                                 types = asm.GetExportedTypes ();
709                                 if (types == null || types.Length == 0)
710                                         continue;
711
712                                 foreach (Type type in types) {
713                                         tmi = type.GetMethod ("AppInitialize",
714                                                               BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
715                                                               null,
716                                                               Type.EmptyTypes,
717                                                               null);
718                                         if (tmi == null)
719                                                 continue;
720
721                                         if (mi != null)
722                                                 throw new HttpException ("The static AppInitialize method found in more than one type in the App_Code directory.");
723
724                                         mi = tmi;
725                                 }
726                         }
727
728                         if (mi == null)
729                                 return;
730
731                         mi.Invoke (null, null);
732                 }
733                 
734                 bool CollectFiles (string dir, AppCodeAssembly aca)
735                 {
736                         bool haveFiles = false;
737                         
738                         AppCodeAssembly curaca = aca;
739                         foreach (string f in Directory.GetFiles (dir)) {
740                                 aca.AddFile (f);
741                                 haveFiles = true;
742                         }
743                         
744                         foreach (string d in Directory.GetDirectories (dir)) {
745                                 foreach (AppCodeAssembly a in assemblies)
746                                         if (a.SourcePath == d) {
747                                                 curaca = a;
748                                                 break;
749                                         }
750                                 if (CollectFiles (d, curaca))
751                                         haveFiles = true;
752                                 curaca = aca;
753                         }
754                         return haveFiles;
755                 }
756         }
757 }
758