Browse Source

Added the new extensible wrapper generator.

pull/1/head
triton 13 years ago
parent
commit
e819156c3b
  1. 36
      src/Generator/Filter.cs
  2. 243
      src/Generator/Generator.cs
  3. 91
      src/Generator/Generator.csproj
  4. 8
      src/Generator/Generator.csproj.user
  5. 10
      src/Generator/Generator.t4properties
  6. 34
      src/Generator/Glob.cs
  7. 79
      src/Generator/Helpers.cs
  8. 1434
      src/Generator/Options.cs
  9. 181
      src/Generator/Program.cs
  10. 36
      src/Generator/Properties/AssemblyInfo.cs
  11. 684
      src/Generator/Templates/CSharpModule.cs
  12. 202
      src/Generator/Templates/CSharpModule.tt
  13. 177
      src/Generator/Transform.cs
  14. 4
      src/Generator/packages.config

36
src/Generator/Filter.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
using System;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
public enum FilterMode {
Include,
Exclude,
External
}
public enum ImplementationType {
@class,
@struct
}
public struct Filter {
public string TypeName { get; set; }
public FilterMode Mode { get; set; }
public ImplementationType ImplType { get; set; }
public static Dictionary<string, Filter> Load (XDocument doc, out FilterMode @default)
{
string value;
@default = (value = (string)doc.Root.Attribute ("default")) != null ? (FilterMode)Enum.Parse (typeof (FilterMode), value) : FilterMode.Include;
var rules = from rule in doc.Root.Elements ()
let mode = (FilterMode)Enum.Parse (typeof (FilterMode), rule.Name.LocalName)
let impl = (value = (string)rule.Attribute ("implementation")) != null ? (ImplementationType)Enum.Parse (typeof (ImplementationType), value) : ImplementationType.@class
select new Filter { TypeName = rule.Value, Mode = mode, ImplType = impl };
return rules.ToDictionary<Filter,string> (r => r.TypeName);
}
}

243
src/Generator/Generator.cs

@ -0,0 +1,243 @@ @@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Cxxi;
using Cxxi.Templates;
public partial class Generator
{
public List<Transformation> Transformations { get; set; }
Library Library;
Options Options;
public Generator(Library library, Options options)
{
Transformations = new List<Transformation>();
Library = library;
Options = options;
}
public void Process()
{
TransformModule();
ProcessModules();
}
// Generates the binding code.
public void Generate()
{
GenerateModules();
}
int UniqueType = 0;
void CleanupText(ref string debugText)
{
// Strip off newlines from the debug text.
if (String.IsNullOrWhiteSpace(debugText))
debugText = String.Empty;
// TODO: Make this transformation in the output.
debugText = Regex.Replace(debugText, " ( )+", " ");
debugText = Regex.Replace(debugText, "\n", "");
}
void ProcessType(Declaration type)
{
// If after all the transformations the type still does
// not have a name, then generate one.
if (String.IsNullOrWhiteSpace(type.Name))
type.Name = String.Format("UnnamedType{0}", UniqueType++);
CleanupText(ref type.DebugText);
}
void ProcessTypes<T>(List<T> types) where T : Declaration
{
foreach (T type in types)
ProcessType(type);
}
void ProcessClasses(List<Class> Classes)
{
ProcessTypes(Classes);
foreach (var @class in Classes)
ProcessTypes(@class.Fields);
}
void ProcessFunctions(List<Function> Functions)
{
ProcessTypes(Functions);
foreach (var function in Functions)
{
if (function.ReturnType == null)
{
// Ignore and warn about unknown types.
function.Ignore = true;
var s = "Function '{0}' was ignored due to unknown return type...";
Console.WriteLine( String.Format(s, function.Name) );
}
ProcessTypes(function.Parameters);
}
}
void ProcessModules()
{
if (String.IsNullOrEmpty(Library.Name))
Library.Name = "";
// Process everything in the global namespace for now.
foreach (var module in Library.Modules)
{
ProcessNamespace(module.Global);
}
}
void ProcessNamespace(Namespace @namespace)
{
ProcessTypes(@namespace.Enums);
ProcessFunctions(@namespace.Functions);
ProcessClasses(@namespace.Classes);
}
void TransformModule()
{
if (String.IsNullOrEmpty(Library.Name))
Library.Name = "";
// Process everything in the global namespace for now.
foreach (var module in Library.Modules)
{
Namespace global = module.Global;
foreach (Enumeration @enum in global.Enums)
TransformEnum(@enum);
foreach (Function function in global.Functions)
TransformFunction(function);
foreach (Class @class in global.Classes)
TransformClass(@class);
}
}
void TransformType(Declaration type)
{
foreach (var transform in Transformations)
transform.ProcessType(type);
}
void TransformClass(Class @class)
{
TransformType(@class);
foreach (var field in @class.Fields)
TransformType(field);
}
void TransformFunction(Function function)
{
TransformType(function);
foreach (var param in function.Parameters)
TransformType(param);
}
void TransformEnum(Enumeration @enum)
{
TransformType(@enum);
foreach (var transform in Transformations)
{
foreach (var item in @enum.Items)
transform.ProcessEnumItem(item);
}
// If the enumeration only has power of two values, assume it's
// a flags enum.
bool isFlags = true;
bool hasBigRange = false;
foreach (var item in @enum.Items)
{
if (item.Name.Length >= 1 && Char.IsDigit(item.Name[0]))
item.Name = String.Format("_{0}", item.Name);
long value = item.Value;
if (value >= 4)
hasBigRange = true;
if (value <= 1 || value.IsPowerOfTwo())
continue;
isFlags = false;
}
// Only apply this heuristic if there are enough values to have a
// reasonable chance that it really is a bitfield.
if (isFlags && hasBigRange)
{
@enum.Modifiers |= Enumeration.EnumModifiers.Flags;
}
// If we still do not have a valid name, then try to guess one
// based on the enum value names.
if (!String.IsNullOrWhiteSpace(@enum.Name))
return;
var names = new List<string>();
foreach (var item in @enum.Items)
names.Add(item.Name);
var prefix = names.ToArray().CommonPrefix();
// Try a simple heuristic to make sure we end up with a valid name.
if (prefix.Length >= 3)
{
prefix = prefix.Trim().Trim(new char[] { '_' });
@enum.Name = prefix;
}
}
void GenerateModules()
{
// Process everything in the global namespace for now.
foreach (var module in Library.Modules)
{
if (module.Ignore || !module.HasDeclarations)
continue;
// Generate the code from templates.
var template = new CSharpModule();
template.Library = Library;
template.Options = Options;
template.Module = module;
if (!Directory.Exists(Options.OutputDir))
Directory.CreateDirectory(Options.OutputDir);
var file = Path.GetFileNameWithoutExtension(module.FileName) + ".cs";
var path = Path.Combine(Options.OutputDir, file);
// Normalize path.
path = Path.GetFullPath(path);
string code = template.TransformText();
Console.WriteLine(" Generated '" + file + "'.");
File.WriteAllText(path, code);
}
}
}

