Add new command line system; expose internals

internals
Thraka 1 year 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>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>
</Project>

@ -21,7 +21,7 @@ namespace CodeGenerator
if (v == null) return 0;
return v.ToObject<int>();
}
public void LoadFrom(string directory)
public void LoadFrom(string directory, bool excludeInternals = true)
{
JObject typesJson;
@ -65,23 +65,27 @@ namespace CodeGenerator
{
JProperty jp = (JProperty)jt;
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;
}
EnumMember[] elements = jp.Values().Select(v =>
{
return new EnumMember(v["name"].ToString(), v["calc_value"].ToString());
}).ToArray();
return new EnumDefinition(name, elements);
return new EnumDefinition(name, elements, isInternal);
}).Where(x => x != null).ToArray();
Types = typesJson["structs"].Select(jt =>
{
JProperty jp = (JProperty)jt;
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;
}
TypeReference[] fields = jp.Values().Select(v =>
{
if (v["type"].ToString().Contains("static")) { return null; }
@ -94,7 +98,7 @@ namespace CodeGenerator
v["template_type"]?.ToString(),
Enums);
}).Where(tr => tr != null).ToArray();
return new TypeDefinition(name, fields);
return new TypeDefinition(name, fields, isInternal);
}).Where(x => x != null).ToArray();
Functions = functionsJson.Children().Select(jt =>
@ -120,7 +124,9 @@ namespace CodeGenerator
}
}
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;
if (exportedName == null)
@ -184,7 +190,8 @@ namespace CodeGenerator
structName,
comment,
isConstructor,
isDestructor);
isDestructor,
isInternal);
}).Where(od => od != null).ToArray();
if(overloads.Length == 0) return null;
return new FunctionDefinition(name, overloads, Enums);
@ -231,8 +238,9 @@ namespace CodeGenerator
public string Name { get; }
public string FriendlyName { 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;
if (Name.EndsWith('_'))
@ -250,6 +258,7 @@ namespace CodeGenerator
{
_sanitizedNames.Add(el.Name, SanitizeMemberName(el.Name));
}
IsInternal = isInternal;
}
public string SanitizeNames(string text)
@ -302,11 +311,13 @@ namespace CodeGenerator
{
public string Name { 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;
Fields = fields;
IsInternal = isInternal;
}
}
@ -496,6 +507,7 @@ namespace CodeGenerator
public string Comment { get; }
public bool IsConstructor { get; }
public bool IsDestructor { get; }
public bool IsInternal { get; }
public OverloadDefinition(
string exportedName,
@ -506,7 +518,8 @@ namespace CodeGenerator
string structName,
string comment,
bool isConstructor,
bool isDestructor)
bool isDestructor,
bool isInternal)
{
ExportedName = exportedName;
FriendlyName = friendlyName;
@ -518,11 +531,12 @@ namespace CodeGenerator
Comment = comment;
IsConstructor = isConstructor;
IsDestructor = isDestructor;
IsInternal = isInternal;
}
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.RegularExpressions;
using System.Threading.Tasks;
using System.CommandLine;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace CodeGenerator
{
internal static class Program
{
static void Main(string[] args)
private const string InternalNamespace = ".Internal";
static async Task<int> Main(string[] args)
{
string outputPath;
if (args.Length > 0)
{
outputPath = args[0];
}
else
{
outputPath = AppContext.BaseDirectory;
}
// internal vars for command line results used by the rest of the program.
bool runApp = false;
string outputPath = string.Empty;
string libraryName = string.Empty;
bool useInternals = false;
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
#region Command line handler
var optionOutputPath = new Option<DirectoryInfo>(
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;
if (args.Length > 1)
{
libraryName = args[1];
}
else
string value = result.Tokens.Single().Value;
try { return Directory.CreateDirectory(value); }
catch (Exception) { result.ErrorMessage = $"Unable to create directory: {value}"; return null; }
},
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
{
@ -78,7 +126,7 @@ namespace CodeGenerator
string definitionsPath = Path.Combine(AppContext.BaseDirectory, "definitions", libraryName);
var defs = new ImguiDefinitions();
defs.LoadFrom(definitionsPath);
defs.LoadFrom(definitionsPath, !useInternals);
Console.WriteLine($"Outputting generated code files to {outputPath}.");
@ -118,7 +166,7 @@ namespace CodeGenerator
writer.Using("ImGuiNET");
}
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}");
foreach (TypeReference field in td.Fields)
@ -284,6 +332,10 @@ namespace CodeGenerator
{
writer.Using("ImGuiNET");
}
if (useInternals)
{
writer.Using($"{projectNamespace}{InternalNamespace}");
}
writer.WriteLine(string.Empty);
writer.PushBlock($"namespace {projectNamespace}");
writer.PushBlock($"public static unsafe partial class {classPrefix}Native");
@ -347,6 +399,7 @@ namespace CodeGenerator
writer.PopBlock();
}
// Root ImGui* class items - Noninternal
using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}.gen.cs")))
{
writer.Using("System");
@ -360,52 +413,65 @@ namespace CodeGenerator
writer.WriteLine(string.Empty);
writer.PushBlock($"namespace {projectNamespace}");
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 (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.
if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; }
bool hasVaList = false;
for (int i = 0; i < overload.Parameters.Length; i++)
foreach (OverloadDefinition overload in fd.Overloads.Where(o => !o.IsMemberFunction && o.IsInternal == isInternal))
{
TypeReference p = overload.Parameters[i];
string paramType = GetTypeString(p.Type, p.IsFunctionPointer);
if (p.Name == "...") { continue; }
string exportedName = overload.ExportedName;
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.
if (paramType == "va_list")
bool hasVaList = false;
for (int i = 0; i < overload.Parameters.Length; i++)
{
hasVaList = true;
break;
TypeReference p = overload.Parameters[i];
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(
kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();
KeyValuePair<string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending(
kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();
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++)
for (int i = overload.DefaultValues.Count; i >= 0; i--)
{
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)
@ -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}.");
}
}
return 0;
}
private static bool IsStringFieldName(string name)

@ -55,6 +55,17 @@ namespace CodeGenerator
{ "ImPlotFormatter", "IntPtr" },
{ "ImPlotGetter", "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>()
@ -73,6 +84,7 @@ namespace CodeGenerator
"ImVec2",
"ImVec4",
"ImGuiStoragePair",
"ImGuiStyleMod",
};
public static readonly Dictionary<string, string> WellKnownDefaultValues = new Dictionary<string, string>()

Loading…
Cancel
Save