2 // GenerateResource.cs: Task that generates the resources.
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>
11 // (C) 2005 Marek Sieradzki
12 // Copyright 2010 Novell, Inc (http://www.novell.com)
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
38 using System.Collections;
39 using System.Collections.Generic;
40 using System.Resources;
41 using System.Reflection;
43 using Microsoft.Build.Framework;
44 using Microsoft.Build.Utilities;
45 using Mono.XBuild.Tasks.GenerateResourceInternal;
46 using Mono.XBuild.Utilities;
48 namespace Microsoft.Build.Tasks {
49 public sealed class GenerateResource : TaskExtension {
51 ITaskItem[] filesWritten;
52 bool neverLockTypeAssemblies;
53 ITaskItem[] outputResources;
55 ITaskItem[] references;
58 string stronglyTypedClassName;
59 string stronglyTypedFilename;
60 string stronglyTypedLanguage;
61 string stronglyTypedNamespace;
64 public GenerateResource ()
66 useSourcePath = false;
69 public override bool Execute ()
71 if (sources.Length == 0)
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");
81 if (IsResgenRequired (sourceFile, outputFile))
82 result &= CompileResourceFile (sourceFile, outputFile);
84 ITaskItem newItem = new TaskItem (source);
85 newItem.ItemSpec = outputFile;
87 temporaryFilesWritten.Add (newItem);
90 if (sources.Length != outputResources.Length) {
91 Log.LogError ("Sources count is different than OutputResources count.");
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.");
102 if (IsResgenRequired (sources [i].ItemSpec, outputResources [i].ItemSpec))
103 result &= CompileResourceFile (sources [i].ItemSpec, outputResources [i].ItemSpec);
104 temporaryFilesWritten.Add (outputResources [i]);
108 filesWritten = temporaryFilesWritten.ToArray ();
113 // true if the resx file or any file referenced
114 // by the resx is newer than the .resources file
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)
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);
127 if (String.Compare (Path.GetExtension (resx_filename), ".resx", true) != 0)
130 // resx file, check for files referenced from there
131 XmlTextReader xr = null;
134 // <data type="System.Resources.ResXFileRef, System.Windows.Forms" ..>
135 // <value>... filename;.. </value>
137 xr = new XmlTextReader (resx_filename);
138 string basepath = Path.GetDirectoryName (resx_filename);
140 if (xr.NodeType != XmlNodeType.Element ||
141 String.Compare (xr.LocalName, "data") != 0)
144 string type = xr.GetAttribute ("type");
145 if (String.IsNullOrEmpty (type))
148 if (String.Compare (type, "System.Resources.ResXFileRef, System.Windows.Forms") != 0)
151 xr.ReadToDescendant ("value");
152 if (xr.NodeType != XmlNodeType.Element)
155 string value = xr.ReadElementContentAsString ();
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))
166 } catch (XmlException) {
167 // Ignore xml errors, let resgen handle it
177 // true if first is newer than second
178 static bool IsFileNewerThan (string first, string second)
180 FileInfo finfo_first = new FileInfo (first);
181 FileInfo finfo_second = new FileInfo (second);
182 return finfo_first.LastWriteTime > finfo_second.LastWriteTime;
186 private IResourceReader GetReader (Stream stream, string name)
188 string format = Path.GetExtension (name);
189 switch (format.ToLowerInvariant ()) {
191 return new PoResourceReader (stream);
194 return new TxtResourceReader (stream);
196 return new ResourceReader (stream);
198 ResXResourceReader reader = new ResXResourceReader (stream);
200 // set correct basepath to resolve relative paths in file refs
202 reader.BasePath = Path.GetDirectoryName (Path.GetFullPath (name));
206 throw new Exception ("Unknown format in file " + name);
210 private IResourceWriter GetWriter (Stream stream, string name)
212 string format = Path.GetExtension (name);
213 switch (format.ToLowerInvariant ()) {
215 return new PoResourceWriter (stream);
218 return new TxtResourceWriter (stream);
220 return new ResourceWriter (stream);
222 return new System.Resources.ResXResourceWriter (stream);
224 throw new Exception ("Unknown format in file " + name);
229 private bool CompileResourceFile (string sname, string dname )
231 if (!File.Exists (sname)) {
232 Log.LogError ("Resource file '{0}' not found.", sname);
236 Resgen resgen = new Resgen ();
237 resgen.BuildEngine = this.BuildEngine;
238 resgen.UseSourcePath = true;
240 resgen.SourceFile = sname;
241 resgen.OutputFile = dname;
243 return resgen.Execute ();
247 public ITaskItem[] FilesWritten {
254 public bool NeverLockTypeAssemblies {
256 return neverLockTypeAssemblies;
259 neverLockTypeAssemblies = value;
264 public ITaskItem[] OutputResources {
266 return outputResources;
269 outputResources = value;
273 public bool PublicClass {
274 get { return publicClass; }
275 set { publicClass = value; }
278 public ITaskItem[] References {
288 public ITaskItem[] Sources {
297 public ITaskItem StateFile {
307 public string StronglyTypedClassName {
309 return stronglyTypedClassName;
312 stronglyTypedClassName = value;
317 public string StronglyTypedFileName {
319 return stronglyTypedFilename;
322 stronglyTypedFilename = value;
326 public string StronglyTypedLanguage {
328 return stronglyTypedLanguage;
331 stronglyTypedLanguage = value;
335 public string StronglyTypedNamespace {
337 return stronglyTypedNamespace;
340 stronglyTypedNamespace = value;
344 public bool UseSourcePath {
346 return useSourcePath;
349 useSourcePath = value;
354 class Resgen : ToolTaskExtension
360 protected internal override void AddCommandLineCommands (
361 CommandLineBuilderExtension commandLine)
364 commandLine.AppendSwitch ("/useSourcePath");
366 commandLine.AppendSwitch (String.Format ("/compile \"{0}{1}\"", SourceFile,
367 OutputFile != null ? "," + OutputFile : ""));
370 public override bool Execute ()
372 if (String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MONO_IOMAP")))
373 EnvironmentVariables = new string [] { "MONO_IOMAP=drive" };
374 return base.Execute ();
377 protected override string GenerateFullPathToTool ()
379 if (!string.IsNullOrEmpty (ToolPath))
380 return Path.Combine (ToolPath, ToolExe);
381 return ToolLocationHelper.GetPathToDotNetFrameworkFile (ToolExe, TargetDotNetFrameworkVersion.VersionLatest);
384 protected override MessageImportance StandardOutputLoggingImportance {
385 get { return MessageImportance.Low; }
388 protected override string ToolName {
389 get { return "resgen.exe"; }
392 public string SourceFile { get; set; }
393 public string OutputFile { get; set; }
395 public bool UseSourcePath { get; set; }