91
src/Generator/Generator.csproj

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{73499B8E-A6A4-42FF-AB8A-754CE2780777}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Cxxi</RootNamespace>
<AssemblyName>Generator</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(SolutionDir)bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="Parser_d">
<HintPath>..\..\bin\Parser_d.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Filter.cs" />
<Compile Include="Generator.cs" />
<Compile Include="Glob.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="Options.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Templates\CSharpModule.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>CSharpModule.tt</DependentUpon>
</Compile>
<Compile Include="Transform.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Templates\CSharpModule.tt">
<Generator>TextTemplatingFilePreprocessor</Generator>
<LastGenOutput>CSharpModule.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bridge\Bridge.csproj">
<Project>{6BEB8FA2-97AA-40B7-AB92-42F6EDDC4490}</Project>
<Name>Bridge</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

8
src/Generator/Generator.csproj.user

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>
<StartArguments>--debug --ns=SDL -outdir=gen/build/NSDL SDL/SDL.h</StartArguments>
<StartWorkingDirectory>C:\Development\cxxi\bin\</StartWorkingDirectory>
</PropertyGroup>
</Project>

10
src/Generator/Generator.t4properties

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TransformationPropertyValues xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Values>
<TransformationPropertyValue>
<FileName>Templates\CSharpModule.tt</FileName>
<PropertyName>Host</PropertyName>
<Value>VisualStudio</Value>
</TransformationPropertyValue>
</Values>
</TransformationPropertyValues>

34
src/Generator/Glob.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.IO;
public static class Glob
{
static public string[] GetFiles(string[] patterns)
{
List<string> filelist = new List<string>();
foreach (string pattern in patterns)
filelist.AddRange(GetFiles(pattern));
string[] files = new string[filelist.Count];
filelist.CopyTo(files, 0);
return files;
}
static public string[] GetFiles(string patternlist)
{
List<string> filelist = new List<string>();
foreach (string pattern in
patternlist.Split(Path.GetInvalidPathChars()))
{
string dir = Path.GetDirectoryName(pattern);
if (String.IsNullOrEmpty(dir)) dir =
Directory.GetCurrentDirectory();
filelist.AddRange(Directory.GetFiles(
Path.GetFullPath(dir),
Path.GetFileName(pattern)));
}
string[] files = new string[filelist.Count];
filelist.CopyTo(files, 0);
return files;
}
}

