Add new command line system; expose internals

internals
Thraka 2 years ago
parent 421efe7629
commit 2bedfd5898
  1. 1
      src/CodeGenerator/CodeGenerator.csproj
  2. 40
      src/CodeGenerator/ImguiDefinitions.cs
  3. 178
      src/CodeGenerator/Program.cs
  4. 12
      src/CodeGenerator/TypeInfo.cs

@ -25,5 +25,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -21,7 +21,7 @@ namespace CodeGenerator
if (v == null) return 0; if (v == null) return 0;
return v.ToObject<int>(); return v.ToObject<int>();
} }
public void LoadFrom(string directory) public void LoadFrom(string directory, bool excludeInternals = true)
{ {
JObject typesJson; JObject typesJson;
@ -65,23 +65,27 @@ namespace CodeGenerator
{ {
JProperty jp = (JProperty)jt; JProperty jp = (JProperty)jt;
string name = jp.Name; string name = jp.Name;
if (typeLocations?[jp.Name]?.Value<string>().Contains("internal") ?? false) { bool isInternal = typeLocations?[jp.Name]?.Value<string>().Contains("internal") ?? false;
if (excludeInternals && isInternal)
return null; return null;
}
EnumMember[] elements = jp.Values().Select(v => EnumMember[] elements = jp.Values().Select(v =>
{ {
return new EnumMember(v["name"].ToString(), v["calc_value"].ToString()); return new EnumMember(v["name"].ToString(), v["calc_value"].ToString());
}).ToArray(); }).ToArray();
return new EnumDefinition(name, elements); return new EnumDefinition(name, elements, isInternal);
}).Where(x => x != null).ToArray(); }).Where(x => x != null).ToArray();
Types = typesJson["structs"].Select(jt => Types = typesJson["structs"].Select(jt =>
{ {
JProperty jp = (JProperty)jt; JProperty jp = (JProperty)jt;
string name = jp.Name; string name = jp.Name;
if (typeLocations?[jp.Name]?.Value<string>().Contains("internal") ?? false) { bool isInternal = typeLocations?[jp.Name]?.Value<string>().Contains("internal") ?? false;
if (excludeInternals && isInternal)
return null; return null;
}
TypeReference[] fields = jp.Values().Select(v => TypeReference[] fields = jp.Values().Select(v =>
{ {
if (v["type"].ToString().Contains("static")) { return null; } if (v["type"].ToString().Contains("static")) { return null; }
@ -94,7 +98,7 @@ namespace CodeGenerator
v["template_type"]?.ToString(), v["template_type"]?.ToString(),
Enums); Enums);
}).Where(tr => tr != null).ToArray(); }).Where(tr => tr != null).ToArray();
return new TypeDefinition(name, fields); return new TypeDefinition(name, fields, isInternal);
}).Where(x => x != null).ToArray(); }).Where(x => x != null).ToArray();
Functions = functionsJson.Children().Select(jt => Functions = functionsJson.Children().Select(jt =>
@ -120,7 +124,9 @@ namespace CodeGenerator
} }
} }
if (friendlyName == null) { return null; } if (friendlyName == null) { return null; }
if (val["location"]?.ToString().Contains("internal") ?? false) return null; bool isInternal = val["location"]?.ToString().Contains("internal") ?? false;
if (excludeInternals && isInternal)
return null;
string exportedName = ov_cimguiname; string exportedName = ov_cimguiname;
if (exportedName == null) if (exportedName == null)
@ -184,7 +190,8 @@ namespace CodeGenerator
structName, structName,
comment, comment,
isConstructor, isConstructor,
isDestructor); isDestructor,
isInternal);
}).Where(od => od != null).ToArray(); }).Where(od => od != null).ToArray();
if(overloads.Length == 0) return null; if(overloads.Length == 0) return null;
return new FunctionDefinition(name, overloads, Enums); return new FunctionDefinition(name, overloads, Enums);
@ -231,8 +238,9 @@ namespace CodeGenerator
public string Name { get; } public string Name { get; }
public string FriendlyName { get; } public string FriendlyName { get; }
public EnumMember[] Members { get; } public EnumMember[] Members { get; }
public bool IsInternal { get; }
public EnumDefinition(string name, EnumMember[] elements) public EnumDefinition(string name, EnumMember[] elements, bool isInternal)
{ {
Name = name; Name = name;
if (Name.EndsWith('_')) if (Name.EndsWith('_'))
@ -250,6 +258,7 @@ namespace CodeGenerator
{ {
_sanitizedNames.Add(el.Name, SanitizeMemberName(el.Name)); _sanitizedNames.Add(el.Name, SanitizeMemberName(el.Name));
} }
IsInternal = isInternal;
} }
public string SanitizeNames(string text) public string SanitizeNames(string text)
@ -302,11 +311,13 @@ namespace CodeGenerator
{ {
public string Name { get; } public string Name { get; }
public TypeReference[] Fields { get; } public TypeReference[] Fields { get; }
public bool IsInternal { get; }
public TypeDefinition(string name, TypeReference[] fields) public TypeDefinition(string name, TypeReference[] fields, bool isInternal)
{ {
Name = name; Name = name;
Fields = fields; Fields = fields;
IsInternal = isInternal;
} }
} }
@ -496,6 +507,7 @@ namespace CodeGenerator
public string Comment { get; } public string Comment { get; }
public bool IsConstructor { get; } public bool IsConstructor { get; }
public bool IsDestructor { get; } public bool IsDestructor { get; }
public bool IsInternal { get; }
public OverloadDefinition( public OverloadDefinition(
string exportedName, string exportedName,
@ -506,7 +518,8 @@ namespace CodeGenerator
string structName, string structName,
string comment, string comment,
bool isConstructor, bool isConstructor,
bool isDestructor) bool isDestructor,
bool isInternal)
{ {
ExportedName = exportedName; ExportedName = exportedName;
FriendlyName = friendlyName; FriendlyName = friendlyName;
@ -518,11 +531,12 @@ namespace CodeGenerator
Comment = comment; Comment = comment;
IsConstructor = isConstructor; IsConstructor = isConstructor;
IsDestructor = isDestructor; IsDestructor = isDestructor;
IsInternal = isInternal;
} }
public OverloadDefinition WithParameters(TypeReference[] parameters) public OverloadDefinition WithParameters(TypeReference[] parameters)
{ {
return new OverloadDefinition(ExportedName, FriendlyName, parameters, DefaultValues, ReturnType, StructName, Comment, IsConstructor, IsDestructor); return new OverloadDefinition(ExportedName, FriendlyName, parameters, DefaultValues, ReturnType, StructName, Comment, IsConstructor, IsDestructor, IsInternal);
} }
} }
} }

