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.
37 using System.Collections;
38 using System.Collections.Generic;
39 using System.Resources;
40 using System.Reflection;
42 using Microsoft.Build.Framework;
43 using Microsoft.Build.Utilities;
44 using Mono.XBuild.Tasks.GenerateResourceInternal;
45 using Mono.XBuild.Utilities;
47 namespace Microsoft.Build.Tasks {
48 public sealed class GenerateResource : TaskExtension {
50 ITaskItem[] filesWritten;
51 bool neverLockTypeAssemblies;
52 ITaskItem[] outputResources;
54 ITaskItem[] references;
57 string stronglyTypedClassName;
58 string stronglyTypedFilename;
59 string stronglyTypedLanguage;
60 string stronglyTypedNamespace;
63 public GenerateResource ()
65 useSourcePath = false;
68 public override bool Execute ()
70 if (sources.Length == 0)
74 List <ITaskItem> temporaryFilesWritten = new List <ITaskItem> ();
75 if (outputResources == null) {
76 foreach (ITaskItem source in sources) {
77 string sourceFile = source.ItemSpec;
78 string outputFile = source.GetMetadata ("AutoGen").Equals ("true", StringComparison.OrdinalIgnoreCase) ?
79 source.ItemSpec.Replace ('\\', '.').Replace ('/', '.') :
80 Path.ChangeExtension (sourceFile, "resources");
82 if (IsResgenRequired (sourceFile, outputFile))
83 result &= CompileResourceFile (sourceFile, outputFile);
85 ITaskItem newItem = new TaskItem (source);
86 newItem.ItemSpec = outputFile;
88 temporaryFilesWritten.Add (newItem);
91 if (sources.Length != outputResources.Length) {
92 Log.LogError ("Sources count is different than OutputResources count.");
96 for (int i = 0; i < sources.Length; i ++) {
97 if (String.IsNullOrEmpty (outputResources [i].ItemSpec)) {
98 Log.LogError ("Filename of output can not be empty.");
103 if (IsResgenRequired (sources [i].ItemSpec, outputResources [i].ItemSpec))
104 result &= CompileResourceFile (sources [i].ItemSpec, outputResources [i].ItemSpec);
105 temporaryFilesWritten.Add (outputResources [i]);
109 filesWritten = temporaryFilesWritten.ToArray ();
114 // true if the resx file or any file referenced
115 // by the resx is newer than the .resources file
117 // Code taken from monodevelop
118 // main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs
119 bool IsResgenRequired (string resx_filename, string resources_filename)
121 if (IsFileNewerThan (resx_filename, resources_filename)) {
122 Log.LogMessage (MessageImportance.Low,
123 "Resource file '{0}' is newer than the source file '{1}', skipping.",
124 resources_filename, resx_filename);
128 if (String.Compare (Path.GetExtension (resx_filename), ".resx", true) != 0)
131 // resx file, check for files referenced from there
132 XmlTextReader xr = null;
135 // <data type="System.Resources.ResXFileRef, System.Windows.Forms" ..>
136 // <value>... filename;.. </value>
138 xr = new XmlTextReader (resx_filename);
139 string basepath = Path.GetDirectoryName (resx_filename);
141 if (xr.NodeType != XmlNodeType.Element ||
142 String.Compare (xr.LocalName, "data") != 0)
145 string type = xr.GetAttribute ("type");
146 if (String.IsNullOrEmpty (type))
149 if (String.Compare (type, "System.Resources.ResXFileRef, System.Windows.Forms") != 0)
152 xr.ReadToDescendant ("value");
153 if (xr.NodeType != XmlNodeType.Element)
156 string value = xr.ReadElementContentAsString ();
158 string [] parts = value.Split (';');
159 if (parts.Length > 0) {
160 string referenced_filename = MSBuildUtils.FromMSBuildPath (
161 Path.Combine (basepath, parts [0]).Trim ());
162 if (File.Exists (referenced_filename) &&
163 IsFileNewerThan (referenced_filename, resources_filename))
167 } catch (XmlException) {
168 // Ignore xml errors, let resgen handle it
178 // true if first is newer than second
179 static bool IsFileNewerThan (string first, string second)
181 FileInfo finfo_first = new FileInfo (first);
182 FileInfo finfo_second = new FileInfo (second);
183 return finfo_first.LastWriteTime > finfo_second.LastWriteTime;
187 private IResourceReader GetReader (Stream stream, string name)
189 string format = Path.GetExtension (name);
190 switch (format.ToLowerInvariant ()) {
192 return new PoResourceReader (stream);
195 return new TxtResourceReader (stream);
197 return new ResourceReader (stream);
199 ResXResourceReader reader = new ResXResourceReader (stream);
201 // set correct basepath to resolve relative paths in file refs
203 reader.BasePath = Path.GetDirectoryName (Path.GetFullPath (name));
207 throw new Exception ("Unknown format in file " + name);
211 private IResourceWriter GetWriter (Stream stream, string name)
213 string format = Path.GetExtension (name);
214 switch (format.ToLowerInvariant ()) {
216 return new PoResourceWriter (stream);
219 return new TxtResourceWriter (stream);
221 return new ResourceWriter (stream);
223 return new System.Resources.ResXResourceWriter (stream);
225 throw new Exception ("Unknown format in file " + name);
230 private bool CompileResourceFile (string sname, string dname )
232 if (!File.Exists (sname)) {
233 Log.LogError ("Resource file '{0}' not found.", sname);
237 Resgen resgen = new Resgen ();
238 resgen.BuildEngine = this.BuildEngine;
239 resgen.UseSourcePath = true;
241 resgen.SourceFile = sname;
242 resgen.OutputFile = dname;
244 return resgen.Execute ();
248 public ITaskItem[] FilesWritten {
255 public bool NeverLockTypeAssemblies {
257 return neverLockTypeAssemblies;
260 neverLockTypeAssemblies = value;
265 public ITaskItem[] OutputResources {
267 return outputResources;
270 outputResources = value;
274 public bool PublicClass {
275 get { return publicClass; }
276 set { publicClass = value; }
279 public ITaskItem[] References {
289 public ITaskItem[] Sources {
298 public ITaskItem StateFile {
308 public string StronglyTypedClassName {
310 return stronglyTypedClassName;
313 stronglyTypedClassName = value;
318 public string StronglyTypedFileName {
320 return stronglyTypedFilename;
323 stronglyTypedFilename = value;
327 public string StronglyTypedLanguage {
329 return stronglyTypedLanguage;
332 stronglyTypedLanguage = value;
336 public string StronglyTypedNamespace {
338 return stronglyTypedNamespace;
341 stronglyTypedNamespace = value;
345 public bool UseSourcePath {
347 return useSourcePath;
350 useSourcePath = value;
355 class Resgen : ToolTaskExtension
361 protected internal override void AddCommandLineCommands (
362 CommandLineBuilderExtension commandLine)
365 commandLine.AppendSwitch ("/useSourcePath");
367 commandLine.AppendSwitch (String.Format ("/compile \"{0}{1}\"", SourceFile,
368 OutputFile != null ? "," + OutputFile : ""));
371 public override bool Execute ()
373 if (String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MONO_IOMAP")))
374 EnvironmentVariables = new string [] { "MONO_IOMAP=drive" };
375 return base.Execute ();
378 protected override string GenerateFullPathToTool ()
380 if (!string.IsNullOrEmpty (ToolPath))
381 return Path.Combine (ToolPath, ToolExe);
382 return ToolLocationHelper.GetPathToDotNetFrameworkFile (ToolExe, TargetDotNetFrameworkVersion.VersionLatest);
385 protected override MessageImportance StandardOutputLoggingImportance {
386 get { return MessageImportance.Low; }
389 protected override string ToolName {
390 get { return "resgen.exe"; }
393 public string SourceFile { get; set; }
394 public string OutputFile { get; set; }
396 public bool UseSourcePath { get; set; }