// // System.Web.Compilation.AppResourceAseemblyBuilder // // Authors: // Marek Habersack (mhabersack@novell.com) // // (C) 2007-2009 Novell, Inc (http://novell.com/) // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // #if NET_2_0 using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; using System.Threading; using System.Web; using System.Web.Configuration; namespace System.Web.Compilation { internal class AppResourcesAssemblyBuilder { CompilationSection config; CompilerInfo ci; CodeDomProvider _provider; string baseAssemblyPath; string baseAssemblyDirectory; string canonicAssemblyName; Assembly mainAssembly; AppResourcesCompiler appResourcesCompiler; public CodeDomProvider Provider { get { if (_provider == null) _provider = ci.CreateProvider (); else return _provider; if (_provider == null) throw new ApplicationException ("Failed to instantiate the default compiler."); return _provider; } } public Assembly MainAssembly { get { return mainAssembly; } } public AppResourcesAssemblyBuilder (string canonicAssemblyName, string baseAssemblyPath, AppResourcesCompiler appres) { this.appResourcesCompiler = appres; this.baseAssemblyPath = baseAssemblyPath; this.baseAssemblyDirectory = Path.GetDirectoryName (baseAssemblyPath); this.canonicAssemblyName = canonicAssemblyName; config = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection; if (config == null || !CodeDomProvider.IsDefinedLanguage (config.DefaultLanguage)) throw new ApplicationException ("Could not get the default compiler."); ci = CodeDomProvider.GetCompilerInfo (config.DefaultLanguage); if (ci == null || !ci.IsCodeDomProviderTypeValid) throw new ApplicationException ("Failed to obtain the default compiler information."); } public void Build () { Build (null); } public void Build (CodeCompileUnit unit) { Dictionary > cultures = appResourcesCompiler.CultureFiles; string defaultAssemblyKey = AppResourcesCompiler.DefaultCultureKey; foreach (KeyValuePair > kvp in cultures) BuildAssembly (kvp.Key, kvp.Value, defaultAssemblyKey, unit); } void BuildAssembly (string cultureName, List files, string defaultAssemblyKey, CodeCompileUnit unit) { if (String.Compare (cultureName, defaultAssemblyKey, StringComparison.Ordinal) == 0) BuildDefaultAssembly (files, unit); else BuildSatelliteAssembly (cultureName, files); } void BuildDefaultAssembly (List files, CodeCompileUnit unit) { AssemblyBuilder abuilder = new AssemblyBuilder (Provider); if (unit != null) abuilder.AddCodeCompileUnit (unit); CompilerParameters cp = ci.CreateDefaultCompilerParameters (); cp.OutputAssembly = baseAssemblyPath; cp.GenerateExecutable = false; cp.TreatWarningsAsErrors = true; cp.IncludeDebugInformation = config.Debug; foreach (string f in files) cp.EmbeddedResources.Add (f); CompilerResults results = abuilder.BuildAssembly (cp); if (results == null) return; Assembly ret = null; if (results.NativeCompilerReturnValue == 0) { ret = results.CompiledAssembly; BuildManager.TopLevelAssemblies.Add (ret); mainAssembly = ret; } else { if (HttpContext.Current.IsCustomErrorEnabled) throw new ApplicationException ("An error occurred while compiling global resources."); throw new CompilationException (null, results.Errors, null); } HttpRuntime.WritePreservationFile (ret, canonicAssemblyName); HttpRuntime.EnableAssemblyMapping (true); } void BuildSatelliteAssembly (string cultureName, List files) { string assemblyPath = BuildAssemblyPath (cultureName); var info = new ProcessStartInfo (); var al = new Process (); string arguments = SetAlPath (info); var sb = new StringBuilder (arguments); sb.Append ("/c:\"" + cultureName + "\" "); sb.Append ("/t:lib "); sb.Append ("/out:\"" + assemblyPath + "\" "); string responseFilePath = assemblyPath + ".response"; using (FileStream fs = File.OpenWrite (responseFilePath)) { using (StreamWriter sw = new StreamWriter (fs)) { foreach (string f in files) sw.WriteLine ("/embed:\"" + f + "\" "); } } sb.Append ("@\"" + responseFilePath + "\""); info.Arguments = sb.ToString (); info.CreateNoWindow = true; info.UseShellExecute = false; info.RedirectStandardOutput = true; info.RedirectStandardError = true; al.StartInfo = info; var alOutput = new StringCollection (); var alMutex = new Mutex (); DataReceivedEventHandler outputHandler = (object sender, DataReceivedEventArgs args) => { if (args.Data != null) { alMutex.WaitOne (); alOutput.Add (args.Data); alMutex.ReleaseMutex (); } }; al.ErrorDataReceived += outputHandler; al.OutputDataReceived += outputHandler; // TODO: consider using asynchronous processes try { al.Start (); } catch (Exception ex) { throw new HttpException (String.Format ("Error running {0}", al.StartInfo.FileName), ex); } Exception alException = null; int exitCode = 0; try { al.BeginOutputReadLine (); al.BeginErrorReadLine (); al.WaitForExit (); exitCode = al.ExitCode; } catch (Exception ex) { alException = ex; } finally { al.CancelErrorRead (); al.CancelOutputRead (); al.Close (); } if (exitCode != 0 || alException != null) { // TODO: consider adding a new type of compilation exception, // tailored for al CompilerErrorCollection errors = null; if (alOutput.Count != 0) { foreach (string line in alOutput) { if (!line.StartsWith ("ALINK: error ", StringComparison.Ordinal)) continue; if (errors == null) errors = new CompilerErrorCollection (); int colon = line.IndexOf (':', 13); string errorNumber = colon != -1 ? line.Substring (13, colon - 13) : "Unknown"; string errorText = colon != -1 ? line.Substring (colon + 1) : line.Substring (13); errors.Add (new CompilerError (Path.GetFileName (assemblyPath), 0, 0, errorNumber, errorText)); } } throw new CompilationException (Path.GetFileName (assemblyPath), errors, null); } } string SetAlPath (ProcessStartInfo info) { if (HttpRuntime.RunningOnWindows) { string alPath; string monoPath; PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static|BindingFlags.NonPublic); MethodInfo get_gac = gac.GetGetMethod (true); string p = Path.GetDirectoryName ((string) get_gac.Invoke (null, null)); monoPath = Path.Combine (Path.GetDirectoryName (Path.GetDirectoryName (p)), "bin\\mono.bat"); if (!File.Exists (monoPath)) { monoPath = Path.Combine (Path.GetDirectoryName (Path.GetDirectoryName (p)), "bin\\mono.exe"); if (!File.Exists (monoPath)) { monoPath = Path.Combine (Path.GetDirectoryName (Path.GetDirectoryName (Path.GetDirectoryName (p))), "mono\\mono\\mini\\mono.exe"); if (!File.Exists (monoPath)) throw new FileNotFoundException ("Windows mono path not found: " + monoPath); } } alPath = Path.Combine (p, "2.0\\al.exe"); if (!File.Exists (alPath)) { alPath = Path.Combine(Path.GetDirectoryName (p), "lib\\net_2_0\\al.exe"); if (!File.Exists (alPath)) throw new FileNotFoundException ("Windows al path not found: " + alPath); } info.FileName = monoPath; return alPath; } else { info.FileName = "al2"; return String.Empty; } } string BuildAssemblyPath (string cultureName) { string baseDir = Path.Combine (baseAssemblyDirectory, cultureName); if (!Directory.Exists (baseDir)) Directory.CreateDirectory (baseDir); string baseFileName = Path.GetFileNameWithoutExtension (baseAssemblyPath); string fileName = String.Concat (baseFileName, ".resources.dll"); fileName = Path.Combine (baseDir, fileName); return fileName; } CodeCompileUnit GenerateAssemblyInfo (string cultureName) { CodeAttributeArgument[] args = new CodeAttributeArgument [1]; args [0] = new CodeAttributeArgument (new CodePrimitiveExpression (cultureName)); CodeCompileUnit unit = new CodeCompileUnit (); unit.AssemblyCustomAttributes.Add ( new CodeAttributeDeclaration ( new CodeTypeReference ("System.Reflection.AssemblyCultureAttribute"), args)); args = new CodeAttributeArgument [2]; args [0] = new CodeAttributeArgument (new CodePrimitiveExpression ("ASP.NET")); args [1] = new CodeAttributeArgument (new CodePrimitiveExpression (Environment.Version.ToString ())); unit.AssemblyCustomAttributes.Add ( new CodeAttributeDeclaration ( new CodeTypeReference ("System.CodeDom.Compiler.GeneratedCodeAttribute"), args)); return unit; } } } #endif