Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / GenerateResource.cs
1 //
2 // GenerateResource.cs: Task that generates the resources.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //   Paolo Molaro (lupus@ximian.com)
7 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //   Lluis Sanchez Gual <lluis@novell.com>
9 //   Ankit Jain <jankit@novell.com>
10 //
11 // (C) 2005 Marek Sieradzki
12 // Copyright 2010 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
33 #if NET_2_0
34
35 using System;
36 using System.Text;
37 using System.IO;
38 using System.Collections;
39 using System.Collections.Generic;
40 using System.Resources;
41 using System.Reflection;
42 using System.Xml;
43 using Microsoft.Build.Framework;
44 using Microsoft.Build.Utilities;
45 using Mono.XBuild.Tasks.GenerateResourceInternal;
46 using Mono.XBuild.Utilities;
47
48 namespace Microsoft.Build.Tasks {
49         public sealed class GenerateResource : TaskExtension {
50         
51                 ITaskItem[]     filesWritten;
52                 bool            neverLockTypeAssemblies;
53                 ITaskItem[]     outputResources;
54                 bool            publicClass;
55                 ITaskItem[]     references;
56                 ITaskItem[]     sources;
57                 ITaskItem       stateFile;
58                 string          stronglyTypedClassName;
59                 string          stronglyTypedFilename;
60                 string          stronglyTypedLanguage;
61                 string          stronglyTypedNamespace;
62                 bool            useSourcePath;
63                 
64                 public GenerateResource ()
65                 {
66                         useSourcePath = false;
67                 }
68
69                 public override bool Execute ()
70                 {
71                         if (sources.Length == 0)
72                                 return true;
73
74                         bool result = true;
75                         List  <ITaskItem> temporaryFilesWritten = new List <ITaskItem> ();
76                         if (outputResources == null) {
77                                 foreach (ITaskItem source in sources) {
78                                         string sourceFile = source.ItemSpec;
79                                         string outputFile = Path.ChangeExtension (sourceFile, "resources");
80
81                                         if (IsResgenRequired (sourceFile, outputFile))
82                                                 result &= CompileResourceFile (sourceFile, outputFile);
83
84                                         ITaskItem newItem = new TaskItem (source);
85                                         newItem.ItemSpec = outputFile;
86
87                                         temporaryFilesWritten.Add (newItem);
88                                 }
89                         } else {
90                                 if (sources.Length != outputResources.Length) {
91                                         Log.LogError ("Sources count is different than OutputResources count.");
92                                         return false;
93                                 }
94
95                                 for (int i = 0; i < sources.Length; i ++) {
96                                         if (String.IsNullOrEmpty (outputResources [i].ItemSpec)) {
97                                                 Log.LogError ("Filename of output can not be empty.");
98                                                 result = false;
99                                                 continue;
100                                         }
101
102                                         if (IsResgenRequired (sources [i].ItemSpec, outputResources [i].ItemSpec))
103                                                 result &= CompileResourceFile (sources [i].ItemSpec, outputResources [i].ItemSpec);
104                                         temporaryFilesWritten.Add (outputResources [i]);
105                                 }
106                         }
107                         
108                         filesWritten = temporaryFilesWritten.ToArray ();
109
110                         return result;
111                 }
112                 
113                 // true if the resx file or any file referenced
114                 // by the resx is newer than the .resources file
115                 //
116                 // Code taken from monodevelop
117                 // main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs
118                 bool IsResgenRequired (string resx_filename, string resources_filename)
119                 {
120                         if (IsFileNewerThan (resx_filename, resources_filename)) {
121                                 Log.LogMessage (MessageImportance.Low,
122                                                 "Resource file '{0}' is newer than the source file '{1}', skipping.",
123                                                 resources_filename, resx_filename);
124                                 return true;
125                         }
126
127                         if (String.Compare (Path.GetExtension (resx_filename), ".resx", true) != 0)
128                                 return true;
129
130                         // resx file, check for files referenced from there
131                         XmlTextReader xr = null;
132                         try {
133                                 // look for
134                                 // <data type="System.Resources.ResXFileRef, System.Windows.Forms" ..>
135                                 //   <value>... filename;.. </value>
136                                 // </data>
137                                 xr = new XmlTextReader (resx_filename);
138                                 string basepath = Path.GetDirectoryName (resx_filename);
139                                 while (xr.Read ()) {
140                                         if (xr.NodeType != XmlNodeType.Element ||
141                                                 String.Compare (xr.LocalName, "data") != 0)
142                                                 continue;
143
144                                         string type = xr.GetAttribute ("type");
145                                         if (String.IsNullOrEmpty (type))
146                                                 continue;
147
148                                         if (String.Compare (type, "System.Resources.ResXFileRef, System.Windows.Forms") != 0)
149                                                 continue;
150
151                                         xr.ReadToDescendant ("value");
152                                         if (xr.NodeType != XmlNodeType.Element)
153                                                 continue;
154
155                                         string value = xr.ReadElementContentAsString ();
156
157                                         string [] parts = value.Split (';');
158                                         if (parts.Length > 0) {
159                                                 string referenced_filename = MSBuildUtils.FromMSBuildPath (
160                                                                 Path.Combine (basepath, parts [0]).Trim ());
161                                                 if (File.Exists (referenced_filename) &&
162                                                         IsFileNewerThan (referenced_filename, resources_filename))
163                                                         return true;
164                                         }
165                                 }
166                         } catch (XmlException) {
167                                 // Ignore xml errors, let resgen handle it
168                                 return true;
169                         } finally {
170                                 if (xr != null)
171                                         xr.Close ();
172                         }
173
174                         return false;
175                 }
176
177                 // true if first is newer than second
178                 static bool IsFileNewerThan (string first, string second)
179                 {
180                         FileInfo finfo_first = new FileInfo (first);
181                         FileInfo finfo_second = new FileInfo (second);
182                         return finfo_first.LastWriteTime > finfo_second.LastWriteTime;
183                 }
184
185 #if false
186                 private IResourceReader GetReader (Stream stream, string name)
187                 {
188                         string format = Path.GetExtension (name);
189                         switch (format.ToLowerInvariant ()) {
190                         case ".po":
191                                 return new PoResourceReader (stream);
192                         case ".txt":
193                         case ".text":
194                                 return new TxtResourceReader (stream);
195                         case ".resources":
196                                 return new ResourceReader (stream);
197                         case ".resx":
198                                 ResXResourceReader reader = new ResXResourceReader (stream);
199
200                                 // set correct basepath to resolve relative paths in file refs
201                                 if (useSourcePath)
202                                         reader.BasePath = Path.GetDirectoryName (Path.GetFullPath (name));
203
204                                 return reader;
205                         default:
206                                 throw new Exception ("Unknown format in file " + name);
207                         }
208                 }
209                 
210                 private IResourceWriter GetWriter (Stream stream, string name)
211                 {
212                         string format = Path.GetExtension (name);
213                         switch (format.ToLowerInvariant ()) {
214                         case ".po":
215                                 return new PoResourceWriter (stream);
216                         case ".txt":
217                         case ".text":
218                                 return new TxtResourceWriter (stream);
219                         case ".resources":
220                                 return new ResourceWriter (stream);
221                         case ".resx":
222                                 return new System.Resources.ResXResourceWriter (stream);
223                         default:
224                                 throw new Exception ("Unknown format in file " + name);
225                         }
226                 }
227 #endif
228                 
229                 private bool CompileResourceFile (string sname, string dname )
230                 {
231                         if (!File.Exists (sname)) {
232                                 Log.LogError ("Resource file '{0}' not found.", sname);
233                                 return false;
234                         }
235
236                         Resgen resgen = new Resgen ();
237                         resgen.BuildEngine = this.BuildEngine;
238                         resgen.UseSourcePath = true;
239
240                         resgen.SourceFile = sname;
241                         resgen.OutputFile = dname;
242
243                         return resgen.Execute ();
244                 }
245
246                 [Output]
247                 public ITaskItem[] FilesWritten {
248                         get {
249                                 return filesWritten;
250                         }
251                 }
252
253                 [MonoTODO]
254                 public bool NeverLockTypeAssemblies {
255                         get {
256                                 return neverLockTypeAssemblies;
257                         }
258                         set {
259                                 neverLockTypeAssemblies = value;
260                         }
261                 }
262
263                 [Output]
264                 public ITaskItem[] OutputResources {
265                         get {
266                                 return outputResources;
267                         }
268                         set {
269                                 outputResources = value;
270                         }
271                 }
272                 
273                 public bool PublicClass {
274                         get { return publicClass; }
275                         set { publicClass = value; }
276                 }
277
278                 public ITaskItem[] References {
279                         get {
280                                 return references;
281                         }
282                         set {
283                                 references = value;
284                         }
285                 }
286
287                 [Required]
288                 public ITaskItem[] Sources {
289                         get {
290                                 return sources;
291                         }
292                         set {
293                                 sources = value;
294                         }
295                 }
296
297                 public ITaskItem StateFile {
298                         get {
299                                 return stateFile;
300                         }
301                         set {
302                                 stateFile = value;
303                         }
304                 }
305
306                 [Output]
307                 public string StronglyTypedClassName {
308                         get {
309                                 return stronglyTypedClassName;
310                         }
311                         set {
312                                 stronglyTypedClassName = value;
313                         }
314                 }
315
316                 [Output]
317                 public string StronglyTypedFileName {
318                         get {
319                                 return stronglyTypedFilename;
320                         }
321                         set {
322                                 stronglyTypedFilename = value;
323                         }
324                 }
325
326                 public string StronglyTypedLanguage {
327                         get {
328                                 return stronglyTypedLanguage;
329                         }
330                         set {
331                                 stronglyTypedLanguage = value;
332                         }
333                 }
334
335                 public string StronglyTypedNamespace {
336                         get {
337                                 return stronglyTypedNamespace;
338                         }
339                         set {
340                                 stronglyTypedNamespace = value;
341                         }
342                 }
343
344                 public bool UseSourcePath {
345                         get {
346                                 return useSourcePath;
347                         }
348                         set {
349                                 useSourcePath = value;
350                         }
351                 }
352         }
353
354         class Resgen : ToolTaskExtension
355         {
356                 public Resgen ()
357                 {
358                 }
359
360                 protected internal override void AddCommandLineCommands (
361                                                  CommandLineBuilderExtension commandLine)
362                 {
363                         if (UseSourcePath)
364                                 commandLine.AppendSwitch ("/useSourcePath");
365
366                         commandLine.AppendSwitch (String.Format ("/compile \"{0}{1}\"", SourceFile,
367                                                 OutputFile != null ? "," + OutputFile : ""));
368                 }
369
370                 public override bool Execute ()
371                 {
372                         if (String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MONO_IOMAP")))
373                                 EnvironmentVariables = new string [] { "MONO_IOMAP=drive" };
374                         return base.Execute ();
375                 }
376
377                 protected override string GenerateFullPathToTool ()
378                 {
379                         if (!string.IsNullOrEmpty (ToolPath))
380                                 return Path.Combine (ToolPath, ToolExe);
381                         return ToolLocationHelper.GetPathToDotNetFrameworkFile (ToolExe, TargetDotNetFrameworkVersion.VersionLatest);
382                 }
383
384                 protected override MessageImportance StandardOutputLoggingImportance {
385                         get { return MessageImportance.Low; }
386                 }
387
388                 protected override string ToolName {
389                         get { return "resgen.exe"; }
390                 }
391
392                 public string SourceFile { get; set; }
393                 public string OutputFile { get; set; }
394
395                 public bool UseSourcePath { get; set; }
396         }
397 }
398
399 #endif