Imported Upstream version 6.0.0.172

Former-commit-id: f3cc9b82f3e5bd8f0fd3ebc098f789556b44e9cd
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-04-12 14:10:50 +00:00
parent 8016999e4d
commit 64ac736ec5
32155 changed files with 3981439 additions and 75368 deletions

View File

@@ -26,6 +26,40 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
/*
* When making any changes to this tool, please be aware that these are not
* automatically tested by a Jenkins job, so you need to do that manually.
*
* You need to do the following:
*
* 1.) Build the tool with the `build` profile
* $ make PROFILE=build -C mcs/tools/resx2sr
*
* 2.) Update the SR in corlib:
* $ make -C mcs/class/corlib update-corefx-sr
*
* 3.) Look at the output by doing a `git diff` and also building the code.
*
* 4.) Search for 'RESX_RESOURCE_STRING' and `RESX_EXTRA_ARGUMENTS` in all BCL Makefiles:
* $ git grep RESX_RESOURCE_STRING mcs/class
* $ git grep 'RESX_EXTRA_ARGUMENTS' mcs/class
*
* 5.) As a minimum, repeat steps 2.) and 3.) for each directory containing `RESX_EXTRA_ARGUMENTS`.
* (at the moment, this is only corlib, but please check with `git grep` to make sure).
*
* 6.) To test the tool in `--existing` mode, check out `System.Web.Services`, you can do something
* like this:
*
* RESX_EXTRA_ARGUMENTS = --in=corefx/SR.template.cs --name=System.Web.Services.Res --existing
* RESX_RESOURCE_STRING = ../referencesource/System.Web.Services/System.Web.Services.txt
*
* If you have any questions about the process, please ask me and I'll be glad to help.
*
* December 7th, 2018
* Martin Baulig (mabaul@microsoft.com)
*
*/
using System;
using System.IO;
using System.Collections.Generic;
@@ -40,6 +74,8 @@ public class Program
{
public bool ShowHelp { get; set; }
public string OutputFile { get; set; }
public bool ExistingOnly { get; set; }
public bool WarnConstantMismatch { get; set; }
}
internal static List<string> InputFiles;
@@ -52,13 +88,22 @@ public class Program
InputFiles = new List<string> ();
ExistingKeys = new Dictionary<string, object> ();
string className = "SR";
var p = new OptionSet () {
{ "o|out=", "Specifies output file name",
v => options.OutputFile = v },
{ "i|in=", "Specifies input file name",
v => InputFiles.Add (v) },
{ "h|help", "Display available options",
{ "n|name=", "Name for generated class, default is 'SR'",
v => className = v },
{ "h|help", "Display available options",
v => options.ShowHelp = v != null },
{ "e|existing", "Only update existing values, do not add keys",
v => options.ExistingOnly = true
}, { "warn-mismatch", "Warn about constant mismatches",
v => options.WarnConstantMismatch = true
}
};
List<string> extra;
@@ -87,7 +132,7 @@ public class Program
if (!LoadStrings (resxStrings, extra))
return 3;
GenerateFile (resxStrings, options);
GenerateFile (className, resxStrings, options);
return 0;
}
@@ -101,7 +146,7 @@ public class Program
p.WriteOptionDescriptions (Console.Out);
}
static void GenerateFile (List<Tuple<string, string, string>> txtStrings, CmdOptions options)
static void GenerateFile (string className, List<Tuple<string, string, string>> txtStrings, CmdOptions options)
{
// var outputFile = options.OutputFile ?? "SR.cs";
@@ -111,7 +156,14 @@ public class Program
str.WriteLine ("//");
str.WriteLine ();
str.WriteLine ("partial class SR");
int nsIdx = className.LastIndexOf ('.');
if (nsIdx > 0) {
str.WriteLine ($"namespace {className.Substring (0, nsIdx)}");
str.WriteLine ("{");
className = className.Substring (nsIdx+1);
}
str.WriteLine ($"partial class {className}");
str.WriteLine ("{");
var dict = new Dictionary<string, string> ();
@@ -121,12 +173,29 @@ public class Program
var value = ToCSharpString (entry.Item2);
string found;
if (dict.TryGetValue (entry.Item1, out found)) {
if (found == value)
if (found == value || !options.WarnConstantMismatch)
continue;
str.WriteLine ($"\t// Constant value mismatch");
// The same entry was found with different values in multiple input files.
Console.Error.WriteLine ($"Constant value mismatch for {entry.Item1}:\n\tOld: {found}\n\tNew: {value}");
continue;
}
// Always add to list of seen keys.
dict.Add (entry.Item1, value);
// The following conditional could be simplified to one line, but I belive
// it is easier to understand what it does by writing it verbosely like this.
if (options.ExistingOnly) {
// We read in all entries, then update the strings of the existing ones,
// so skip the non-existing ones.
if (!ExistingKeys.ContainsKey (entry.Item1))
continue;
} else {
dict.Add (entry.Item1, value);
// In this mode of operation, we pass some existing files via `--in=`,
// so we skip the existing ones.
if (ExistingKeys.ContainsKey (entry.Item1))
continue;
}
str.Write ($"\tpublic const string {entry.Item1} = \"{value}\";");
@@ -136,7 +205,21 @@ public class Program
str.WriteLine ();
}
if (options.ExistingOnly) {
// Add any keys that we missed from the input files.
foreach (var v in ExistingKeys.Keys) {
if (!dict.ContainsKey (v)) {
str.WriteLine ($"\tinternal const string {v} = \"{v}\";");
}
}
}
str.WriteLine ("}");
if (nsIdx > 0) {
str.WriteLine ("}");
}
}
}
@@ -156,23 +239,42 @@ public class Program
return false;
}
var rr = new ResXResourceReader (fileName);
rr.UseResXDataNodes = true;
var dict = rr.GetEnumerator ();
while (dict.MoveNext ()) {
var node = (ResXDataNode)dict.Value;
if (ExistingKeys.ContainsKey (node.Name))
continue;
ExistingKeys.Add (node.Name, null);
resourcesStrings.Add (Tuple.Create (node.Name, (string) node.GetValue ((ITypeResolutionService)null), node.Comment));
if (string.Equals (Path.GetExtension (fileName), ".txt", StringComparison.OrdinalIgnoreCase)) {
resourcesStrings.AddRange (ReadTextResources (fileName));
} else {
resourcesStrings.AddRange (ReadResxFile (fileName));
}
}
return true;
}
static IEnumerable<Tuple<string,string,string>> ReadResxFile (string fileName)
{
var rr = new ResXResourceReader (fileName);
rr.UseResXDataNodes = true;
var dict = rr.GetEnumerator ();
while (dict.MoveNext ()) {
var node = (ResXDataNode)dict.Value;
yield return Tuple.Create (node.Name, (string) node.GetValue ((ITypeResolutionService)null), node.Comment);
}
}
static IEnumerable<Tuple<string,string,string>> ReadTextResources (string fileName)
{
foreach (var line in File.ReadAllLines (fileName)) {
if (line.Length == 0 || line[0] == ';') {
continue;
}
var idx = line.IndexOf ('=');
if (idx < 1) {
Console.Error.WriteLine ($"Error reading resource file '{fileName}'");
continue;
}
yield return Tuple.Create (line.Substring (0, idx), line.Substring (idx+1), (string)null);
}
}
static bool LoadInputFiles ()
{
var reg = new Regex (@"\s*public const string (\w+)\s+=\s+");
@@ -191,12 +293,11 @@ public class Program
continue;
var key = match.Groups[1].Value;
if (!ExistingKeys.ContainsKey (key))
ExistingKeys.Add (key, null);
ExistingKeys[key] = null;
}
}
}
return true;
}
}
}