[xbuild] Use files referenced by resx for dependency check.
authorAnkit Jain <radical@corewars.org>
Wed, 15 Sep 2010 19:24:25 +0000 (00:54 +0530)
committerAnkit Jain <radical@corewars.org>
Wed, 15 Sep 2010 19:52:19 +0000 (01:22 +0530)
For .resx resource files, check files referenced by the resx
file also, to determine whether to resgen it or not.

IsResgenRequired () has been taken from MonoDevelop
(main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Formats.MD1/MD1DotNetProjectHandler.cs)

mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/GenerateResource.cs
mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/Utilities.cs

index 98b818303b2e3a3123148620000cbe4e79fa8085..31676af3f4092029edc516418ece07df11dc0841 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,6 +39,7 @@ 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;
@@ -73,7 +77,8 @@ namespace Microsoft.Build.Tasks {
                                        string sourceFile = source.ItemSpec;
                                        string outputFile = Path.ChangeExtension (sourceFile, "resources");
 
-                                       result &= CompileResourceFile (sourceFile, outputFile);
+                                       if (IsResgenRequired (sourceFile, outputFile))
+                                               result &= CompileResourceFile (sourceFile, outputFile);
 
                                        ITaskItem newItem = new TaskItem (source);
                                        source.ItemSpec = outputFile;
@@ -93,7 +98,8 @@ namespace Microsoft.Build.Tasks {
                                                continue;
                                        }
 
-                                       result &= CompileResourceFile (sources [i].ItemSpec, outputResources [i].ItemSpec);
+                                       if (IsResgenRequired (sources [i].ItemSpec, outputResources [i].ItemSpec))
+                                               result &= CompileResourceFile (sources [i].ItemSpec, outputResources [i].ItemSpec);
                                        temporaryFilesWritten.Add (outputResources [i]);
                                }
                        }
@@ -103,6 +109,78 @@ namespace Microsoft.Build.Tasks {
                        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 = Utilities.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)
                {
@@ -154,13 +232,6 @@ namespace Microsoft.Build.Tasks {
                                return false;
                        }
 
-                       if (File.GetLastWriteTime (sname) <= File.GetLastWriteTime (dname)) {
-                               Log.LogMessage (MessageImportance.Low,
-                                               "Resource file '{0}' is newer than the source file '{1}', skipping.",
-                                               dname, sname);
-                               return true;
-                       }
-
                        Resgen resgen = new Resgen ();
                        resgen.BuildEngine = this.BuildEngine;
                        resgen.UseSourcePath = true;
index e9a5ac5915ea8fbc2649e4595fbdec7bc7060169..5a0c56ec9015dd353274c2f6f8c954e7e37036fa 100644 (file)
@@ -28,6 +28,7 @@
 #if NET_2_0
 
 using System;
+using System.IO;
 
 namespace Microsoft.Build.Tasks {
        internal static class Utilities {
@@ -42,6 +43,68 @@ namespace Microsoft.Build.Tasks {
                        }
 
                }
+
+               internal static string FromMSBuildPath (string relPath)
+               {
+                       if (relPath == null || relPath.Length == 0)
+                               return null;
+
+                       bool is_windows = Path.DirectorySeparatorChar == '\\';
+                       string path = relPath;
+                       if (!is_windows)
+                               path = path.Replace ("\\", "/");
+
+                       // a path with drive letter is invalid/unusable on non-windows
+                       if (!is_windows && char.IsLetter (path [0]) && path.Length > 1 && path[1] == ':')
+                               return null;
+
+                       if (System.IO.File.Exists (path)){
+                               return Path.GetFullPath (path);
+                       }
+
+                       if (Path.IsPathRooted (path)) {
+
+                               // Windows paths are case-insensitive. When mapping an absolute path
+                               // we can try to find the correct case for the path.
+
+                               string[] names = path.Substring (1).Split ('/');
+                               string part = "/";
+
+                               for (int n=0; n<names.Length; n++) {
+                                       string[] entries;
+
+                                       if (names [n] == ".."){
+                                               if (part == "/")
+                                                       return ""; // Can go further back. It's not an existing file
+                                               part = Path.GetFullPath (part + "/..");
+                                               continue;
+                                       }
+
+                                       entries = Directory.GetFileSystemEntries (part);
+
+                                       string fpath = null;
+                                       foreach (string e in entries) {
+                                               if (string.Compare (Path.GetFileName (e), names[n], true) == 0) {
+                                                       fpath = e;
+                                                       break;
+                                               }
+                                       }
+                                       if (fpath == null) {
+                                               // Part of the path does not exist. Can't do any more checking.
+                                               part = Path.GetFullPath (part);
+                                               for (; n < names.Length; n++)
+                                                       part += "/" + names[n];
+                                               return part;
+                                       }
+
+                                       part = fpath;
+                               }
+                               return Path.GetFullPath (part);
+                       } else {
+                               return Path.GetFullPath (path);
+                       }
+               }
+
        }
 
 }