79
src/Generator/Helpers.cs

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Cxxi.Templates
{
public partial class CSharpModule
{
public Library Library;
public Options Options;
public Module Module;
readonly string DefaultIndent = " ";
// from https://github.com/mono/mono/blob/master/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs
private static string[] keywords = new string[]
{
"abstract","event","new","struct","as","explicit","null","switch","base","extern",
"this","false","operator","throw","break","finally","out","true",
"fixed","override","try","case","params","typeof","catch","for",
"private","foreach","protected","checked","goto","public",
"unchecked","class","if","readonly","unsafe","const","implicit","ref",
"continue","in","return","using","virtual","default",
"interface","sealed","volatile","delegate","internal","do","is",
"sizeof","while","lock","stackalloc","else","static","enum",
"namespace",
"object","bool","byte","float","uint","char","ulong","ushort",
"decimal","int","sbyte","short","double","long","string","void",
"partial", "yield", "where"
};
public static string SafeIdentifier(string proposedName)
{
proposedName = new string(((IEnumerable<char>)proposedName).Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray());
return keywords.Contains(proposedName) ? "@" + proposedName : proposedName;
}
}
public static class IntHelpers
{
public static bool IsPowerOfTwo(this long x)
{
return (x != 0) && ((x & (x - 1)) == 0);
}
}
public static class StringHelpers
{
public static string CommonPrefix(this string[] ss)
{
if (ss.Length == 0)
{
return "";
}
if (ss.Length == 1)
{
return ss[0];
}
int prefixLength = 0;
foreach (char c in ss[0])
{
foreach (string s in ss)
{
if (s.Length <= prefixLength || s[prefixLength] != c)
{
return ss[0].Substring(0, prefixLength);
}
}
prefixLength++;
}
return ss[0]; // all strings identical
}
}
}

1434
src/Generator/Options.cs

File diff suppressed because it is too large Load Diff

181
src/Generator/Program.cs

