Skip to content

Commit 117567d

Browse files
authored
Add initial Emscripten generator. (mono#1712)
1 parent 9b06e7b commit 117567d

21 files changed

+719
-33
lines changed

docs/UsersManual.md

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ There is also experimental support for these JavaScript-related targets:
3939
- N-API (Node.js)
4040
- QuickJS
4141
- TypeScript
42+
- Emscripten
4243

4344
# 3. Native Targets
4445

src/CLI/CLI.cs

+25-12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using CppSharp.Generators;
56
using Mono.Options;
67

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

18-
optionSet.Add("I=", "the {PATH} of a folder to search for include files", (i) => { AddIncludeDirs(i, errorMessages); });
19+
optionSet.Add("I=", "the {PATH} of a folder to search for include files", i => { AddIncludeDirs(i, errorMessages); });
1920
optionSet.Add("l=", "{LIBRARY} that that contains the symbols of the generated code", l => options.Libraries.Add(l));
2021
optionSet.Add("L=", "the {PATH} of a folder to search for additional libraries", l => options.LibraryDirs.Add(l));
2122
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));
22-
optionSet.Add("A=", "additional Clang arguments to pass to the compiler while parsing the given header files", (v) => AddArgument(v, errorMessages));
23+
optionSet.Add("A=", "additional Clang arguments to pass to the compiler while parsing the given header files", v => AddArgument(v, errorMessages));
2324

2425
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));
2526
optionSet.Add("on=|outputnamespace=", "the {NAMESPACE} that will be used for the generated code", on => options.OutputNamespace = on);
@@ -30,8 +31,8 @@ static bool ParseCommandLineArgs(string[] args, List<string> errorMessages, ref
3031
optionSet.Add("d|debug", "enables debug mode which generates more verbose code to aid debugging", v => options.Debug = true);
3132
optionSet.Add("c|compile", "enables automatic compilation of the generated code", v => options.Compile = true);
3233
optionSet.Add("g=|gen=|generator=", "the {TYPE} of generated code: 'csharp' or 'cli' ('cli' supported only for Windows)", g => { GetGeneratorKind(g, errorMessages); });
33-
optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux'", p => { GetDestinationPlatform(p, errorMessages); });
34-
optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64'", a => { GetDestinationArchitecture(a, errorMessages); });
34+
optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux' or 'emscripten'", p => { GetDestinationPlatform(p, errorMessages); });
35+
optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64' or 'wasm32' or 'wasm64'", a => { GetDestinationArchitecture(a, errorMessages); });
3536
optionSet.Add("prefix=", "sets a string prefix to the names of generated files", a => { options.Prefix = a; });
3637

3738
optionSet.Add("exceptions", "enables support for C++ exceptions in the parser", v => { options.EnableExceptions = true; });
@@ -208,26 +209,29 @@ static void GetGeneratorKind(string generator, List<string> errorMessages)
208209
switch (generator.ToLower())
209210
{
210211
case "csharp":
211-
options.Kind = CppSharp.Generators.GeneratorKind.CSharp;
212+
options.Kind = GeneratorKind.CSharp;
212213
return;
213214
case "cli":
214-
options.Kind = CppSharp.Generators.GeneratorKind.CLI;
215+
options.Kind = GeneratorKind.CLI;
215216
return;
216217
case "c":
217-
options.Kind = CppSharp.Generators.GeneratorKind.C;
218+
options.Kind = GeneratorKind.C;
218219
return;
219220
case "cpp":
220-
options.Kind = CppSharp.Generators.GeneratorKind.CPlusPlus;
221+
options.Kind = GeneratorKind.CPlusPlus;
221222
return;
222223
case "napi":
223-
options.Kind = CppSharp.Generators.GeneratorKind.NAPI;
224+
options.Kind = GeneratorKind.NAPI;
224225
return;
225226
case "qjs":
226-
options.Kind = CppSharp.Generators.GeneratorKind.QuickJS;
227+
options.Kind = GeneratorKind.QuickJS;
227228
return;
228229
case "ts":
229230
case "typescript":
230-
options.Kind = CppSharp.Generators.GeneratorKind.TypeScript;
231+
options.Kind = GeneratorKind.TypeScript;
232+
return;
233+
case "emscripten":
234+
options.Kind = GeneratorKind.Emscripten;
231235
return;
232236
}
233237

@@ -247,6 +251,9 @@ static void GetDestinationPlatform(string platform, List<string> errorMessages)
247251
case "linux":
248252
options.Platform = TargetPlatform.Linux;
249253
return;
254+
case "emscripten":
255+
options.Platform = TargetPlatform.Emscripten;
256+
return;
250257
}
251258

