Skip to content
Permalink
Browse files

Add support for union types and emitting a file header. (#76)

* Support traversing more typedef types

* Adding basic support for emitting a prefix to any generated file.

* Adding basic support for union types.
  • Loading branch information...
tannergooding committed Jul 8, 2019
1 parent 6684c9c commit b15d4a79facfec91782abfec91236fe031c2c9df
@@ -83,6 +83,7 @@ public void Close()
using var sw = new StreamWriter(stream, defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen);
{
sw.NewLine = "\n";
sw.Write(_config.HeaderText);

foreach (var usingDirective in usingDirectives)
{
@@ -269,6 +270,8 @@ private void CloseOutputBuilder(Stream stream, OutputBuilder outputBuilder, bool

if (outputBuilder.UsingDirectives.Any() && _config.GenerateMultipleFiles)
{
sw.Write(_config.HeaderText);

foreach (var usingDirective in outputBuilder.UsingDirectives)
{
sw.Write("using");
@@ -1165,6 +1168,11 @@ private void VisitFieldDecl(FieldDecl fieldDecl)

var type = fieldDecl.Type;
var typeName = GetRemappedTypeName(fieldDecl, type, out var nativeTypeName);

if (fieldDecl.Parent.IsUnion)
{
_outputBuilder.WriteIndentedLine("[FieldOffset(0)]");
}
AddNativeTypeNameAttribute(nativeTypeName);

_outputBuilder.WriteIndented(GetAccessSpecifierName(fieldDecl));
@@ -1192,6 +1200,11 @@ private void VisitFieldDecl(FieldDecl fieldDecl)
}
else
{
if (fieldDecl.IsBitField)
{
AddDiagnostic(DiagnosticLevel.Warning, "Unsupported field declaration kind: 'BitField'. Generated bindings may be incomplete.", fieldDecl);
}

_outputBuilder.Write(typeName);
_outputBuilder.Write(' ');
_outputBuilder.Write(escapedName);
@@ -1405,6 +1418,12 @@ private void VisitRecordDecl(RecordDecl recordDecl)

StartUsingOutputBuilder(name);
{
if (recordDecl.IsUnion)
{
_outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
_outputBuilder.WriteIndentedLine("[StructLayout(LayoutKind.Explicit)]");
}

_outputBuilder.WriteIndented(GetAccessSpecifierName(recordDecl));
_outputBuilder.Write(' ');

@@ -1613,7 +1632,7 @@ private void VisitTypedefDecl(TypedefDecl typedefDecl, Type underlyingType)
{
VisitTypedefDecl(typedefDecl, typedefType.Decl.UnderlyingType);
}
else if (!(underlyingType is BuiltinType) && !(underlyingType is TagType))
else if (!(underlyingType is BuiltinType) && !(underlyingType is IncompleteArrayType) && !(underlyingType is TagType))
{
AddDiagnostic(DiagnosticLevel.Error, $"Unsupported underlying type: '{underlyingType.KindSpelling}'. Generating bindings may be incomplete.", typedefDecl);
}
@@ -1667,6 +1686,14 @@ private void VisitTypedefDeclForPointeeType(TypedefDecl typedefDecl, Type pointe
}
StopUsingOutputBuilder();
}
else if (pointeeType is PointerType pointerType)
{
VisitTypedefDeclForPointeeType(typedefDecl, pointerType.PointeeType);
}
else if (pointeeType is TypedefType typedefType)
{
VisitTypedefDeclForPointeeType(typedefDecl, typedefType.Decl.UnderlyingType);
}
else if (!(pointeeType is BuiltinType) && !(pointeeType is TagType))
{
AddDiagnostic(DiagnosticLevel.Error, $"Unsupported pointee type: '{pointeeType.KindSpelling}'. Generating bindings may be incomplete.", typedefDecl);
@@ -11,7 +11,7 @@ public sealed class PInvokeGeneratorConfiguration
private readonly Dictionary<string, string> _remappedNames;
private readonly PInvokeGeneratorConfigurationOptions _options;

public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary<string, string> remappedNames = null, string[] traversalNames = null)
public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary<string, string> remappedNames = null, string[] traversalNames = null)
{
if (excludedNames is null)
{
@@ -51,6 +51,7 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s
_options = options;

ExcludedNames = excludedNames;
HeaderText = headerFile is object ? File.ReadAllText(headerFile) : string.Empty;
LibraryPath = libraryPath;
MethodClassName = methodClassName;
MethodPrefixToStrip = methodPrefixToStrip;
@@ -86,6 +87,8 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s

public bool GenerateUnixTypes => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateUnixTypes);

public string HeaderText { get; }

public string LibraryPath { get;}

public string MethodClassName { get; }
@@ -13,5 +13,7 @@ internal FieldDecl(CXCursor handle) : base(handle, CXCursorKind.CXCursor_FieldDe
public bool IsBitField => Handle.IsBitField;

public bool IsMutable => Handle.CXXField_IsMutable;

public RecordDecl Parent => (RecordDecl)CursorParent;
}
}
@@ -15,5 +15,7 @@ internal RecordDecl(CXCursor handle, CXCursorKind expectedKind) : base(handle, e
}

public IReadOnlyList<FieldDecl> Fields => _fields.Value;

public bool IsUnion => Kind == CXCursorKind.CXCursor_UnionDecl;
}
}
@@ -25,6 +25,7 @@ public static async Task<int> Main(params string[] args)
AddDefineOption(s_rootCommand);
AddExcludeOption(s_rootCommand);
AddFileOption(s_rootCommand);
AddHeaderOption(s_rootCommand);
AddIncludeOption(s_rootCommand);
AddLibraryOption(s_rootCommand);
AddMethodClassNameOption(s_rootCommand);
@@ -44,6 +45,7 @@ public static int Run(InvocationContext context)
var defines = context.ParseResult.ValueForOption<string[]>("define");
var excludedNames = context.ParseResult.ValueForOption<string[]>("exclude");
var files = context.ParseResult.ValueForOption<string[]>("file");
var headerFile = context.ParseResult.ValueForOption<string>("headerFile");
var includeDirs = context.ParseResult.ValueForOption<string[]>("include");
var libraryPath = context.ParseResult.ValueForOption<string>("libraryPath");
var methodClassName = context.ParseResult.ValueForOption<string>("methodClassName");
@@ -168,7 +170,7 @@ public static int Run(InvocationContext context)
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited

var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, configOptions, excludedNames, methodClassName, methodPrefixToStrip, remappedNames, traversalNames);
var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, configOptions, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames);

int exitCode = 0;

@@ -324,6 +326,22 @@ private static void AddFileOption(RootCommand rootCommand)
rootCommand.AddOption(option);
}