@ -0,0 +1,181 @@ @@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Mono.Options;
using Cxxi;
public class Options
{
public bool Verbose = false;
public string IncludeDirs;
public string OutputDir;
public bool ShowHelpText = false;
public bool OutputDebug = false;
public string OutputNamespace;
public List<string> Headers;
}
class Program
{
static void ShowHelp(OptionSet options)
{
var module = Process.GetCurrentProcess().MainModule;
var exeName = Path.GetFileName(module.FileName);
Console.WriteLine("Usage: " + exeName + " [options]+ headers");
Console.WriteLine("Generates C# bindings from C/C++ header files.");
Console.WriteLine();
Console.WriteLine("Options:");
options.WriteOptionDescriptions(Console.Out);
}
bool ParseCommandLineOptions(String[] args)
{
var set = new OptionSet()
{
// Parser options
{ "C|compiler=", v => new object() },
{ "D|defines=", v => new object() },
{ "I|include=", v => options.IncludeDirs = v },
// Generator options
{ "ns|namespace=", v => options.OutputNamespace = v },
{ "o|outdir=", v => options.OutputDir = v },
{ "debug", v => options.OutputDebug = true },
// Misc. options
{ "v|verbose", v => { options.Verbose = true; } },
{ "h|?|help", v => options.ShowHelpText = v != null },
};
if (args.Length == 0 || options.ShowHelpText)
{
ShowHelp(set);
return false;
}
try
{
options.Headers = set.Parse(args);
}
catch (OptionException ex)
{
Console.WriteLine("Error parsing the command line.");
ShowHelp(set);
return false;
}
return true;
}
Library library;
Options options;
public void GenerateCode()
{
Console.WriteLine("Generating wrapper code...");
if (library.Modules.Count > 0)
{
var gen = new Generator(library, options);
TransformSDL(gen);
gen.Generate();
}
}
public void ParseNativeHeaders()
{
Console.WriteLine("Parsing native code...");
foreach (var file in options.Headers)
{
var path = String.Empty;
try
{
path = Path.GetFullPath(file);
}
catch (ArgumentException ex)
{
Console.WriteLine("Invalid path '" + file + "'.");
continue;
}
var Opts = new ParserOptions();
Opts.FileName = path;
Opts.Library = library;
Opts.Verbose = false;
if (!ClangParser.Parse(Opts))
{
Console.WriteLine(" Could not parse '" + file + "'.");
continue;
}
Console.WriteLine(" Parsed '" + file + "'.");
}
}
void TransformSDL(Generator g)
{
g.IgnoreEnumWithMatchingItem("SDL_FALSE");
g.IgnoreEnumWithMatchingItem("DUMMY_ENUM_VALUE");
g.IgnoreEnumWithMatchingItem("SDL_ENOMEM");
g.SetNameOfEnumWithMatchingItem("SDL_SCANCODE_UNKNOWN", "ScanCode");
g.SetNameOfEnumWithMatchingItem("SDLK_UNKNOWN", "Key");
g.SetNameOfEnumWithMatchingItem("KMOD_NONE", "KeyModifier");
g.SetNameOfEnumWithMatchingItem("SDL_LOG_CATEGORY_CUSTOM", "LogCategory");
g.GenerateEnumFromMacros("InitFlags", "SDL_INIT_(.*)").SetFlags();
g.GenerateEnumFromMacros("Endianness", "SDL_(.*)_ENDIAN");
g.GenerateEnumFromMacros("KeyState", "SDL_RELEASED", "SDL_PRESSED");
g.GenerateEnumFromMacros("AlphaState", "SDL_ALPHA_(.*)");
g.GenerateEnumFromMacros("HatState", "SDL_HAT_(.*)");
g.IgnoreModuleWithName("SDL_atomic*");
g.IgnoreModuleWithName("SDL_endian*");
g.IgnoreModuleWithName("SDL_main*");
g.IgnoreModuleWithName("SDL_mutex*");
g.IgnoreModuleWithName("SDL_stdinc*");
g.RemovePrefix("SDL_");
g.RemovePrefix("SCANCODE_");
g.RemovePrefix("SDLK_");
g.RemovePrefix("KMOD_");
g.RemovePrefix("LOG_CATEGORY_");
g.Process();
g.FindEnum("PIXELTYPE").Name = "PixelType";
g.FindEnum("BITMAPORDER").Name = "BitmapOrder";
g.FindEnum("PACKEDORDER").Name = "PackedOrder";
g.FindEnum("ARRAYORDER").Name = "ArrayOrder";
g.FindEnum("PACKEDLAYOUT").Name = "PackedLayout";
g.FindEnum("PIXELFORMAT").Name = "PixelFormat";
g.FindEnum("assert_state").Name = "AssertState";
//gen.FindEnum("LOG_CATEGORY").Name = "LogCategory";
}
public void Run(String[] args)
{
options = new Options();
if (!ParseCommandLineOptions(args))
return;
library = new Library(options.OutputNamespace);
ParseNativeHeaders();
GenerateCode();
}
static void Main(String[] args)
{
var program = new Program();
program.Run(args);
Console.ReadKey();
}
}

36
src/Generator/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Generator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("Generator")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("16e665d1-888a-41e4-bb81-02075f7907b2")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

684
src/Generator/Templates/CSharpModule.cs

