Merge pull request #1605 from PowerShell/vors/resgen

C#-based resgen
This commit is contained in:
Andy Schwartzmeyer 2016-08-02 12:48:39 -07:00 committed by GitHub
commit f5502bcddc
4 changed files with 198 additions and 139 deletions

2
.gitignore vendored
View file

@ -50,4 +50,4 @@ gen
.profile.ps1
#VS Code files
/.vscode
.vscode

View file

@ -1276,22 +1276,17 @@ function Start-ResGen
[CmdletBinding()]
param()
Get-ChildItem $PSScriptRoot/src -Directory | ? {
Get-ChildItem (Join-Path $_.FullName 'resources') -ErrorAction SilentlyContinue} | % {
$_. Name} | % {
# Add .NET CLI tools to PATH
Find-Dotnet
$module = $_
Get-ChildItem "$PSScriptRoot/src/$module/resources" -Filter '*.resx' | % {
$className = $_.Name.Replace('.resx', '')
$xml = [xml](Get-Content -raw $_.FullName)
$fileName = $className
$genSource = Get-StronglyTypeCsFileForResx -xml $xml -ModuleName $module -ClassName $className
$outPath = "$PSScriptRoot/src/$module/gen/$fileName.cs"
Write-Verbose "ResGen for $outPath"
New-Item -Type Directory -ErrorAction SilentlyContinue (Split-Path $outPath) > $null
Set-Content -Encoding Ascii -Path $outPath -Value $genSource
}
Push-Location "$PSScriptRoot/src/ResGen"
try
{
Start-NativeExecution { dotnet run } | Write-Verbose
}
finally
{
Pop-Location
}
}
@ -1576,129 +1571,6 @@ function script:Start-NativeExecution([scriptblock]$sb)
}
}
function script:Get-StronglyTypeCsFileForResx
{
param($xml, $ModuleName, $ClassName)
# Example
#
# $ClassName = Full.Name.Of.The.ClassFoo
# $shortClassName = ClassFoo
# $namespaceName = Full.Name.Of.The
$shortClassName = $ClassName
$namespaceName = $null
$lastIndexOfDot = $className.LastIndexOf(".")
if ($lastIndexOfDot -ne -1)
{
$namespaceName = $className.Substring(0, $lastIndexOfDot)
$shortClassName = $className.Substring($lastIndexOfDot + 1)
}
$banner = @'
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a Start-ResGen funciton from build.psm1.
// To add or remove a member, edit your .ResX file then rerun Start-ResGen.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
{0}
'@
$namespace = @'
namespace {0} {{
{1}
}}
'@
$body = @'
using System;
using System.Reflection;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class {0} {{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal {0}() {{
}}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {{
get {{
if (object.ReferenceEquals(resourceMan, null)) {{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("{1}.resources.{3}", typeof({0}).GetTypeInfo().Assembly);
resourceMan = temp;
}}
return resourceMan;
}}
}}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {{
get {{
return resourceCulture;
}}
set {{
resourceCulture = value;
}}
}}
{2}
}}
'@
$entry = @'
/// <summary>
/// Looks up a localized string similar to {1}
/// </summary>
internal static string {0} {{
get {{
return ResourceManager.GetString("{0}", resourceCulture);
}}
}}
'@
$entries = $xml.root.data | % {
if ($_) {
$val = $_.value.Replace("`n", "`n ///")
$name = $_.name.Replace(' ', '_')
$entry -f $name,$val
}
} | Out-String
$bodyCode = $body -f $shortClassName,$ModuleName,$entries,$ClassName
if ($NamespaceName)
{
$bodyCode = $namespace -f $NamespaceName, $bodyCode
}
$resultCode = $banner -f $bodyCode
return $resultCode -replace "`r`n?|`n","`r`n"
}
# Builds coming out of this project can have version number as 'a.b.c' OR 'a.b.c-d-f'
# This function converts the above version into major.minor[.build[.revision]] format
function Get-PackageVersionAsMajorMinorBuildRevision