@ -8,37 +8,85 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.CommandLine;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace CodeGenerator namespace CodeGenerator
{ {
internal static class Program internal static class Program
{ {
static void Main(string[] args) private const string InternalNamespace = ".Internal";
static async Task<int> Main(string[] args)
{ {
string outputPath; // internal vars for command line results used by the rest of the program.
if (args.Length > 0) bool runApp = false;
{ string outputPath = string.Empty;
outputPath = args[0]; string libraryName = string.Empty;
} bool useInternals = false;
else
{
outputPath = AppContext.BaseDirectory;
}
if (!Directory.Exists(outputPath)) #region Command line handler
{ var optionOutputPath = new Option<DirectoryInfo>(
Directory.CreateDirectory(outputPath); aliases: new[] { "--outputDir", "-o" },
} description: "The directory to place generated code files.",
parseArgument: result =>
{
if (result.Tokens.Count == 0)
return new DirectoryInfo(AppContext.BaseDirectory);
string libraryName; string value = result.Tokens.Single().Value;
if (args.Length > 1)
{ try { return Directory.CreateDirectory(value); }
libraryName = args[1]; catch (Exception) { result.ErrorMessage = $"Unable to create directory: {value}"; return null; }
} },
else isDefault: true);
var optionLibraryname = new Option<string>(
aliases: new[] { "--library", "-l" },
description: "The library to read parse.",
getDefaultValue: () => "cimgui")
.FromAmong("cimgui", "cimplot", "cimnodes", "cimguizmo");
var optionInternal = new Option<bool>(
name: "--internal",
description: "When set to true, includes the internal header file.",
parseArgument: result =>
{
// Using parse with isDefault: false, instead of the normal validation, allows us to use "--internal" without specifying true to mean true.
if (result.Tokens.Count == 0)
return true;
if (bool.TryParse(result.Tokens.Single().Value, out var value))
return value;
result.ErrorMessage = "Invalid option for --internal. Value must be true or false.";
return false; // ignored because of error message.
},
isDefault: false);
var rootCommand = new RootCommand("Generates code for the ImGui.NET libraries based on the cimgui definition files.");
rootCommand.AddOption(optionInternal);
rootCommand.AddOption(optionOutputPath);
rootCommand.AddOption(optionLibraryname);
rootCommand.SetHandler((outputPathValue, libNameValue, useInternalValue) =>
{ {
libraryName = "cimgui"; outputPath = outputPathValue.FullName;
} libraryName = libNameValue;
useInternals = useInternalValue;
runApp = true;
}, optionOutputPath, optionLibraryname, optionInternal);
var commandResult = await rootCommand.InvokeAsync(args);
if (!runApp)
return commandResult;
#endregion
string projectNamespace = libraryName switch string projectNamespace = libraryName switch
{ {
@ -78,7 +126,7 @@ namespace CodeGenerator
string definitionsPath = Path.Combine(AppContext.BaseDirectory, "definitions", libraryName); string definitionsPath = Path.Combine(AppContext.BaseDirectory, "definitions", libraryName);
var defs = new ImguiDefinitions(); var defs = new ImguiDefinitions();
defs.LoadFrom(definitionsPath); defs.LoadFrom(definitionsPath, !useInternals);
Console.WriteLine($"Outputting generated code files to {outputPath}."); Console.WriteLine($"Outputting generated code files to {outputPath}.");
@ -118,7 +166,7 @@ namespace CodeGenerator
writer.Using("ImGuiNET"); writer.Using("ImGuiNET");
} }
writer.WriteLine(string.Empty); writer.WriteLine(string.Empty);
writer.PushBlock($"namespace {projectNamespace}"); writer.PushBlock($"namespace {projectNamespace}{(td.IsInternal ? InternalNamespace : string.Empty)}");
writer.PushBlock($"public unsafe partial struct {td.Name}"); writer.PushBlock($"public unsafe partial struct {td.Name}");
foreach (TypeReference field in td.Fields) foreach (TypeReference field in td.Fields)
@ -284,6 +332,10 @@ namespace CodeGenerator
{ {
writer.Using("ImGuiNET"); writer.Using("ImGuiNET");
} }
if (useInternals)
{
writer.Using($"{projectNamespace}{InternalNamespace}");
}
writer.WriteLine(string.Empty); writer.WriteLine(string.Empty);
writer.PushBlock($"namespace {projectNamespace}"); writer.PushBlock($"namespace {projectNamespace}");
writer.PushBlock($"public static unsafe partial class {classPrefix}Native"); writer.PushBlock($"public static unsafe partial class {classPrefix}Native");
@ -347,6 +399,7 @@ namespace CodeGenerator
writer.PopBlock(); writer.PopBlock();
} }
// Root ImGui* class items - Noninternal
using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}.gen.cs"))) using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}.gen.cs")))
{ {
writer.Using("System"); writer.Using("System");
@ -360,52 +413,65 @@ namespace CodeGenerator
writer.WriteLine(string.Empty); writer.WriteLine(string.Empty);
writer.PushBlock($"namespace {projectNamespace}"); writer.PushBlock($"namespace {projectNamespace}");
writer.PushBlock($"public static unsafe partial class {classPrefix}"); writer.PushBlock($"public static unsafe partial class {classPrefix}");
foreach (FunctionDefinition fd in defs.Functions) EmitFunctions(false);
writer.PopBlock();
writer.PopBlock();
if (useInternals)
{ {
if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; } writer.PushBlock($"namespace {projectNamespace}{InternalNamespace}");
writer.PushBlock($"public static unsafe partial class {classPrefix}");
EmitFunctions(true);
writer.PopBlock();
writer.PopBlock();
}
foreach (OverloadDefinition overload in fd.Overloads) void EmitFunctions(bool isInternal)
{
foreach (FunctionDefinition fd in defs.Functions)
{ {
string exportedName = overload.ExportedName; if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; }
if (exportedName.StartsWith("ig"))
{
exportedName = exportedName.Substring(2, exportedName.Length - 2);
}
if (exportedName.Contains("~")) { continue; }
if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters.
bool hasVaList = false; foreach (OverloadDefinition overload in fd.Overloads.Where(o => !o.IsMemberFunction && o.IsInternal == isInternal))
for (int i = 0; i < overload.Parameters.Length; i++)
{ {
TypeReference p = overload.Parameters[i]; string exportedName = overload.ExportedName;
string paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (exportedName.StartsWith("ig"))
if (p.Name == "...") { continue; } {
exportedName = exportedName.Substring(2, exportedName.Length - 2);
}
if (exportedName.Contains("~")) { continue; }
if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters.
if (paramType == "va_list") bool hasVaList = false;
for (int i = 0; i < overload.Parameters.Length; i++)
{ {
hasVaList = true; TypeReference p = overload.Parameters[i];
break; string paramType = GetTypeString(p.Type, p.IsFunctionPointer);
if (p.Name == "...") { continue; }
if (paramType == "va_list")
{
hasVaList = true;
break;
}
} }
} if (hasVaList) { continue; }
if (hasVaList) { continue; }
KeyValuePair<string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending( KeyValuePair<string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending(
kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray(); kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();
for (int i = overload.DefaultValues.Count; i >= 0; i--) for (int i = overload.DefaultValues.Count; i >= 0; i--)
{
if (overload.IsMemberFunction) { continue; }
Dictionary<string, string> defaults = new Dictionary<string, string>();
for (int j = 0; j < i; j++)
{ {
defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value); Dictionary<string, string> defaults = new Dictionary<string, string>();
for (int j = 0; j < i; j++)
{
defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
}
EmitOverload(writer, overload, defaults, null, classPrefix);
} }
EmitOverload(writer, overload, defaults, null, classPrefix);
} }
} }
} }
writer.PopBlock();
writer.PopBlock();
} }
foreach (var method in defs.Variants) foreach (var method in defs.Variants)
@ -415,6 +481,8 @@ namespace CodeGenerator
if (!variant.Used) Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}."); if (!variant.Used) Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}.");
} }
} }
return 0;
} }
private static bool IsStringFieldName(string name) private static bool IsStringFieldName(string name)