@ -0,0 +1,684 @@ @@ -0,0 +1,684 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 10.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Cxxi.Templates
{
using System;
#line 1 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "10.0.0.0")]
public partial class CSharpModule : CSharpModuleBase
{
public virtual string TransformText()
{
this.Write("using System;\r\nusing System.Runtime.InteropServices;\r\n\r\nnamespace ");
#line 6 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(SafeIdentifier(Library.Name)));
#line default
#line hidden
this.Write("\r\n{\r\n");
#line 8 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
GenerateDeclarations();
#line default
#line hidden
this.Write("}\r\n\r\n");
return this.GenerationEnvironment.ToString();
}
#line 13 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateDeclarations()
{
PushIndent(DefaultIndent);
bool NeedsNewline = false;
// Generate all the enum declarations for the module.
for(int i = 0; i < Module.Global.Enums.Count; ++i)
{
var E = Module.Global.Enums[i];
if (E.Ignore) continue;
GenerateEnum(E);
NeedsNewline = true;
if (i < Module.Global.Enums.Count - 1)
WriteLine("");
}
if (NeedsNewline)
WriteLine("");
NeedsNewline = false;
// Generate all the struct/class declarations for the module.
for(int i = 0; i < Module.Global.Classes.Count; ++i)
{
var C = Module.Global.Classes[i];
if (C.Ignore) continue;
GenerateClass(C);
NeedsNewline = true;
if (i < Module.Global.Classes.Count - 1)
WriteLine("");
}
if (NeedsNewline)
WriteLine("");
if (Module.Global.HasFunctions)
{
WriteLine("public partial class " + SafeIdentifier(Library.Name));
WriteLine("{");
PushIndent(DefaultIndent);
}
// Generate all the function declarations for the module.
foreach(var E in Module.Global.Functions)
{
GenerateFunction(E);
}
if (Module.Global.HasFunctions)
{
PopIndent();
WriteLine("}");
}
PopIndent();
}
#line default
#line hidden
#line 75 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateTypeCommon(Declaration T)
{
GenerateSummary(T.BriefComment);
GenerateDebug(T);
}
#line default
#line hidden
#line 83 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateClass(Class C)
{
if(C.Ignore) return;
GenerateTypeCommon(C);
Write("public ");
if (C.IsAbstract)
Write("abstract ");
Write("class {0}", C.Name);
if (C.HasBase)
Write(" : {0}", C.Bases[0].Class.Name);
WriteLine(String.Empty);
WriteLine("{");
PushIndent(DefaultIndent);
foreach(var F in C.Fields)
{
GenerateTypeCommon(F);
WriteLine("public {0} {1};", F.Type, F.Name);
}
PopIndent();
WriteLine("}");
}
#line default
#line hidden
#line 115 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateFunction(Function F)
{
if(F.Ignore) return;
GenerateTypeCommon(F);
#line default
#line hidden
#line 120 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("[DllImport(\"");
#line default
#line hidden
#line 121 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(SafeIdentifier(Library.Name)));
#line default
#line hidden
#line 121 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(".dll\")]\r\npublic static extern ");
#line default
#line hidden
#line 122 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(F.ReturnType));
#line default
#line hidden
#line 122 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(" ");
#line default
#line hidden
#line 122 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(SafeIdentifier(F.Name)));
#line default
#line hidden
#line 122 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("(");
#line default
#line hidden
#line 122 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
for(int i = 0; i < F.Parameters.Count; ++i)
{
var P = F.Parameters[i];
Write("{0} {1}", P.Type, SafeIdentifier(P.Name));
if (i < F.Parameters.Count - 1)
Write(", ");
}
#line default
#line hidden
#line 130 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(");\r\n");
#line default
#line hidden
#line 131 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
WriteLine("");
}
#line default
#line hidden
#line 136 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateDebug(Declaration T)
{
if(Options.OutputDebug && !String.IsNullOrWhiteSpace(T.DebugText))
WriteLine("// DEBUG: " + T.DebugText);
}
#line default
#line hidden
#line 144 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateSummary(string Comment)
{
if(String.IsNullOrWhiteSpace(Comment))
return;
#line default
#line hidden
#line 149 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("/// <summary>\r\n/// ");
#line default
#line hidden
#line 151 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(Comment));
#line default
#line hidden
#line 151 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("\r\n/// </summary>\r\n");
#line default
#line hidden
#line 153 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
}
#line default
#line hidden
#line 157 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateInlineSummary(string Comment)
{
if(String.IsNullOrWhiteSpace(Comment))
return;
#line default
#line hidden
#line 162 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("/// <summary> ");
#line default
#line hidden
#line 163 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(Comment));
#line default
#line hidden
#line 163 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(" </summary>\r\n");
#line default
#line hidden
#line 164 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
}
#line default
#line hidden
#line 168 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
public void GenerateEnum(Enumeration E)
{
if(E.Ignore) return;
GenerateTypeCommon(E);
if(E.Modifiers.HasFlag(Enumeration.EnumModifiers.Flags))
WriteLine("[Flags]");
#line default
#line hidden
#line 176 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("public enum ");
#line default
#line hidden
#line 177 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(SafeIdentifier(E.Name)));
#line default
#line hidden
#line 177 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("\r\n");
#line default
#line hidden
#line 178 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
if(E.Type.Type != PrimitiveType.Int32)
WriteLine(" : {0}", E.Type.Type.ConvertToTypeName());
WriteLine("{");
PushIndent(DefaultIndent);
for(int i = 0; i < E.Items.Count; ++i)
{
var I = E.Items[i];
GenerateInlineSummary(I.Comment);
if (I.ExplicitValue)
Write(String.Format("{0} = {1}", SafeIdentifier(I.Name), I.Value));
else
Write(String.Format("{0}", SafeIdentifier(I.Name)));
if (i < E.Items.Count - 1)
WriteLine(",");
}
PopIndent();
WriteLine("");
#line default
#line hidden
#line 198 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
this.Write("}\r\n");
#line default
#line hidden
#line 200 "C:\Users\Tritonite\Development\cxxi\src\Generator\Templates\CSharpModule.tt"
}
#line default
#line hidden
}
#line default
#line hidden
#region Base class
/// <summary>
/// Base class for this transformation
/// </summary>
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "10.0.0.0")]
public class CSharpModuleBase
{
#region Fields
private global::System.Text.StringBuilder generationEnvironmentField;
private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
private global::System.Collections.Generic.List<int> indentLengthsField;
private string currentIndentField = "";
private bool endsWithNewline;
private global::System.Collections.Generic.IDictionary<string, object> sessionField;
#endregion
#region Properties
/// <summary>
/// The string builder that generation-time code is using to assemble generated output
/// </summary>
protected System.Text.StringBuilder GenerationEnvironment
{
get
{
if ((this.generationEnvironmentField == null))
{
this.generationEnvironmentField = new global::System.Text.StringBuilder();
}
return this.generationEnvironmentField;
}
set
{
this.generationEnvironmentField = value;
}
}
/// <summary>
/// The error collection for the generation process
/// </summary>
public System.CodeDom.Compiler.CompilerErrorCollection Errors
{
get
{
if ((this.errorsField == null))
{
this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
}
return this.errorsField;
}
}
/// <summary>
/// A list of the lengths of each indent that was added with PushIndent
/// </summary>
private System.Collections.Generic.List<int> indentLengths
{
get
{
if ((this.indentLengthsField == null))
{
this.indentLengthsField = new global::System.Collections.Generic.List<int>();
}
return this.indentLengthsField;
}
}
/// <summary>
/// Gets the current indent we use when adding lines to the output
/// </summary>
public string CurrentIndent
{
get
{
return this.currentIndentField;
}
}
/// <summary>
/// Current transformation session
/// </summary>
public virtual global::System.Collections.Generic.IDictionary<string, object> Session
{
get
{
return this.sessionField;
}
set
{
this.sessionField = value;
}
}
#endregion
#region Transform-time helpers
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void Write(string textToAppend)
{
if (string.IsNullOrEmpty(textToAppend))
{
return;
}
// If we're starting off, or if the previous text ended with a newline,
// we have to append the current indent first.
if (((this.GenerationEnvironment.Length == 0)
|| this.endsWithNewline))
{
this.GenerationEnvironment.Append(this.currentIndentField);
this.endsWithNewline = false;
}
// Check if the current text ends with a newline
if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
{
this.endsWithNewline = true;
}
// This is an optimization. If the current indent is "", then we don't have to do any
// of the more complex stuff further down.
if ((this.currentIndentField.Length == 0))
{
this.GenerationEnvironment.Append(textToAppend);
return;
}
// Everywhere there is a newline in the text, add an indent after it
textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
// If the text ends with a newline, then we should strip off the indent added at the very end
// because the appropriate indent will be added when the next time Write() is called
if (this.endsWithNewline)
{
this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
}
else
{
this.GenerationEnvironment.Append(textToAppend);
}
}
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void WriteLine(string textToAppend)
{
this.Write(textToAppend);
this.GenerationEnvironment.AppendLine();
this.endsWithNewline = true;
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void Write(string format, params object[] args)
{
this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void WriteLine(string format, params object[] args)
{
this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Raise an error
/// </summary>
public void Error(string message)
{
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
error.ErrorText = message;
this.Errors.Add(error);
}
/// <summary>
/// Raise a warning
/// </summary>
public void Warning(string message)
{
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
error.ErrorText = message;
error.IsWarning = true;
this.Errors.Add(error);
}
/// <summary>
/// Increase the indent
/// </summary>
public void PushIndent(string indent)
{
if ((indent == null))
{
throw new global::System.ArgumentNullException("indent");
}
this.currentIndentField = (this.currentIndentField + indent);
this.indentLengths.Add(indent.Length);
}
/// <summary>
/// Remove the last indent that was added with PushIndent
/// </summary>
public string PopIndent()
{
string returnValue = "";
if ((this.indentLengths.Count > 0))
{
int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
if ((indentLength > 0))
{
returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
}
}
return returnValue;
}
/// <summary>
/// Remove any indentation
/// </summary>
public void ClearIndent()
{
this.indentLengths.Clear();
this.currentIndentField = "";
}
#endregion
#region ToString Helpers
/// <summary>
/// Utility class to produce culture-oriented representation of an object as a string.
/// </summary>
public class ToStringInstanceHelper
{
private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
/// <summary>
/// Gets or sets format provider to be used by ToStringWithCulture method.
/// </summary>
public System.IFormatProvider FormatProvider
{
get
{
return this.formatProviderField ;
}
set
{
if ((value != null))
{
this.formatProviderField = value;
}
}
}
/// <summary>
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
/// </summary>
public string ToStringWithCulture(object objectToConvert)
{
if ((objectToConvert == null))
{
throw new global::System.ArgumentNullException("objectToConvert");
}
System.Type t = objectToConvert.GetType();
System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
typeof(System.IFormatProvider)});
if ((method == null))
{
return objectToConvert.ToString();
}
else
{
return ((string)(method.Invoke(objectToConvert, new object[] {
this.formatProviderField })));
}
}
}
private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
public ToStringInstanceHelper ToStringHelper
{
get
{
return this.toStringHelperField;
}
}
#endregion
}
#endregion
}

