Skip to content

Add initial Emscripten generator. #1712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/UsersManual.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ There is also experimental support for these JavaScript-related targets:
- N-API (Node.js)
- QuickJS
- TypeScript
- Emscripten

# 3. Native Targets

Expand Down
37 changes: 25 additions & 12 deletions src/CLI/CLI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CppSharp.Generators;
using Mono.Options;

namespace CppSharp
Expand All @@ -15,11 +16,11 @@ static bool ParseCommandLineArgs(string[] args, List<string> errorMessages, ref
{
var showHelp = false;

optionSet.Add("I=", "the {PATH} of a folder to search for include files", (i) => { AddIncludeDirs(i, errorMessages); });
optionSet.Add("I=", "the {PATH} of a folder to search for include files", i => { AddIncludeDirs(i, errorMessages); });
optionSet.Add("l=", "{LIBRARY} that that contains the symbols of the generated code", l => options.Libraries.Add(l));
optionSet.Add("L=", "the {PATH} of a folder to search for additional libraries", l => options.LibraryDirs.Add(l));
optionSet.Add("D:", "additional define with (optional) value to add to be used while parsing the given header files", (n, v) => AddDefine(n, v, errorMessages));
optionSet.Add("A=", "additional Clang arguments to pass to the compiler while parsing the given header files", (v) => AddArgument(v, errorMessages));
optionSet.Add("A=", "additional Clang arguments to pass to the compiler while parsing the given header files", v => AddArgument(v, errorMessages));

optionSet.Add("o=|output=", "the {PATH} for the generated bindings file (doesn't need the extension since it will depend on the generator)", v => HandleOutputArg(v, errorMessages));
optionSet.Add("on=|outputnamespace=", "the {NAMESPACE} that will be used for the generated code", on => options.OutputNamespace = on);
Expand All @@ -30,8 +31,8 @@ static bool ParseCommandLineArgs(string[] args, List<string> errorMessages, ref
optionSet.Add("d|debug", "enables debug mode which generates more verbose code to aid debugging", v => options.Debug = true);
optionSet.Add("c|compile", "enables automatic compilation of the generated code", v => options.Compile = true);
optionSet.Add("g=|gen=|generator=", "the {TYPE} of generated code: 'csharp' or 'cli' ('cli' supported only for Windows)", g => { GetGeneratorKind(g, errorMessages); });
optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux'", p => { GetDestinationPlatform(p, errorMessages); });
optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64'", a => { GetDestinationArchitecture(a, errorMessages); });
optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux' or 'emscripten'", p => { GetDestinationPlatform(p, errorMessages); });
optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64' or 'wasm32' or 'wasm64'", a => { GetDestinationArchitecture(a, errorMessages); });
optionSet.Add("prefix=", "sets a string prefix to the names of generated files", a => { options.Prefix = a; });

optionSet.Add("exceptions", "enables support for C++ exceptions in the parser", v => { options.EnableExceptions = true; });
Expand Down Expand Up @@ -208,26 +209,29 @@ static void GetGeneratorKind(string generator, List<string> errorMessages)
switch (generator.ToLower())
{
case "csharp":
options.Kind = CppSharp.Generators.GeneratorKind.CSharp;
options.Kind = GeneratorKind.CSharp;
return;
case "cli":
options.Kind = CppSharp.Generators.GeneratorKind.CLI;
options.Kind = GeneratorKind.CLI;
return;
case "c":
options.Kind = CppSharp.Generators.GeneratorKind.C;
options.Kind = GeneratorKind.C;
return;
case "cpp":
options.Kind = CppSharp.Generators.GeneratorKind.CPlusPlus;
options.Kind = GeneratorKind.CPlusPlus;
return;
case "napi":
options.Kind = CppSharp.Generators.GeneratorKind.NAPI;
options.Kind = GeneratorKind.NAPI;
return;
case "qjs":
options.Kind = CppSharp.Generators.GeneratorKind.QuickJS;
options.Kind = GeneratorKind.QuickJS;
return;
case "ts":
case "typescript":
options.Kind = CppSharp.Generators.GeneratorKind.TypeScript;
options.Kind = GeneratorKind.TypeScript;
return;
case "emscripten":
options.Kind = GeneratorKind.Emscripten;
return;
}

Expand All @@ -247,6 +251,9 @@ static void GetDestinationPlatform(string platform, List<string> errorMessages)
case "linux":
options.Platform = TargetPlatform.Linux;
return;
case "emscripten":
options.Platform = TargetPlatform.Emscripten;
return;
}

errorMessages.Add($"Unknown target platform: {platform}. Defaulting to {options.Platform}");
Expand All @@ -262,6 +269,12 @@ static void GetDestinationArchitecture(string architecture, List<string> errorMe
case "x64":
options.Architecture = TargetArchitecture.x64;
return;
case "wasm32":
options.Architecture = TargetArchitecture.WASM32;
return;
case "wasm64":
options.Architecture = TargetArchitecture.WASM64;
return;
}

errorMessages.Add($"Unknown target architecture: {architecture}. Defaulting to {options.Architecture}");
Expand All @@ -286,7 +299,7 @@ static void Main(string[] args)
return;
}

Generator gen = new Generator(options);
var gen = new Generator(options);

bool validOptions = gen.ValidateOptions(errorMessages);
PrintErrorMessages(errorMessages);
Expand Down
61 changes: 42 additions & 19 deletions src/CLI/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,51 @@ void SetupTargetTriple()
{
var tripleBuilder = new StringBuilder();

if (options.Architecture == TargetArchitecture.x64)
tripleBuilder.Append("x86_64-");
else if (options.Architecture == TargetArchitecture.x86)
tripleBuilder.Append("i686-");

if (options.Platform == TargetPlatform.Windows)
{
tripleBuilder.Append("pc-win32-msvc");
abi = CppAbi.Microsoft;
}
else if (options.Platform == TargetPlatform.MacOS)
switch (options.Architecture)
{
tripleBuilder.Append("apple-darwin12.4.0");
abi = CppAbi.Itanium;
case TargetArchitecture.x64:
tripleBuilder.Append("x86_64-");
break;
case TargetArchitecture.x86:
tripleBuilder.Append("i686-");
break;
case TargetArchitecture.WASM32:
tripleBuilder.Append("wasm32-");
break;
case TargetArchitecture.WASM64:
tripleBuilder.Append("wasm64-");
break;
}
else if (options.Platform == TargetPlatform.Linux)
{
tripleBuilder.Append("linux-gnu");
abi = CppAbi.Itanium;

if (options.Cpp11ABI)
tripleBuilder.Append("-cxx11abi");
switch (options.Platform)
{
case TargetPlatform.Windows:
tripleBuilder.Append("pc-win32-msvc");
abi = CppAbi.Microsoft;
break;
case TargetPlatform.MacOS:
tripleBuilder.Append("apple-darwin12.4.0");
abi = CppAbi.Itanium;
break;
case TargetPlatform.Linux:
{
tripleBuilder.Append("linux-gnu");
abi = CppAbi.Itanium;

if (options.Cpp11ABI)
tripleBuilder.Append("-cxx11abi");
break;
}
case TargetPlatform.Emscripten:
{
if (options.Architecture != TargetArchitecture.WASM32 &&
options.Architecture != TargetArchitecture.WASM64)
throw new Exception("Emscripten target is only compatible with WASM architectures");

tripleBuilder.Append("unknown-emscripten");
abi = CppAbi.Itanium;
break;
}
}

triple = tripleBuilder.ToString();
Expand Down
4 changes: 3 additions & 1 deletion src/CLI/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ namespace CppSharp
enum TargetArchitecture
{
x86,
x64
x64,
WASM32,
WASM64
}

class Options
Expand Down
3 changes: 2 additions & 1 deletion src/Core/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public enum TargetPlatform
MacOS,
iOS,
WatchOS,
TVOS
TVOS,
Emscripten,
}

public static class Platform
Expand Down
3 changes: 3 additions & 0 deletions src/Generator/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using CppSharp.Generators.CLI;
using CppSharp.Generators.Cpp;
using CppSharp.Generators.CSharp;
using CppSharp.Generators.Emscripten;
using CppSharp.Generators.TS;
using CppSharp.Parser;
using CppSharp.Passes;
Expand Down Expand Up @@ -43,6 +44,8 @@ Generator CreateGeneratorFromKind(GeneratorKind kind)
return new CLIGenerator(Context);
case GeneratorKind.CSharp:
return new CSharpGenerator(Context);
case GeneratorKind.Emscripten:
return new EmscriptenGenerator(Context);
case GeneratorKind.QuickJS:
return new QuickJSGenerator(Context);
case GeneratorKind.NAPI:
Expand Down
1 change: 1 addition & 0 deletions src/Generator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum GeneratorKind
CSharp = 2,
C,
CPlusPlus,
Emscripten,
ObjectiveC,
Java,
Swift,
Expand Down
73 changes: 73 additions & 0 deletions src/Generator/Generators/Emscripten/EmscriptenGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Collections.Generic;
using System.Linq;
using CppSharp.AST;
using CppSharp.Generators.Cpp;

namespace CppSharp.Generators.Emscripten
{
/// <summary>
/// Emscripten generator responsible for driving the generation of binding files.
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
/// </summary>
public class EmscriptenGenerator : CppGenerator
{
public EmscriptenGenerator(BindingContext context) : base(context)
{
}

public override List<GeneratorOutput> Generate()
{
var outputs = base.Generate();

foreach (var module in Context.Options.Modules)
{
if (module == Context.Options.SystemModule)
continue;

var output = GenerateModule(module);
if (output != null)
{
OnUnitGenerated(output);
outputs.Add(output);
}
}

return outputs;
}

public override List<CodeGenerator> Generate(IEnumerable<TranslationUnit> units)
{
var outputs = new List<CodeGenerator>();

var header = new EmscriptenHeaders(Context, units);
outputs.Add(header);

var source = new EmscriptenSources(Context, units);
outputs.Add(source);

return outputs;
}

public override GeneratorOutput GenerateModule(Module module)
{
if (module == Context.Options.SystemModule)
return null;

var moduleGen = new EmscriptenModule(Context, module);

var output = new GeneratorOutput
{
TranslationUnit = new TranslationUnit
{
FilePath = $"{module.LibraryName}_embind_module.cpp",
Module = module
},
Outputs = new List<CodeGenerator> { moduleGen }
};

output.Outputs[0].Process();

return output;
}
}
}
48 changes: 48 additions & 0 deletions src/Generator/Generators/Emscripten/EmscriptenHeaders.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using CppSharp.AST;

namespace CppSharp.Generators.Emscripten
{
/// <summary>
/// Generates Emscripten Embind C/C++ header files.
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
/// </summary>
public class EmscriptenHeaders : EmscriptenCodeGenerator
{
public EmscriptenHeaders(BindingContext context, IEnumerable<TranslationUnit> units)
: base(context, units)
{
CTypePrinter.PushContext(TypePrinterContextKind.Managed);
}

//public override bool ShouldGenerateNamespaces => false;

public override void Process()
{
GenerateFilePreamble(CommentKind.BCPL);

PushBlock(BlockKind.Includes);
WriteLine("#pragma once");
NewLine();
PopBlock();

var name = GetTranslationUnitName(TranslationUnit);
WriteLine($"extern \"C\" void embind_init_{name}();");
}

public override bool VisitClassDecl(Class @class)
{
return true;
}

public override bool VisitEvent(Event @event)
{
return true;
}

public override bool VisitFieldDecl(Field field)
{
return true;
}
}
}
21 changes: 21 additions & 0 deletions src/Generator/Generators/Emscripten/EmscriptenMarshal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using CppSharp.Generators.C;

namespace CppSharp.Generators.Emscripten
{
public class EmscriptenMarshalNativeToManagedPrinter : MarshalPrinter<MarshalContext, CppTypePrinter>
{
public EmscriptenMarshalNativeToManagedPrinter(MarshalContext marshalContext)
: base(marshalContext)
{
}
}

public class EmscriptenMarshalManagedToNativePrinter : MarshalPrinter<MarshalContext, CppTypePrinter>
{
public EmscriptenMarshalManagedToNativePrinter(MarshalContext ctx)
: base(ctx)
{
Context.MarshalToNative = this;
}
}
}
Loading