2009-02-28 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AppCodeCompiler.cs
index 8e9d77ab867810d1f2773a759d416bd270bf40e9..71ac1d1452e111e5f1ed05c970e0d84f14726e29 100644 (file)
@@ -44,15 +44,57 @@ using System.Web.Util;
 
 namespace System.Web.Compilation
 {
+       class AssemblyPathResolver
+       {
+               static Dictionary <string, string> assemblyCache;
+
+               static AssemblyPathResolver ()
+               {
+                       assemblyCache = new Dictionary <string, string> ();
+               }
+
+               public static string GetAssemblyPath (string assemblyName)
+               {
+                       lock (assemblyCache) {
+                               if (assemblyCache.ContainsKey (assemblyName))
+                                       return assemblyCache [assemblyName];
+
+                               Assembly asm = null;
+                               Exception error = null;
+                               if (assemblyName.IndexOf (',') != -1) {
+                                       try {
+                                               asm = Assembly.Load (assemblyName);
+                                       } catch (Exception e) {
+                                               error = e;
+                                       }
+                               }
+
+                               if (asm == null) {
+                                       try {
+                                               asm = Assembly.LoadWithPartialName (assemblyName);
+                                       } catch (Exception e) {
+                                               error = e;
+                                       }
+                               }
+                        
+                               if (asm == null)
+                                       throw new HttpException (String.Format ("Unable to find assembly {0}", assemblyName), error);
+
+                               assemblyCache.Add (assemblyName, asm.Location);
+                               return asm.Location;
+                       }
+               }
+       }
+       
        internal class AppCodeAssembly
        {
-               private List<string> files;
-               private List<CodeCompileUnit> units;
+               List<string> files;
+               List<CodeCompileUnit> units;
                
-               private string name;
-               private string path;
-               private bool validAssembly;
-               private string outputAssemblyName;
+               string name;
+               string path;
+               bool validAssembly;
+               string outputAssemblyName;
 
                public string OutputAssemblyName
                {
@@ -121,7 +163,7 @@ namespace System.Web.Compilation
                        
                        // First make sure all the files are in the same
                        // language
-                       bool known = false, unknown = false;
+                       bool known = false;
                        foreach (string f in files) {
                                known = true;
                                language = null;
@@ -156,16 +198,13 @@ namespace System.Web.Compilation
                        }
 
                        CodeDomProvider provider = null;
-                       CompilationSection compilationSection = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection;
+                       CompilationSection compilationSection = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
                        if (compilerInfo == null) {
                                if (!CodeDomProvider.IsDefinedLanguage (compilationSection.DefaultLanguage))
                                        throw new HttpException ("Failed to retrieve default source language");
                                compilerInfo = CodeDomProvider.GetCompilerInfo (compilationSection.DefaultLanguage);
                                if (compilerInfo == null || !compilerInfo.IsCodeDomProviderTypeValid)
                                        throw new HttpException ("Internal error while initializing application");
-                               provider = compilerInfo.CreateProvider ();
-                               if (provider == null)
-                                       throw new HttpException ("A code provider error occurred while initializing application.");
                        }
 
                        provider = compilerInfo.CreateProvider ();
@@ -186,12 +225,11 @@ namespace System.Web.Compilation
                                parameters.ReferencedAssemblies.AddRange (binAssemblies);
                        
                        if (compilationSection != null) {
-                               AssemblyName asmName;
                                foreach (AssemblyInfo ai in compilationSection.Assemblies)
                                        if (ai.Assembly != "*") {
                                                try {
-                                                       asmName = new AssemblyName (ai.Assembly);
-                                                       parameters.ReferencedAssemblies.Add (asmName.Name);
+                                                       parameters.ReferencedAssemblies.Add (
+                                                               AssemblyPathResolver.GetAssemblyPath (ai.Assembly));
                                                } catch (Exception ex) {
                                                        throw new HttpException (
                                                                String.Format ("Could not find assembly {0}.", ai.Assembly),
@@ -206,11 +244,10 @@ namespace System.Web.Compilation
                                        if (bprovider == null)
                                                continue;
                                        bprovider.GenerateCode (abuilder);
-                                       unknown = true;
                                }
                        }
 
-                       if (knownfiles.Count == 0 && unknownfiles.Count == 0)
+                       if (knownfiles.Count == 0 && unknownfiles.Count == 0 && units.Count == 0)
                                return;
                        
                        outputAssemblyName = (string)FileUtils.CreateTemporaryFile (
@@ -220,7 +257,10 @@ namespace System.Web.Compilation
                        foreach (Assembly a in BuildManager.TopLevelAssemblies)
                                parameters.ReferencedAssemblies.Add (a.Location);
                        CompilerResults results = abuilder.BuildAssembly (parameters);
-                       if (results.Errors.Count == 0) {
+                       if (results == null)
+                               return;
+                       
+                       if (results.NativeCompilerReturnValue == 0) {
                                BuildManager.CodeAssemblies.Add (results.CompiledAssembly);
                                BuildManager.TopLevelAssemblies.Add (results.CompiledAssembly);
                                HttpRuntime.WritePreservationFile (results.CompiledAssembly, name);
@@ -230,13 +270,13 @@ namespace System.Web.Compilation
                                throw new CompilationException (null, results.Errors, null);
                        }
                }
-
-               private string PhysicalToVirtual (string file)
+               
+               VirtualPath PhysicalToVirtual (string file)
                {
-                       return file.Replace (HttpRuntime.AppDomainAppPath, "/").Replace (Path.DirectorySeparatorChar, '/');
+                       return new VirtualPath (file.Replace (HttpRuntime.AppDomainAppPath, "/").Replace (Path.DirectorySeparatorChar, '/'));
                }
                
-               private BuildProvider GetBuildProviderFor (string file, BuildProviderCollection buildProviders)
+               BuildProvider GetBuildProviderFor (string file, BuildProviderCollection buildProviders)
                {
                        if (file == null || file.Length == 0 || buildProviders == null || buildProviders.Count == 0)
                                return null;
@@ -250,7 +290,7 @@ namespace System.Web.Compilation
                        return null;
                }
 
-               private bool IsCorrectBuilderType (BuildProvider bp)
+               bool IsCorrectBuilderType (BuildProvider bp)
                {
                        if (bp == null)
                                return false;
@@ -283,7 +323,7 @@ namespace System.Web.Compilation
        
        internal class AppCodeCompiler
        {
-               static private bool _alreadyCompiled;
+               static bool _alreadyCompiled;
                internal static string DefaultAppCodeAssemblyName;
                
                // A dictionary that contains an entry per an assembly that will
@@ -308,8 +348,7 @@ namespace System.Web.Compilation
                // Files for which exist BuildProviders but which have no
                // unambiguous language assigned to them (e.g. .wsdl files), are
                // built using the default website compiler.
-               private List<AppCodeAssembly> assemblies;
-               string _bindir = null;
+               List<AppCodeAssembly> assemblies;
                string providerTypeName = null;
                
                public AppCodeCompiler ()
@@ -320,12 +359,12 @@ namespace System.Web.Compilation
                bool ProcessAppCodeDir (string appCode, AppCodeAssembly defasm)
                {
                        // First process the codeSubDirectories
-                       CompilationSection cs = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
+                       CompilationSection cs = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
                        
                        if (cs != null) {
                                string aname;
                                for (int i = 0; i < cs.CodeSubDirectories.Count; i++) {
-                                       aname = String.Format ("App_SubCode_{0}", cs.CodeSubDirectories[i].DirectoryName);
+                                       aname = String.Concat ("App_SubCode_", cs.CodeSubDirectories[i].DirectoryName);
                                        assemblies.Add (new AppCodeAssembly (
                                                                aname,
                                                                Path.Combine (appCode, cs.CodeSubDirectories[i].DirectoryName)));
@@ -342,28 +381,6 @@ namespace System.Web.Compilation
                        return new CodeTypeReference (type);
                }
 
-               Type GetTypeFromBin (string typeName)
-               {
-                       string bindir = BinDir;
-                       if (!Directory.Exists (bindir))
-                               return null;
-                       
-                       string [] binDlls = Directory.GetFiles (bindir, "*.dll");
-                       Type ret = null;
-                       foreach (string dll in binDlls) {
-                               try {
-                                       Assembly asm = Assembly.LoadFrom (dll);
-                                       ret = asm.GetType (typeName, false);
-                                       if (ret != null)
-                                               break;
-                               } catch (Exception) {
-                                       continue;
-                               }
-                       }
-
-                       return ret;
-               }
-
                string FindProviderTypeName (ProfileSection ps, string providerName)
                {
                        if (ps.Providers == null || ps.Providers.Count == 0)
@@ -399,7 +416,7 @@ namespace System.Web.Compilation
                void GetProfileSettingsSerializeAsAttribute (ProfileSection ps, CodeAttributeDeclarationCollection collection,
                                                             SerializationMode mode)
                {
-                       string parameter = String.Format ("SettingsSerializeAs.{0}", mode.ToString ());
+                       string parameter = String.Concat ("SettingsSerializeAs.", mode.ToString ());
                        collection.Add (
                                new CodeAttributeDeclaration (
                                        "SettingsSerializeAs",
@@ -532,7 +549,7 @@ namespace System.Web.Compilation
 
                string MakeGroupName (string name)
                {
-                       return String.Format ("ProfileGroup{0}", name);
+                       return String.Concat ("ProfileGroup", name);
                }
                
                // FIXME: there should be some validation of syntactic correctness of the member/class name
@@ -566,12 +583,20 @@ namespace System.Web.Compilation
                        }
                        
                        string baseType = ps.Inherits;
-                       bool baseIsGlobal = false;
-                       if (String.IsNullOrEmpty (baseType)) {
+                       if (String.IsNullOrEmpty (baseType))
                                baseType = "System.Web.Profile.ProfileBase";
-                               baseIsGlobal = true;
+                       else {
+                               string[] parts = baseType.Split (new char[] {','});
+                               if (parts.Length > 1)
+                                       baseType = parts [0].Trim ();
                        }
                        
+                       bool baseIsGlobal;
+                       if (baseType.IndexOf ('.') != -1)
+                               baseIsGlobal = true;
+                       else
+                               baseIsGlobal = false;
+                       
                        BuildProfileClass (ps, "ProfileCommon", props, ns, baseType, baseIsGlobal, groupProperties);
                        return true;
                }
@@ -601,7 +626,7 @@ namespace System.Web.Compilation
                                return;
                        
                        string appCode = Path.Combine (HttpRuntime.AppDomainAppPath, "App_Code");
-                       ProfileSection ps = WebConfigurationManager.GetSection ("system.web/profile") as ProfileSection;
+                       ProfileSection ps = WebConfigurationManager.GetWebApplicationSection ("system.web/profile") as ProfileSection;
                        bool haveAppCodeDir = Directory.Exists (appCode);
                        bool haveCustomProfile = HaveCustomProfile (ps);
                        
@@ -620,16 +645,16 @@ namespace System.Web.Compilation
 
                        if (!haveCode)
                                return;
-
+                       
                        HttpRuntime.EnableAssemblyMapping (true);
-                       string bindir = BinDir;
-                       string[] binAssemblies = null;
-                       if (Directory.Exists (bindir))
-                               binAssemblies = Directory.GetFiles (bindir, "*.dll");
+                       string[] binAssemblies = HttpApplication.BinDirectoryAssemblies;
+                       
                        foreach (AppCodeAssembly aca in assemblies)
                                aca.Build (binAssemblies);
                        _alreadyCompiled = true;
                        DefaultAppCodeAssemblyName = Path.GetFileNameWithoutExtension (defasm.OutputAssemblyName);
+
+                       RunAppInitialize ();
                        
                        if (haveCustomProfile && providerTypeName != null) {
                                if (Type.GetType (providerTypeName, false) == null) {
@@ -643,13 +668,49 @@ namespace System.Web.Compilation
                                } else
                                        return;
 
-                               if (GetTypeFromBin (providerTypeName) == null)
+                               if (HttpApplication.LoadTypeFromBin (providerTypeName) == null)
                                        throw new HttpException (String.Format ("Profile provider type not found: {0}",
                                                                                providerTypeName));
                        }
                }
 
-               private bool CollectFiles (string dir, AppCodeAssembly aca)
+               // Documented (sort of...) briefly in:
+               //
+               //   http://quickstarts.asp.net/QuickStartv20/aspnet/doc/extensibility.aspx
+               //   http://msdn2.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx
+               void RunAppInitialize ()
+               {
+                       MethodInfo mi = null, tmi;
+                       Type[] types;
+                       
+                       foreach (Assembly asm in BuildManager.CodeAssemblies) {
+                               types = asm.GetExportedTypes ();
+                               if (types == null || types.Length == 0)
+                                       continue;
+
+                               foreach (Type type in types) {
+                                       tmi = type.GetMethod ("AppInitialize",
+                                                             BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
+                                                             null,
+                                                             Type.EmptyTypes,
+                                                             null);
+                                       if (tmi == null)
+                                               continue;
+
+                                       if (mi != null)
+                                               throw new HttpException ("The static AppInitialize method found in more than one type in the App_Code directory.");
+
+                                       mi = tmi;
+                               }
+                       }
+
+                       if (mi == null)
+                               return;
+
+                       mi.Invoke (null, null);
+               }
+               
+               bool CollectFiles (string dir, AppCodeAssembly aca)
                {
                        bool haveFiles = false;
                        
@@ -671,16 +732,6 @@ namespace System.Web.Compilation
                        }
                        return haveFiles;
                }
-
-               private string BinDir {
-                       get {
-                               if (_bindir != null)
-                                       return _bindir;
-                               AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
-                               _bindir = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
-                               return _bindir;
-                       }
-               }
        }
 }
 #endif