Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / GenerateResource.cs
index e65caefba9e7bde18847282ca648df269c2d9b7a..8abdad3b7f7f76c0fa61469ae8074bb9487ee1e0 100644 (file)
@@ -5,8 +5,11 @@
 //   Marek Sieradzki (marek.sieradzki@gmail.com)
 //   Paolo Molaro (lupus@ximian.com)
 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//   Lluis Sanchez Gual <lluis@novell.com>
+//   Ankit Jain <jankit@novell.com>
 //
 // (C) 2005 Marek Sieradzki
+// Copyright 2010 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -36,8 +39,11 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Resources;
 using System.Reflection;
+using System.Xml;
 using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
 using Mono.XBuild.Tasks.GenerateResourceInternal;
+using Mono.XBuild.Utilities;
 
 namespace Microsoft.Build.Tasks {
        public sealed class GenerateResource : TaskExtension {
@@ -62,47 +68,125 @@ namespace Microsoft.Build.Tasks {
 
                public override bool Execute ()
                {
+                       if (sources.Length == 0)
+                               return true;
+
+                       bool result = true;
                        List  <ITaskItem> temporaryFilesWritten = new List <ITaskItem> ();
-                       if (sources.Length != outputResources.Length) {
-                               Log.LogErrorFromException (new Exception ("Sources count is different than OutputResources count."));
-                               return false;
-                       }
-                       
                        if (outputResources == null) {
                                foreach (ITaskItem source in sources) {
                                        string sourceFile = source.ItemSpec;
                                        string outputFile = Path.ChangeExtension (sourceFile, "resources");
-                                       CompileResourceFile (sourceFile, outputFile);
+
+                                       if (IsResgenRequired (sourceFile, outputFile))
+                                               result &= CompileResourceFile (sourceFile, outputFile);
+
+                                       ITaskItem newItem = new TaskItem (source);
+                                       newItem.ItemSpec = outputFile;
+
+                                       temporaryFilesWritten.Add (newItem);
                                }
                        } else {
-                               IEnumerator <ITaskItem> sourceEnum, outputEnum;
-                               sourceEnum = (IEnumerator <ITaskItem>) sources.GetEnumerator ();
-                               outputEnum = (IEnumerator <ITaskItem>) outputResources.GetEnumerator ();
-                               while (sourceEnum.MoveNext ()) {
-                                       outputEnum.MoveNext ();
-                                       string sourceFile = sourceEnum.Current.ItemSpec;
-                                       string outputFile = outputEnum.Current.ItemSpec;
-                                       if (outputFile == String.Empty) {
-                                               Log.LogErrorFromException (new Exception ("Filename of output can not be empty."));
-                                               return false;
-                                       }
-                                       if (CompileResourceFile (sourceFile, outputFile) == false) {
-                                               Log.LogErrorFromException (new Exception ("Error during compiling resource file."));
-                                               return false;
+                               if (sources.Length != outputResources.Length) {
+                                       Log.LogError ("Sources count is different than OutputResources count.");
+                                       return false;
+                               }
+
+                               for (int i = 0; i < sources.Length; i ++) {
+                                       if (String.IsNullOrEmpty (outputResources [i].ItemSpec)) {
+                                               Log.LogError ("Filename of output can not be empty.");
+                                               result = false;
+                                               continue;
                                        }
-                                       temporaryFilesWritten.Add (outputEnum.Current);
+
+                                       if (IsResgenRequired (sources [i].ItemSpec, outputResources [i].ItemSpec))
+                                               result &= CompileResourceFile (sources [i].ItemSpec, outputResources [i].ItemSpec);
+                                       temporaryFilesWritten.Add (outputResources [i]);
                                }
                        }
                        
                        filesWritten = temporaryFilesWritten.ToArray ();
-                       
-                       return true;
+
+                       return result;
                }
                
+               // true if the resx file or any file referenced
+               // by the resx is newer than the .resources file
+               //
+               // Code taken from monodevelop
+               // main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs
+               bool IsResgenRequired (string resx_filename, string resources_filename)
+               {
+                       if (IsFileNewerThan (resx_filename, resources_filename)) {
+                               Log.LogMessage (MessageImportance.Low,
+                                               "Resource file '{0}' is newer than the source file '{1}', skipping.",
+                                               resources_filename, resx_filename);
+                               return true;
+                       }
+
+                       if (String.Compare (Path.GetExtension (resx_filename), ".resx", true) != 0)
+                               return true;
+
+                       // resx file, check for files referenced from there
+                       XmlTextReader xr = null;
+                       try {
+                               // look for
+                               // <data type="System.Resources.ResXFileRef, System.Windows.Forms" ..>
+                               //   <value>... filename;.. </value>
+                               // </data>
+                               xr = new XmlTextReader (resx_filename);
+                               string basepath = Path.GetDirectoryName (resx_filename);
+                               while (xr.Read ()) {
+                                       if (xr.NodeType != XmlNodeType.Element ||
+                                               String.Compare (xr.LocalName, "data") != 0)
+                                               continue;
+
+                                       string type = xr.GetAttribute ("type");
+                                       if (String.IsNullOrEmpty (type))
+                                               continue;
+
+                                       if (String.Compare (type, "System.Resources.ResXFileRef, System.Windows.Forms") != 0)
+                                               continue;
+
+                                       xr.ReadToDescendant ("value");
+                                       if (xr.NodeType != XmlNodeType.Element)
+                                               continue;
+
+                                       string value = xr.ReadElementContentAsString ();
+
+                                       string [] parts = value.Split (';');
+                                       if (parts.Length > 0) {
+                                               string referenced_filename = MSBuildUtils.FromMSBuildPath (
+                                                               Path.Combine (basepath, parts [0]).Trim ());
+                                               if (File.Exists (referenced_filename) &&
+                                                       IsFileNewerThan (referenced_filename, resources_filename))
+                                                       return true;
+                                       }
+                               }
+                       } catch (XmlException) {
+                               // Ignore xml errors, let resgen handle it
+                               return true;
+                       } finally {
+                               if (xr != null)
+                                       xr.Close ();
+                       }
+
+                       return false;
+               }
+
+               // true if first is newer than second
+               static bool IsFileNewerThan (string first, string second)
+               {
+                       FileInfo finfo_first = new FileInfo (first);
+                       FileInfo finfo_second = new FileInfo (second);
+                       return finfo_first.LastWriteTime > finfo_second.LastWriteTime;
+               }
+
+#if false
                private IResourceReader GetReader (Stream stream, string name)
                {
                        string format = Path.GetExtension (name);
-                       switch (format.ToLower ()) {
+                       switch (format.ToLowerInvariant ()) {
                        case ".po":
                                return new PoResourceReader (stream);
                        case ".txt":
@@ -111,7 +195,13 @@ namespace Microsoft.Build.Tasks {
                        case ".resources":
                                return new ResourceReader (stream);
                        case ".resx":
-                               return new System.Resources.ResXResourceReader (stream);
+                               ResXResourceReader reader = new ResXResourceReader (stream);
+
+                               // set correct basepath to resolve relative paths in file refs
+                               if (useSourcePath)
+                                       reader.BasePath = Path.GetDirectoryName (Path.GetFullPath (name));
+
+                               return reader;
                        default:
                                throw new Exception ("Unknown format in file " + name);
                        }
@@ -120,7 +210,7 @@ namespace Microsoft.Build.Tasks {
                private IResourceWriter GetWriter (Stream stream, string name)
                {
                        string format = Path.GetExtension (name);
-                       switch (format.ToLower ()) {
+                       switch (format.ToLowerInvariant ()) {
                        case ".po":
                                return new PoResourceWriter (stream);
                        case ".txt":
@@ -134,38 +224,23 @@ namespace Microsoft.Build.Tasks {
                                throw new Exception ("Unknown format in file " + name);
                        }
                }
+#endif
                
                private bool CompileResourceFile (string sname, string dname )
                {
-                       FileStream source, dest;
-                       IResourceReader reader;
-                       IResourceWriter writer;
-
-                       try {
-                               source = new FileStream (sname, FileMode.Open, FileAccess.Read);
-
-                               reader = GetReader (source, sname);
+                       if (!File.Exists (sname)) {
+                               Log.LogError ("Resource file '{0}' not found.", sname);
+                               return false;
+                       }
 
-                               dest = new FileStream (dname, FileMode.Create, FileAccess.Write);
-                               writer = GetWriter (dest, dname);
+                       Resgen resgen = new Resgen ();
+                       resgen.BuildEngine = this.BuildEngine;
+                       resgen.UseSourcePath = true;
 
-                               int rescount = 0;
-                               foreach (DictionaryEntry e in reader) {
-                                       rescount++;
-                                       object val = e.Value;
-                                       if (val is string)
-                                               writer.AddResource ((string)e.Key, (string)e.Value);
-                                       else
-                                               writer.AddResource ((string)e.Key, e.Value);
-                               }
+                       resgen.SourceFile = sname;
+                       resgen.OutputFile = dname;
 
-                               reader.Close ();
-                               writer.Close ();
-                       } catch (Exception e) {
-                               Log.LogErrorFromException (e);
-                               return false;
-                       }
-                       return true;
+                       return resgen.Execute ();
                }
 
                [Output]
@@ -275,6 +350,50 @@ namespace Microsoft.Build.Tasks {
                        }
                }
        }
+
+       class Resgen : ToolTaskExtension
+       {
+               public Resgen ()
+               {
+               }
+
+               protected internal override void AddCommandLineCommands (
+                                                CommandLineBuilderExtension commandLine)
+               {
+                       if (UseSourcePath)
+                               commandLine.AppendSwitch ("/useSourcePath");
+
+                       commandLine.AppendSwitch (String.Format ("/compile \"{0}{1}\"", SourceFile,
+                                               OutputFile != null ? "," + OutputFile : ""));
+               }
+
+               public override bool Execute ()
+               {
+                       if (String.IsNullOrEmpty (Environment.GetEnvironmentVariable ("MONO_IOMAP")))
+                               EnvironmentVariables = new string [] { "MONO_IOMAP=drive" };
+                       return base.Execute ();
+               }
+
+               protected override string GenerateFullPathToTool ()
+               {
+                       if (!string.IsNullOrEmpty (ToolPath))
+                               return Path.Combine (ToolPath, ToolExe);
+                       return ToolLocationHelper.GetPathToDotNetFrameworkFile (ToolExe, TargetDotNetFrameworkVersion.VersionLatest);
+               }
+
+               protected override MessageImportance StandardOutputLoggingImportance {
+                       get { return MessageImportance.Low; }
+               }
+
+               protected override string ToolName {
+                       get { return "resgen.exe"; }
+               }
+
+               public string SourceFile { get; set; }
+               public string OutputFile { get; set; }
+
+               public bool UseSourcePath { get; set; }
+       }
 }
 
 #endif