202
src/Generator/Templates/CSharpModule.tt

@ -0,0 +1,202 @@ @@ -0,0 +1,202 @@
<#@ template debug="true" language="C#" #>
<#@ output extension=".cs" #>
using System;
using System.Runtime.InteropServices;
namespace <#= SafeIdentifier(Library.Name) #>
{
<#
GenerateDeclarations();
#>
}
<#+
public void GenerateDeclarations()
{
PushIndent(DefaultIndent);
bool NeedsNewline = false;
// Generate all the enum declarations for the module.
for(int i = 0; i < Module.Global.Enums.Count; ++i)
{
var E = Module.Global.Enums[i];
if (E.Ignore) continue;
GenerateEnum(E);
NeedsNewline = true;
if (i < Module.Global.Enums.Count - 1)
WriteLine("");
}
if (NeedsNewline)
WriteLine("");
NeedsNewline = false;
// Generate all the struct/class declarations for the module.
for(int i = 0; i < Module.Global.Classes.Count; ++i)
{
var C = Module.Global.Classes[i];
if (C.Ignore) continue;
GenerateClass(C);
NeedsNewline = true;
if (i < Module.Global.Classes.Count - 1)
WriteLine("");
}
if (NeedsNewline)
WriteLine("");
if (Module.Global.HasFunctions)
{
WriteLine("public partial class " + SafeIdentifier(Library.Name));
WriteLine("{");
PushIndent(DefaultIndent);
}
// Generate all the function declarations for the module.
foreach(var E in Module.Global.Functions)
{
GenerateFunction(E);
}
if (Module.Global.HasFunctions)
{
PopIndent();
WriteLine("}");
}
PopIndent();
}
#>
<#+
public void GenerateTypeCommon(CppType T)
{
GenerateSummary(T.BriefComment);
GenerateDebug(T);
}
#>
<#+
public void GenerateClass(Class C)
{
if(C.Ignore) return;
GenerateTypeCommon(C);
Write("public ");
if (C.IsAbstract)
Write("abstract ");
Write("class {0}", C.Name);
if (C.HasBase)
Write(" : {0}", C.Bases[0].Class.Name);
WriteLine(String.Empty);
WriteLine("{");
PushIndent(DefaultIndent);
foreach(var F in C.Fields)
{
GenerateTypeCommon(F);
WriteLine("public {0} {1};", F.Type, F.Name);
}
PopIndent();
WriteLine("}");
}
#>
<#+
public void GenerateFunction(Function F)
{
if(F.Ignore) return;
GenerateTypeCommon(F);
#>
[DllImport("<#= SafeIdentifier(Library.Name) #>.dll")]
public static extern <#= F.ReturnType #> <#= SafeIdentifier(F.Name) #>(<#+
for(int i = 0; i < F.Parameters.Count; ++i)
{
var P = F.Parameters[i];
Write("{0} {1}", P.Type, SafeIdentifier(P.Name));
if (i < F.Parameters.Count - 1)
Write(", ");
}
#>);
<#+
WriteLine("");
}
#>
<#+
public void GenerateDebug(CppType T)
{
if(Options.OutputDebug && !String.IsNullOrWhiteSpace(T.DebugText))
WriteLine("// DEBUG: " + T.DebugText);
}
#>
<#+
public void GenerateSummary(string Comment)
{
if(String.IsNullOrWhiteSpace(Comment))
return;
#>
/// <summary>
/// <#= Comment #>
/// </summary>
<#+
}
#>
<#+
public void GenerateInlineSummary(string Comment)
{
if(String.IsNullOrWhiteSpace(Comment))
return;
#>
/// <summary> <#= Comment #> </summary>
<#+
}
#>
<#+
public void GenerateEnum(Enumeration E)
{
if(E.Ignore) return;
GenerateTypeCommon(E);
if(E.Modifiers.HasFlag(Enumeration.EnumModifiers.Flags))
WriteLine("[Flags]");
#>
public enum <#= SafeIdentifier(E.Name) #>
<#+
if(E.Type.Type != PrimitiveType.Int32)
WriteLine(" : {0}", E.Type.Type.ConvertToTypeName());
WriteLine("{");
PushIndent(DefaultIndent);
for(int i = 0; i < E.Items.Count; ++i)
{
var I = E.Items[i];
GenerateInlineSummary(I.Comment);
if (I.ExplicitValue)
Write(String.Format("{0} = {1}", SafeIdentifier(I.Name), I.Value));
else
Write(String.Format("{0}", SafeIdentifier(I.Name)));
if (i < E.Items.Count - 1)
WriteLine(",");
}
PopIndent();
WriteLine("");
#>
}
<#+
}
#>

