Index: IgnoreFileBuildProvider.cs =================================================================== --- IgnoreFileBuildProvider.cs (revision 67781) +++ IgnoreFileBuildProvider.cs (working copy) @@ -31,7 +31,7 @@ #if NET_2_0 namespace System.Web.Compilation { - + [BuildProviderAppliesTo (BuildProviderAppliesTo.All)] class IgnoreFileBuildProvider : BuildProvider { public override void GenerateCode (AssemblyBuilder assemblyBuilder) Index: WebServiceBuildProvider.cs =================================================================== --- WebServiceBuildProvider.cs (revision 67781) +++ WebServiceBuildProvider.cs (working copy) @@ -39,7 +39,7 @@ using System.Web.UI; namespace System.Web.Compilation { - + [BuildProviderAppliesTo (BuildProviderAppliesTo.Web)] sealed class WebServiceBuildProvider : BuildProvider { WebServiceCompiler wscompiler; CompilerType compiler_type; Index: AppResourceFilesCompiler.cs =================================================================== --- AppResourceFilesCompiler.cs (revision 67781) +++ AppResourceFilesCompiler.cs (working copy) @@ -37,6 +37,7 @@ using System.IO; using System.Reflection; using System.Resources; +using System.Web.Util; using Microsoft.CSharp; //using Microsoft.VisualBasic; @@ -574,6 +575,13 @@ return resfile; } + object OnCreateRandomFile (string path) + { + FileStream f = new FileStream (path, FileMode.CreateNew); + f.Close (); + return path; + } + protected string GenRandomFileName (string basepath) { return GenRandomFileName (basepath, null); @@ -581,50 +589,18 @@ protected string GenRandomFileName (string basepath, string ext) { - if (rnd == null) - rnd = new Random (); - FileStream f = null; - string path = null; - do { - int num = rnd.Next (); - num++; - path = Path.Combine (basepath, num.ToString ("x")); - if (ext != null) - path = String.Format ("{0}.{1}", path, ext); - - try { - f = new FileStream (path, FileMode.CreateNew); - } catch (System.IO.IOException) { - f = null; - continue; - } catch { - throw; - } - } while (f == null); - f.Close (); - return path; + return (string)FileUtils.CreateTemporaryFile (basepath, ext, OnCreateRandomFile); } + + object OnCreateRandomDir (string path) + { + DirectoryInfo di = Directory.CreateDirectory (path); + return di.FullName; + } protected string GenTempDir (string basepath) { - if (rnd == null) - rnd = new Random (); - DirectoryInfo di = null; - string path; - do { - int num = rnd.Next (); - num++; - path = Path.Combine (basepath, num.ToString ("x")); - try { - di = Directory.CreateDirectory (path); - } catch (System.IO.IOException) { - di = null; - continue; - } catch { - throw; - } - } while (di == null); - return di.FullName; + return (string)FileUtils.CreateTemporaryFile (basepath, OnCreateRandomDir); } } } Index: PageBuildProvider.cs =================================================================== --- PageBuildProvider.cs (revision 67781) +++ PageBuildProvider.cs (working copy) @@ -40,6 +40,7 @@ namespace System.Web.Compilation { + [BuildProviderAppliesTo (BuildProviderAppliesTo.Web)] sealed class PageBuildProvider : BuildProvider { PageCompiler pcompiler; CompilerType compiler_type; Index: UserControlBuildProvider.cs =================================================================== --- UserControlBuildProvider.cs (revision 67781) +++ UserControlBuildProvider.cs (working copy) @@ -39,7 +39,7 @@ using System.Web.UI; namespace System.Web.Compilation { - + [BuildProviderAppliesTo (BuildProviderAppliesTo.Web)] sealed class UserControlBuildProvider : BuildProvider { UserControlCompiler uccompiler; CompilerType compiler_type; Index: AssemblyBuilder.cs =================================================================== --- AssemblyBuilder.cs (revision 67781) +++ AssemblyBuilder.cs (working copy) @@ -49,27 +49,35 @@ CodeDomProvider provider; List units; List source_files; + List referenced_assemblies; Dictionary resource_files; TempFileCollection temp_files; string virtual_path; //TODO: there should be a Compile () method here which is where all the compilation exceptions are thrown from. + + internal AssemblyBuilder (CodeDomProvider provider) + : this (null, provider) + {} internal AssemblyBuilder (string virtualPath, CodeDomProvider provider) { this.provider = provider; this.virtual_path = virtualPath; units = new List (); - units.Add (new CodeCompileUnit ()); temp_files = new TempFileCollection (); + referenced_assemblies = new List (); CompilationSection section; - section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation", virtualPath); + if (virtualPath != null) + section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation", virtualPath); + else + section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation"); string tempdir = section.TempDirectory; if (tempdir == null || tempdir == "") tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase; temp_files = new TempFileCollection (tempdir, KeepFiles); } - + internal TempFileCollection TempFiles { get { return temp_files; } } @@ -101,14 +109,10 @@ { if (a == null) throw new ArgumentNullException ("a"); - - StringCollection coll = units [units.Count - 1].ReferencedAssemblies; - string location = a.Location; - if (coll.IndexOf (location) == -1) - coll.Add (location); + + referenced_assemblies.Add (a.Location); } - [MonoTODO ("Do something with the buildProvider argument")] public void AddCodeCompileUnit (BuildProvider buildProvider, CodeCompileUnit compileUnit) { if (buildProvider == null) @@ -120,18 +124,30 @@ units.Add (compileUnit); } - [MonoTODO ("Anything to do with the buildProvider argument?")] public TextWriter CreateCodeFile (BuildProvider buildProvider) { if (buildProvider == null) throw new ArgumentNullException ("buildProvider"); - string filename = temp_files.AddExtension ("temp", true); + // Generate a file name with the correct source language extension + string filename = GetTempFilePhysicalPath (provider.FileExtension); SourceFiles.Add (filename); return new StreamWriter (File.OpenWrite (filename), WebEncoding.FileEncoding); } - [MonoTODO ("Anything to do with the buildProvider argument?")] + internal void AddCodeFile (string path) + { + if (path == null || path.Length == 0) + return; + string extension = Path.GetExtension (path); + if (extension == null || extension.Length == 0) + return; // maybe better to throw an exception here? + extension = extension.Substring (1); + string filename = GetTempFilePhysicalPath (extension); + File.Copy (path, filename, true); + SourceFiles.Add (filename); + } + public Stream CreateEmbeddedResource (BuildProvider buildProvider, string name) { if (buildProvider == null) @@ -140,7 +156,7 @@ if (name == null || name == "") throw new ArgumentNullException ("name"); - string filename = temp_files.AddExtension ("resource", true); + string filename = GetTempFilePhysicalPath ("resource"); Stream stream = File.OpenWrite (filename); ResourceFiles [name] = filename; return stream; @@ -156,19 +172,59 @@ { if (extension == null) throw new ArgumentNullException ("extension"); - - return temp_files.AddExtension (extension, true); + + return temp_files.AddExtension (String.Format ("_{0}.{1}", temp_files.Count, extension), true); } public CodeDomProvider CodeDomProvider { get { return provider; } } + internal CompilerResults BuildAssembly (CompilerParameters options) + { + return BuildAssembly (null, options); + } + internal CompilerResults BuildAssembly (string virtualPath, CompilerParameters options) { + if (options == null) + throw new ArgumentNullException ("options"); + CompilerResults results; CodeCompileUnit [] units = GetUnitsAsArray (); - results = provider.CompileAssemblyFromDom (options, units); + + // Since we may have some source files and some code + // units, we generate code from all of them and then + // compile the assembly from the set of temporary source + // files. This also facilates possible debugging for the + // end user, since they get the code beforehand. + List files = SourceFiles; + string filename; + StreamWriter sw = null; + foreach (CodeCompileUnit unit in units) { + filename = GetTempFilePhysicalPath (provider.FileExtension); + try { + sw = new StreamWriter (File.OpenWrite (filename), WebEncoding.FileEncoding); + provider.GenerateCodeFromCompileUnit (unit, sw, null); + files.Add (filename); + } catch { + throw; + } finally { + if (sw != null) { + sw.Flush (); + sw.Close (); + } + } + } + + Dictionary resources = ResourceFiles; + foreach (KeyValuePair de in resources) + options.EmbeddedResources.Add (de.Value); + foreach (string refasm in referenced_assemblies) + options.ReferencedAssemblies.Add (refasm); + + results = provider.CompileAssemblyFromFile (options, files.ToArray ()); + // FIXME: generate the code and display it if (results.NativeCompilerReturnValue != 0) throw new CompilationException (virtualPath, results.Errors, ""); @@ -184,7 +240,8 @@ results.CompiledAssembly = Assembly.LoadFrom (options.OutputAssembly); } - results.TempFiles.Delete (); + if (!KeepFiles) + results.TempFiles.Delete (); return results; } } Index: WebHandlerBuildProvider.cs =================================================================== --- WebHandlerBuildProvider.cs (revision 67781) +++ WebHandlerBuildProvider.cs (working copy) @@ -39,7 +39,7 @@ using System.Web.UI; namespace System.Web.Compilation { - + [BuildProviderAppliesTo (BuildProviderAppliesTo.Web)] sealed class WebHandlerBuildProvider : BuildProvider { WebServiceCompiler wscompiler; CompilerType compiler_type; Index: AppCodeCompiler.cs =================================================================== --- AppCodeCompiler.cs (revision 67781) +++ AppCodeCompiler.cs (working copy) @@ -36,13 +36,12 @@ using System.Globalization; using System.IO; using System.Web.Configuration; +using System.Web.Util; namespace System.Web.Compilation { internal class AppCodeAssembly { - private static Random rnd = new Random (); - private List files; private string name; private string path; @@ -82,6 +81,13 @@ { files.Add (path); } + + object OnCreateTemporaryAssemblyFile (string path) + { + FileStream f = new FileStream (path, FileMode.CreateNew); + f.Close (); + return path; + } // Build and add the assembly to the BuildManager's // CodeAssemblies collection @@ -130,30 +136,96 @@ } CodeDomProvider provider = null; - // First the files in the language we are aware of - if (compilerInfo != null) { + if (compilerInfo == null) { + CompilationSection config = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection; + if (config == null || !CodeDomProvider.IsDefinedLanguage (config.DefaultLanguage)) + throw new HttpException ("Failed to retrieve default source language"); + compilerInfo = CodeDomProvider.GetCompilerInfo (config.DefaultLanguage); + if (compilerInfo == null || !compilerInfo.IsCodeDomProviderTypeValid) + throw new HttpException ("Internal error while initializing application"); provider = compilerInfo.CreateProvider (); if (provider == null) - throw new HttpException ("Unable to create a compiler for App_Code contents"); + throw new HttpException ("A code provider error occurred while initializing application."); + } - CompilerParameters parameters = compilerInfo.CreateDefaultCompilerParameters (); - int num = rnd.Next () + 1; + provider = compilerInfo.CreateProvider (); + if (provider == null) + throw new HttpException ("A code provider error occurred while initializing application."); + + AssemblyBuilder abuilder = new AssemblyBuilder (provider); + foreach (string file in knownfiles) + abuilder.AddCodeFile (file); + + BuildProvider bprovider; + CompilationSection compilationSection = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection; + if (compilationSection != null) { + BuildProviderCollection buildProviders = compilationSection.BuildProviders; - string assemblyName = Path.Combine ( - AppDomain.CurrentDomain.SetupInformation.DynamicBase, - String.Format ("{0}.{1}.dll", name, num.ToString ("x"))); - parameters.OutputAssembly = assemblyName; - CompilerResults results = provider.CompileAssemblyFromFile (parameters, knownfiles.ToArray ()); - if (results.Errors.Count == 0) { - BuildManager.CodeAssemblies.Add (results.PathToAssembly); - BuildManager.TopLevelAssemblies.Add (results.CompiledAssembly); - } else { - if (HttpContext.Current.IsCustomErrorEnabled) - throw new HttpException ("An error occurred while initializing application."); - throw new CompilationException (null, results.Errors, null); + foreach (string file in unknownfiles) { + bprovider = GetBuildProviderFor (file, buildProviders); + if (bprovider == null) + continue; + bprovider.GenerateCode (abuilder); } } + + string assemblyName = (string)FileUtils.CreateTemporaryFile ( + AppDomain.CurrentDomain.SetupInformation.DynamicBase, + name, "dll", OnCreateTemporaryAssemblyFile); + CompilerParameters parameters = compilerInfo.CreateDefaultCompilerParameters (); + parameters.OutputAssembly = assemblyName; + CompilerResults results = abuilder.BuildAssembly (parameters); + if (results.Errors.Count == 0) { + BuildManager.CodeAssemblies.Add (results.PathToAssembly); + BuildManager.TopLevelAssemblies.Add (results.CompiledAssembly); + } else { + if (HttpContext.Current.IsCustomErrorEnabled) + throw new HttpException ("An error occurred while initializing application."); + throw new CompilationException (null, results.Errors, null); + } } + + private BuildProvider GetBuildProviderFor (string file, BuildProviderCollection buildProviders) + { + if (file == null || file.Length == 0 || buildProviders == null || buildProviders.Count == 0) + return null; + + foreach (BuildProvider bp in buildProviders) + if (IsCorrectBuilderType (bp)) + return bp; + + return null; + } + + private bool IsCorrectBuilderType (BuildProvider bp) + { + if (bp == null) + return false; + Type type; + object[] attrs; + + type = bp.GetType (); + attrs = type.GetCustomAttributes (true); + if (attrs == null) + return false; + + BuildProviderAppliesToAttribute bpAppliesTo; + bool attributeFound = false; + foreach (object attr in attrs) { + bpAppliesTo = attr as BuildProviderAppliesToAttribute; + if (bpAppliesTo == null) + continue; + attributeFound = true; + if ((bpAppliesTo.AppliesTo & BuildProviderAppliesTo.All) == BuildProviderAppliesTo.All || + (bpAppliesTo.AppliesTo & BuildProviderAppliesTo.Code) == BuildProviderAppliesTo.Code) + return true; + } + + if (attributeFound) + return false; + return true; + } + } internal class AppCodeCompiler