private static void AddHeaderOption(RootCommand rootCommand)
{
var argument = new Argument
{
ArgumentType = typeof(string),
Arity = ArgumentArity.ExactlyOne,
Name = "file"
};
argument.SetDefaultValue(string.Empty);

var option = new Option("--headerFile", "A file which contains the header to prefix every generated file with.", argument);
option.AddAlias("-h");

rootCommand.AddOption(option);
}

private static void AddIncludeOption(RootCommand rootCommand)
{
var argument = new Argument {
@@ -35,7 +35,7 @@ private async Task ValidateGeneratedBindingsAsync(string inputContents, string e
using var unsavedFile = CXUnsavedFile.Create(DefaultInputFileName, inputContents);

var unsavedFiles = new CXUnsavedFile[] { unsavedFile };
var config = new PInvokeGeneratorConfiguration(DefaultLibraryPath, DefaultNamespaceName, Path.GetRandomFileName(), configOptions, excludedNames, methodClassName: null, methodPrefixToStrip: null, remappedNames);
var config = new PInvokeGeneratorConfiguration(DefaultLibraryPath, DefaultNamespaceName, Path.GetRandomFileName(), configOptions, excludedNames, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames);

using (var pinvokeGenerator = new PInvokeGenerator(config, (path) => outputStream))
{

0 comments on commit b15d4a7

Please sign in to comment.
You can’t perform that action at this time.