252259
errorMessages.Add($"Unknown target platform: {platform}. Defaulting to {options.Platform}");
@@ -262,6 +269,12 @@ static void GetDestinationArchitecture(string architecture, List<string> errorMe
262269
case "x64":
263270
options.Architecture = TargetArchitecture.x64;
264271
return;
272+
case "wasm32":
273+
options.Architecture = TargetArchitecture.WASM32;
274+
return;
275+
case "wasm64":
276+
options.Architecture = TargetArchitecture.WASM64;
277+
return;
265278
}
266279

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

289-
Generator gen = new Generator(options);
302+
var gen = new Generator(options);
290303

291304
var validOptions = gen.ValidateOptions(errorMessages);
292305
PrintErrorMessages(errorMessages);

src/CLI/Generator.cs

+42-19
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,51 @@ void SetupTargetTriple()
2626
{
2727
var tripleBuilder = new StringBuilder();
2828

29-
if (options.Architecture == TargetArchitecture.x64)
30-
tripleBuilder.Append("x86_64-");
31-
else if (options.Architecture == TargetArchitecture.x86)
32-
tripleBuilder.Append("i686-");
33-
34-
if (options.Platform == TargetPlatform.Windows)
35-
{
36-
tripleBuilder.Append("pc-win32-msvc");
37-
abi = CppAbi.Microsoft;
38-
}
39-
else if (options.Platform == TargetPlatform.MacOS)
29+
switch (options.Architecture)
4030
{
41-
tripleBuilder.Append("apple-darwin12.4.0");
42-
abi = CppAbi.Itanium;
31+
case TargetArchitecture.x64:
32+
tripleBuilder.Append("x86_64-");
33+
break;
34+
case TargetArchitecture.x86:
35+
tripleBuilder.Append("i686-");
36+
break;
37+
case TargetArchitecture.WASM32:
38+
tripleBuilder.Append("wasm32-");
39+
break;
40+
case TargetArchitecture.WASM64:
41+
tripleBuilder.Append("wasm64-");
42+
break;
4343
}
44-
else if (options.Platform == TargetPlatform.Linux)
45-
{
46-
tripleBuilder.Append("linux-gnu");
47-
abi = CppAbi.Itanium;
4844

49-
if (options.Cpp11ABI)
50-
tripleBuilder.Append("-cxx11abi");
45+
switch (options.Platform)
46+
{
47+
case TargetPlatform.Windows:
48+
tripleBuilder.Append("pc-win32-msvc");
49+
abi = CppAbi.Microsoft;
50+
break;
51+
case TargetPlatform.MacOS:
52+
tripleBuilder.Append("apple-darwin12.4.0");
53+
abi = CppAbi.Itanium;
54+
break;
55+
case TargetPlatform.Linux:
56+
{
57+
tripleBuilder.Append("linux-gnu");
58+
abi = CppAbi.Itanium;
59+
60+
if (options.Cpp11ABI)
61+
tripleBuilder.Append("-cxx11abi");
62+
break;
63+
}
64+
case TargetPlatform.Emscripten:
65+
{
66+
if (options.Architecture != TargetArchitecture.WASM32 &&
67+
options.Architecture != TargetArchitecture.WASM64)
68+
throw new Exception("Emscripten target is only compatible with WASM architectures");
69+
70+
tripleBuilder.Append("unknown-emscripten");
71+
abi = CppAbi.Itanium;
72+
break;
73+
}
5174
}
5275

5376
triple = tripleBuilder.ToString();

src/CLI/Options.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ namespace CppSharp
66
enum TargetArchitecture
77
{
88
x86,
9-
x64
9+
x64,
10+
WASM32,
11+
WASM64
1012
}
1113

1214
class Options

src/Core/Platform.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public enum TargetPlatform
1111
MacOS,
1212
iOS,
1313
WatchOS,
14-
TVOS
14+
TVOS,
15+
Emscripten,
1516
}
1617

1718
public static class Platform

src/Generator/Driver.cs

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using CppSharp.Generators.CLI;
99
using CppSharp.Generators.Cpp;
1010
using CppSharp.Generators.CSharp;
11+
using CppSharp.Generators.Emscripten;
1112
using CppSharp.Generators.TS;
1213
using CppSharp.Parser;
1314
using CppSharp.Passes;
@@ -43,6 +44,8 @@ Generator CreateGeneratorFromKind(GeneratorKind kind)
4344
return new CLIGenerator(Context);
4445
case GeneratorKind.CSharp:
4546
return new CSharpGenerator(Context);
47+
case GeneratorKind.Emscripten:
48+
return new EmscriptenGenerator(Context);
4649
case GeneratorKind.QuickJS:
4750
return new QuickJSGenerator(Context);
4851
case GeneratorKind.NAPI:

src/Generator/Generator.cs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public enum GeneratorKind
1414
CSharp = 2,
1515
C,
1616
CPlusPlus,
17+
Emscripten,
1718
ObjectiveC,
1819
Java,
1920
Swift,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using CppSharp.AST;
4+
using CppSharp.Generators.Cpp;
5+
6+
namespace CppSharp.Generators.Emscripten
7+
{
8+
/// <summary>
9+
/// Emscripten generator responsible for driving the generation of binding files.
10+
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
11+
/// </summary>
12+
public class EmscriptenGenerator : CppGenerator
13+
{
14+
public EmscriptenGenerator(BindingContext context) : base(context)
15+
{
16+
}
17+
18+
public override List<GeneratorOutput> Generate()
19+
{
20+
var outputs = base.Generate();
21+
22+
foreach (var module in Context.Options.Modules)
23+
{
24+
if (module == Context.Options.SystemModule)
25+
continue;
26+
27+
var output = GenerateModule(module);
28+
if (output != null)
29+
{
30+
OnUnitGenerated(output);
31+
outputs.Add(output);
32+
}
33+
}
34+
35+
return outputs;
36+
}
37+
38+
public override List<CodeGenerator> Generate(IEnumerable<TranslationUnit> units)
39+
{
40+
var outputs = new List<CodeGenerator>();
41+
42+
var header = new EmscriptenHeaders(Context, units);
43+
outputs.Add(header);
44+
45+
var source = new EmscriptenSources(Context, units);
46+
outputs.Add(source);
47+
48+
return outputs;
49+
}
50+
51+
public override GeneratorOutput GenerateModule(Module module)
52+
{
53+
if (module == Context.Options.SystemModule)
54+
return null;
55+
56+
var moduleGen = new EmscriptenModule(Context, module);
57+
58+
var output = new GeneratorOutput
59+
{
60+
TranslationUnit = new TranslationUnit
61+
{
62+
FilePath = $"{module.LibraryName}_embind_module.cpp",
63+
Module = module
64+
},
65+
Outputs = new List<CodeGenerator> { moduleGen }
66+
};
67+
68+
output.Outputs[0].Process();
69+
70+
return output;
71+
}
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System.Collections.Generic;
2+
using CppSharp.AST;
3+
4+
namespace CppSharp.Generators.Emscripten
5+
{
6+
/// <summary>
7+
/// Generates Emscripten Embind C/C++ header files.
8+
/// Embind documentation: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
9+
/// </summary>
10+
public class EmscriptenHeaders : EmscriptenCodeGenerator
11+
{
12+
public EmscriptenHeaders(BindingContext context, IEnumerable<TranslationUnit> units)
13+
: base(context, units)
14+
{
15+
CTypePrinter.PushContext(TypePrinterContextKind.Managed);
16+
}
17+
18+
//public override bool ShouldGenerateNamespaces => false;
19+
20+
public override void Process()
21+
{
22+
GenerateFilePreamble(CommentKind.BCPL);
23+
24+
PushBlock(BlockKind.Includes);
25+
WriteLine("#pragma once");
26+
NewLine();
27+
PopBlock();
28+
29+
var name = GetTranslationUnitName(TranslationUnit);
30+
WriteLine($"extern \"C\" void embind_init_{name}();");
31+
}
32+
33+
public override bool VisitClassDecl(Class @class)
34+
{
35+
return true;
36+
}
37+
38+
public override bool VisitEvent(Event @event)
39+
{
40+
return true;
41+
}
42+
43+
public override bool VisitFieldDecl(Field field)
44+
{
45+
return true;
46+
}
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using CppSharp.Generators.C;
2+
3+
namespace CppSharp.Generators.Emscripten
4+
{
5+
public class EmscriptenMarshalNativeToManagedPrinter : MarshalPrinter<MarshalContext, CppTypePrinter>
6+
{
7+
public EmscriptenMarshalNativeToManagedPrinter(MarshalContext marshalContext)
8+
: base(marshalContext)
9+
{
10+
}
11+
}
12+
13+
public class EmscriptenMarshalManagedToNativePrinter : MarshalPrinter<MarshalContext, CppTypePrinter>
14+
{
15+
public EmscriptenMarshalManagedToNativePrinter(MarshalContext ctx)
16+
: base(ctx)
17+
{
18+
Context.MarshalToNative = this;
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)