2 // System.Web.Compilation.AppResourceAseemblyBuilder
5 // Marek Habersack <grendel@twistedcode.net>
7 // (C) 2007-2009 Novell, Inc (http://novell.com/)
8 // (C) 2011 Xamarin, Inc (http://xamarin.com/)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
33 using System.CodeDom.Compiler;
34 using System.Collections.Generic;
35 using System.Collections.Specialized;
36 using System.ComponentModel;
37 using System.Diagnostics;
39 using System.Reflection;
41 using System.Threading;
43 using System.Web.Configuration;
44 using System.Web.Util;
46 namespace System.Web.Compilation
48 class AppResourcesAssemblyBuilder
50 CompilationSection config;
52 CodeDomProvider _provider;
53 string baseAssemblyPath;
54 string baseAssemblyDirectory;
55 string canonicAssemblyName;
56 Assembly mainAssembly;
57 AppResourcesCompiler appResourcesCompiler;
59 public CodeDomProvider Provider {
61 if (_provider == null)
62 _provider = ci.CreateProvider ();
66 if (_provider == null)
67 throw new ApplicationException ("Failed to instantiate the default compiler.");
72 public Assembly MainAssembly {
73 get { return mainAssembly; }
76 public AppResourcesAssemblyBuilder (string canonicAssemblyName, string baseAssemblyPath, AppResourcesCompiler appres)
78 this.appResourcesCompiler = appres;
79 this.baseAssemblyPath = baseAssemblyPath;
80 this.baseAssemblyDirectory = Path.GetDirectoryName (baseAssemblyPath);
81 this.canonicAssemblyName = canonicAssemblyName;
83 config = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
84 if (config == null || !CodeDomProvider.IsDefinedLanguage (config.DefaultLanguage))
85 throw new ApplicationException ("Could not get the default compiler.");
86 ci = CodeDomProvider.GetCompilerInfo (config.DefaultLanguage);
87 if (ci == null || !ci.IsCodeDomProviderTypeValid)
88 throw new ApplicationException ("Failed to obtain the default compiler information.");
96 public void Build (CodeCompileUnit unit)
98 Dictionary <string, List <string>> cultures = appResourcesCompiler.CultureFiles;
99 List <string> defaultCultureFiles = appResourcesCompiler.DefaultCultureFiles;
101 if (defaultCultureFiles != null)
102 BuildDefaultAssembly (defaultCultureFiles, unit);
104 foreach (KeyValuePair <string, List <string>> kvp in cultures)
105 BuildSatelliteAssembly (kvp.Key, kvp.Value);
108 void BuildDefaultAssembly (List <string> files, CodeCompileUnit unit)
110 AssemblyBuilder abuilder = new AssemblyBuilder (Provider);
112 abuilder.AddCodeCompileUnit (unit);
114 CompilerParameters cp = ci.CreateDefaultCompilerParameters ();
115 cp.OutputAssembly = baseAssemblyPath;
116 cp.GenerateExecutable = false;
117 cp.TreatWarningsAsErrors = true;
118 cp.IncludeDebugInformation = config.Debug;
120 foreach (string f in files)
121 cp.EmbeddedResources.Add (f);
123 CompilerResults results = abuilder.BuildAssembly (cp);
127 if (results.NativeCompilerReturnValue == 0) {
128 mainAssembly = results.CompiledAssembly;
129 BuildManager.TopLevelAssemblies.Add (mainAssembly);
131 if (HttpContext.Current.IsCustomErrorEnabled)
132 throw new ApplicationException ("An error occurred while compiling global resources.");
133 throw new CompilationException (null, results.Errors, null);
136 HttpRuntime.WritePreservationFile (mainAssembly, canonicAssemblyName);
137 HttpRuntime.EnableAssemblyMapping (true);
140 void BuildSatelliteAssembly (string cultureName, List <string> files)
142 string assemblyPath = BuildAssemblyPath (cultureName);
143 var info = new ProcessStartInfo ();
144 var al = new Process ();
146 string arguments = SetAlPath (info);
147 var sb = new StringBuilder (arguments);
149 sb.Append ("/c:\"" + cultureName + "\" ");
150 sb.Append ("/t:lib ");
151 sb.Append ("/out:\"" + assemblyPath + "\" ");
152 if (mainAssembly != null)
153 sb.Append ("/template:\"" + mainAssembly.Location + "\" ");
155 string responseFilePath = assemblyPath + ".response";
156 using (FileStream fs = File.OpenWrite (responseFilePath)) {
157 using (StreamWriter sw = new StreamWriter (fs)) {
158 foreach (string f in files)
159 sw.WriteLine ("/embed:\"" + f + "\" ");
162 sb.Append ("@\"" + responseFilePath + "\"");
164 info.Arguments = sb.ToString ();
165 info.CreateNoWindow = true;
166 info.UseShellExecute = false;
167 info.RedirectStandardOutput = true;
168 info.RedirectStandardError = true;
172 var alOutput = new StringCollection ();
173 var alMutex = new Mutex ();
174 DataReceivedEventHandler outputHandler = (object sender, DataReceivedEventArgs args) => {
175 if (args.Data != null) {
177 alOutput.Add (args.Data);
178 alMutex.ReleaseMutex ();
182 al.ErrorDataReceived += outputHandler;
183 al.OutputDataReceived += outputHandler;
185 // TODO: consider using asynchronous processes
188 } catch (Exception ex) {
189 throw new HttpException (String.Format ("Error running {0}", al.StartInfo.FileName), ex);
192 Exception alException = null;
195 al.BeginOutputReadLine ();
196 al.BeginErrorReadLine ();
198 exitCode = al.ExitCode;
199 } catch (Exception ex) {
202 al.CancelErrorRead ();
203 al.CancelOutputRead ();
207 if (exitCode != 0 || alException != null) {
208 // TODO: consider adding a new type of compilation exception,
210 CompilerErrorCollection errors = null;
212 if (alOutput.Count != 0) {
213 foreach (string line in alOutput) {
214 if (!line.StartsWith ("ALINK: error ", StringComparison.Ordinal))
217 errors = new CompilerErrorCollection ();
219 int colon = line.IndexOf (':', 13);
220 string errorNumber = colon != -1 ? line.Substring (13, colon - 13) : "Unknown";
221 string errorText = colon != -1 ? line.Substring (colon + 1) : line.Substring (13);
223 errors.Add (new CompilerError (Path.GetFileName (assemblyPath), 0, 0, errorNumber, errorText));
227 throw new CompilationException (Path.GetFileName (assemblyPath), errors, null);
231 string SetAlPath (ProcessStartInfo info)
233 if (RuntimeHelpers.RunningOnWindows) {
234 info.FileName = MonoExeLocator.MonoPath;
235 return MonoExeLocator.AlPath + " ";
237 info.FileName = MonoExeLocator.AlPath;
242 string BuildAssemblyPath (string cultureName)
244 string baseDir = Path.Combine (baseAssemblyDirectory, cultureName);
245 if (!Directory.Exists (baseDir))
246 Directory.CreateDirectory (baseDir);
248 string baseFileName = Path.GetFileNameWithoutExtension (baseAssemblyPath);
249 string fileName = String.Concat (baseFileName, ".resources.dll");
250 fileName = Path.Combine (baseDir, fileName);
255 CodeCompileUnit GenerateAssemblyInfo (string cultureName)
257 CodeAttributeArgument[] args = new CodeAttributeArgument [1];
258 args [0] = new CodeAttributeArgument (new CodePrimitiveExpression (cultureName));
260 CodeCompileUnit unit = new CodeCompileUnit ();
261 unit.AssemblyCustomAttributes.Add (
262 new CodeAttributeDeclaration (
263 new CodeTypeReference ("System.Reflection.AssemblyCultureAttribute"),
266 args = new CodeAttributeArgument [2];
267 args [0] = new CodeAttributeArgument (new CodePrimitiveExpression ("ASP.NET"));
268 args [1] = new CodeAttributeArgument (new CodePrimitiveExpression (Environment.Version.ToString ()));
269 unit.AssemblyCustomAttributes.Add (
270 new CodeAttributeDeclaration (
271 new CodeTypeReference ("System.CodeDom.Compiler.GeneratedCodeAttribute"),