159
src/ResGen/Program.cs Normal file
View file

@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Linq;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
// we are assuming resgen is run with 'dotnet run'
// so we can use relative paths
foreach (string folder in Directory.EnumerateDirectories(".."))
{
string moduleName = Path.GetFileName(folder);
string resourcePath = Path.Combine(folder, "resources");
if (Directory.Exists(resourcePath))
{
string genFolder = Path.Combine(folder, "gen");
if (!Directory.Exists(genFolder))
{
Directory.CreateDirectory(genFolder);
}
foreach (string resxPath in Directory.EnumerateFiles(resourcePath, "*.resx"))
{
string className = Path.GetFileNameWithoutExtension(resxPath);
string sourceCode = GetStronglyTypeCsFileForResx(resxPath, moduleName, className);
string outPath = Path.Combine(genFolder, className + ".cs");
Console.WriteLine("ResGen for " + outPath);
File.WriteAllText(outPath, sourceCode);
}
}
}
}
private static string GetStronglyTypeCsFileForResx(string xmlPath, string moduleName, string className)
{
// Example
//
// className = Full.Name.Of.The.ClassFoo
// shortClassName = ClassFoo
// namespaceName = Full.Name.Of.The
string shortClassName = className;
string namespaceName = null;
int lastIndexOfDot = className.LastIndexOf('.');
if (lastIndexOfDot != -1)
{
namespaceName = className.Substring(0, lastIndexOfDot);
shortClassName = className.Substring(lastIndexOfDot + 1);
}
var entries = new StringBuilder();
XElement root = XElement.Parse(File.ReadAllText(xmlPath));
foreach (var data in root.Elements("data"))
{
string value = data.Value.Replace("\n", "\n ///");
string name = data.Attribute("name").Value.Replace(' ', '_');
entries.AppendFormat(ENTRY, name, value);
}
string bodyCode = string.Format(BODY, shortClassName, moduleName, entries.ToString(), className);
if (namespaceName != null)
{
bodyCode = string.Format(NAMESPACE, namespaceName, bodyCode);
}
string resultCode = string.Format(BANNER, bodyCode).Replace("\r\n?|\n", "\r\n");
return resultCode;
}
private static readonly string BANNER = @"
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a dotnet run from src\ResGen folder.
// To add or remove a member, edit your .resx file then rerun src\ResGen.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
{0}
";
private static readonly string NAMESPACE = @"
namespace {0} {{
{1}
}}
";
private static readonly string BODY = @"
using System;
using System.Reflection;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute(""System.Resources.Tools.StronglyTypedResourceBuilder"", ""4.0.0.0"")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class {0} {{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(""Microsoft.Performance"", ""CA1811:AvoidUncalledPrivateCode"")]
internal {0}() {{
}}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {{
get {{
if (object.ReferenceEquals(resourceMan, null)) {{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(""{1}.resources.{3}"", typeof({0}).GetTypeInfo().Assembly);
resourceMan = temp;
}}
return resourceMan;
}}
}}
/// <summary>
/// Overrides the current threads CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {{
get {{
return resourceCulture;
}}
set {{
resourceCulture = value;
}}
}}
{2}
}}
";
private static readonly string ENTRY = @"
/// <summary>
/// Looks up a localized string similar to {1}
/// </summary>
internal static string {0} {{
get {{
return ResourceManager.GetString(""{0}"", resourceCulture);
}}
}}
";
}
}

28
src/ResGen/project.json Normal file
View file

@ -0,0 +1,28 @@
{
"name": "resgen",
"version": "1.0.0-*",
"description": "Generates C# typed bindings for .resx files",
"buildOptions": {
"emitEntryPoint": true
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": "1.0.0",
"System.Xml.XDocument": "4.0.11"
}
}
},
"runtimes": {
"ubuntu.14.04-x64": { },
"debian.8-x64": { },
"centos.7-x64": { },
"win7-x64": { },
"win81-x64": { },
"win10-x64": { },
"osx.10.11-x64": { }
}
}