@ -55,6 +55,17 @@ namespace CodeGenerator
{ "ImPlotFormatter", "IntPtr" }, { "ImPlotFormatter", "IntPtr" },
{ "ImPlotGetter", "IntPtr" }, { "ImPlotGetter", "IntPtr" },
{ "ImPlotTransform", "IntPtr" }, { "ImPlotTransform", "IntPtr" },
// internals
{ "char[5]", "byte*"},
{ "ImGuiDir*", "IntPtr" },
//{ "ImGuiStoragePair", "IntPtr" },
{ "ImGuiDockRequest", "IntPtr" },
{ "ImGuiDockNodeSettings", "IntPtr" },
{ "ImGuiTableColumnIdx", "sbyte" },
{ "ImGuiTableDrawChannelIdx", "byte"},
{ "ImGuiContextHookCallback", "IntPtr" },
{ "ImGuiErrorLogCallback", "IntPtr" },
//{ "ImGuiSizeCallback", "IntPtr"}
}; };
public static readonly List<string> WellKnownEnums = new List<string>() public static readonly List<string> WellKnownEnums = new List<string>()
@ -73,6 +84,7 @@ namespace CodeGenerator
"ImVec2", "ImVec2",
"ImVec4", "ImVec4",
"ImGuiStoragePair", "ImGuiStoragePair",
"ImGuiStyleMod",
}; };
public static readonly Dictionary<string, string> WellKnownDefaultValues = new Dictionary<string, string>() public static readonly Dictionary<string, string> WellKnownDefaultValues = new Dictionary<string, string>()

Loading…
Cancel
Save