177
src/Generator/Transform.cs

@ -0,0 +1,177 @@ @@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;
using System.Globalization;
using Cxxi;
public abstract class Transformation
{
public virtual bool ProcessType(Declaration type)
{
return false;
}
public virtual bool ProcessEnumItem(Enumeration.Item item)
{
return false;
}
}
public class RenameTransform : Transformation
{
public string Pattern;
public string Replacement;
public RenameTransform(string pattern, string replacement)
{
Pattern = pattern;
Replacement = replacement;
}
public override bool ProcessType(Declaration type)
{
return Rename(ref type.Name);
}
public override bool ProcessEnumItem(Enumeration.Item item)
{
return Rename(ref item.Name);
}
bool Rename(ref string name)
{
string replace = Regex.Replace(name, Pattern, Replacement);
if (!name.Equals(replace))
{
name = replace;
return true;
}
return false;
}
}
public partial class Generator
{
public void RemovePrefix(string prefix)
{
Transformations.Add(new RenameTransform(prefix, String.Empty));
}
public void RemoveType(Declaration type)
{
}
public Enumeration FindEnum(string name)
{
foreach (var module in Library.Modules)
{
var @enum = module.Global.FindEnumWithName(name);
if (@enum != null)
return @enum;
}
return null;
}
public Enumeration GetEnumWithMatchingItem(string Pattern)
{
foreach (var module in Library.Modules)
{
Enumeration @enum = module.Global.FindEnumWithItem(Pattern);
if (@enum == null) continue;
return @enum;
}
return null;
}
public Module IgnoreModuleWithName(string Pattern)
{
Module module = Library.Modules.Find(
m => Regex.Match(m.FilePath, Pattern).Success);
if (module != null)
module.Ignore = true;
return module;
}
public void IgnoreEnumWithMatchingItem(string Pattern)
{
Enumeration @enum = GetEnumWithMatchingItem(Pattern);
if (@enum != null)
@enum.Ignore = true;
}
public void SetNameOfEnumWithMatchingItem(string Pattern, string Name)
{
Enumeration @enum = GetEnumWithMatchingItem(Pattern);
if (@enum != null)
@enum.Name = Name;
}
static bool ParseToNumber(string num, out long val)
{
if (num.StartsWith("0x", StringComparison.CurrentCultureIgnoreCase))
{
num = num.Substring(2);
return long.TryParse(num, NumberStyles.HexNumber,
CultureInfo.CurrentCulture, out val);
}
return long.TryParse(num, out val);
}
static long ParseMacroExpression(string Expression)
{
long val;
if (ParseToNumber(Expression, out val))
return val;
// TODO: Handle string expressions
return 0;
}
public Enumeration.Item GenerateEnumItemFromMacro(MacroDefine macro)
{
var item = new Enumeration.Item();
item.Name = macro.Name;
item.Expression = macro.Expression;
item.Value = ParseMacroExpression(macro.Expression);
return item;
}
public Enumeration GenerateEnumFromMacros(string Name, params string[] Macros)
{
Enumeration @enum = new Enumeration();
@enum.Name = Name;
var pattern = String.Join("|", Macros);
var regex = new Regex(pattern);
foreach (var module in Library.Modules)
{
foreach (var macro in module.Macros)
{
var match = regex.Match(macro.Name);
if (!match.Success) continue;
var item = GenerateEnumItemFromMacro(macro);
@enum.AddItem(item);
}
if (@enum.Items.Count > 0)
{
module.Global.Enums.Add(@enum);
break;
}
}
return @enum;
}
}

4
src/Generator/packages.config

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommandLineParser" version="1.9.3.19" targetFramework="net40-Client" />
</packages>
Loading…
Cancel
Save