Skip to content

Commit d410f2d

Browse files
WASM bindings in ACT (through emscripten) (#225)
* Generates entirety of bindings from Python version (Uses ACT structure). However, some important patches need to be moved. So binding is not functional * Bindings have been tested. They are in the same state as the ones created through Python * Update static wrapper and out param wrapper to wrap more functions * classParam is also properly detected and wrapped now. Implicit example seems to work too * Some changes and possible bugs fixed (while implementing the RTTI example) * An automated build script that can build the base library and generate WASM bindings automatically (with cleanup) * Push RTTI bindings + Updated XML * Its safer to use NameSpace instead of LibraryName as Library name can be any random string * Fix file name * Uncommented check from build.sh * Fix transposed matrices * Working version of WASM build and run script * Allow example generation from WASM directly * Delete the monolith WASM build + example generator * Add relevant entries in build scripts * Update dockerfile to also include emcc + basic changes * Upgrade python version (emscripten uses walrus operator) * Change default python version * WASM needs 1 more library in docker image --------- Co-authored-by: gangatp <[email protected]>
1 parent d3631a8 commit d410f2d

File tree

11 files changed

+1302
-244
lines changed

11 files changed

+1302
-244
lines changed

Build/Dockerfile

Lines changed: 52 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,81 +7,70 @@ LABEL maintainer="Andrii Anpilogov ([email protected])"
77

88
RUN [ -e /etc/yum.conf ] && sed -i '/tsflags=nodocs/d' /etc/yum.conf || true
99

10-
# RUN yum install -y icu
10+
# Vault repos for CentOS 8
11+
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* && \
12+
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* && \
13+
yum update -y
1114

12-
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
13-
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
14-
RUN yum update -y
15+
RUN dnf -y install dnf-plugins-core dnf-plugin-config-manager && dnf -y update
16+
RUN dnf -y install epel-release && dnf -y config-manager --set-enabled powertools
17+
RUN dnf -y install libatomic
1518

16-
RUN dnf -y install dnf-plugins-core
17-
RUN dnf -y install dnf-plugin-config-manager
18-
RUN dnf -y update
19+
# Tooling
20+
RUN dnf -y install gcc-toolset-9-gcc gcc-toolset-9-gcc-c++ cmake ninja-build git curl
1921

20-
RUN dnf -y install epel-release
21-
RUN dnf -y config-manager --set-enabled powertools
22+
# ---- Python 3.9 (required by recent emscripten) ----
23+
RUN dnf -y module enable python39 && \
24+
dnf -y install python39 python39-pip && \
25+
alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 2 && \
26+
alternatives --set python3 /usr/bin/python3.9
2227

23-
# GCC
24-
RUN dnf -y install gcc-toolset-9-gcc
25-
RUN dnf -y install gcc-toolset-9-gcc-c++
28+
# (optional) ICU runtime/headers if you need them
29+
# RUN dnf -y install libicu libicu-devel
2630

27-
# CMake & Ninja
28-
RUN dnf -y install cmake
29-
RUN dnf -y install ninja-build
30-
31-
# Free Pascal
32-
# 3.0.4 should be used for validating compiler base compatibility.
31+
# Free Pascal 3.0.4
3332
RUN (cd /opt && curl -O -L 'http://downloads.sourceforge.net/project/freepascal/Linux/3.0.4/fpc-3.0.4-1.x86_64.rpm' \
3433
&& rpm -i fpc-3.0.4-1.x86_64.rpm)
3534

36-
# Using 3.2.2 until PolymorphicFactory is reworked to not using generics that are not full functional in 3.0.4.
37-
# RUN (cd /opt && curl -O -L 'http://downloads.sourceforge.net/project/freepascal/Linux/3.2.2/fpc-3.2.2-1.x86_64.rpm' \
38-
# && rpm -i fpc-3.2.2-1.x86_64.rpm)
39-
40-
# Golang
41-
RUN (cd /opt && curl -O -L https://golang.org/dl/go1.17.2.linux-amd64.tar.gz \
42-
&& tar zxvf go1.17.2.linux-amd64.tar.gz)
43-
44-
# Java
45-
RUN dnf -y install \
46-
java-11-openjdk-devel
35+
# Go
36+
RUN (cd /opt && curl -O -L https://golang.org/dl/go1.17.2.linux-amd64.tar.gz && tar zxvf go1.17.2.linux-amd64.tar.gz)
37+
ENV PATH="${PATH}:/opt/go/bin"
4738

48-
# Mono
39+
# Java & Mono
40+
RUN dnf -y install java-11-openjdk-devel
4941
RUN rpm --import https://download.mono-project.com/repo/xamarin.gpg \
5042
&& dnf config-manager --add-repo https://download.mono-project.com/repo/centos8-stable.repo \
5143
&& dnf -y install mono-complete
5244

53-
# General purpose tools
54-
RUN dnf -y install \
55-
glibc-common \
56-
glibc-utils \
57-
less \
58-
passwd \
59-
tar \
60-
vim-minimal \
61-
vim-enhanced \
62-
which \
63-
sudo \
64-
bash-completion \
65-
mc \
66-
yum-utils \
67-
&& yum clean all
68-
69-
# # Initialize Toolkit
70-
RUN echo "source /opt/rh/gcc-toolset-9/enable" >> /etc/bashrc
71-
ENV PATH="${PATH}:/opt/go/bin"
72-
73-
# Initialize Toolkit
74-
# RUN : "${USER_ID:?Build argument needs to be set and non-empty.}" \
75-
# : "${GROUP_ID:?Build argument needs to be set and non-empty.}"
45+
# General tools
46+
RUN dnf -y install glibc-common glibc-utils less passwd tar vim-minimal vim-enhanced which sudo bash-completion mc yum-utils && yum clean all
7647

77-
# Create user
78-
RUN if [ $USER_ID != "" ] ; then groupadd docker \
79-
&& useradd -ms /bin/bash --uid $USER_ID -g docker -G wheel user \
80-
&& printf "user:user" | chpasswd \
81-
&& printf "user ALL= NOPASSWD: ALL\\n" >> /etc/sudoers ; fi
82-
83-
# Initialize Toolkit
84-
RUN if [ $USER_ID != "" ] ; then echo "source /opt/rh/gcc-toolset-9/enable" >> /home/user/.bash_profile ; fi
85-
RUN if [ $USER_ID != "" ] ; then echo "export PATH=\$PATH:/opt/go/bin" >> /home/user/.bash_profile ; fi
48+
# Enable GCC toolset in shells
49+
RUN echo "source /opt/rh/gcc-toolset-9/enable" >> /etc/bashrc
8650

87-
ENTRYPOINT ["./entrypoint.sh"]
51+
# ---- Emscripten SDK (emsdk) ----
52+
ENV EMSDK=/opt/emsdk
53+
RUN git clone https://github.com/emscripten-core/emsdk.git ${EMSDK} && \
54+
bash -lc "cd ${EMSDK} && ./emsdk install latest && ./emsdk activate latest"
55+
56+
# Source emsdk env in interactive shells
57+
RUN echo "source ${EMSDK}/emsdk_env.sh" >> /etc/bashrc
58+
59+
# Create user (if requested)
60+
RUN if [ -n "$USER_ID" ]; then \
61+
groupadd -g ${GROUP_ID:-1000} docker && \
62+
useradd -ms /bin/bash --uid $USER_ID -g docker -G wheel user && \
63+
printf 'user:user' | chpasswd && \
64+
printf 'user ALL= NOPASSWD: ALL\n' >> /etc/sudoers && \
65+
echo 'source /opt/rh/gcc-toolset-9/enable' >> /home/user/.bash_profile && \
66+
echo "export EMSDK_PYTHON=${EMSDK_PYTHON}" >> /home/user/.bash_profile && \
67+
echo "source ${EMSDK}/emsdk_env.sh" >> /home/user/.bash_profile ; \
68+
fi
69+
70+
# Sanity check (now python3 == 3.9; don't rely on EMSDK_PYTHON which emsdk_env clears)
71+
RUN bash -lc "source ${EMSDK}/emsdk_env.sh && \
72+
python3 --version && emcc --version && em++ --version && node --version && \
73+
/opt/emsdk/upstream/bin/wasm-opt --version"
74+
75+
# Your repo should provide this script; it will source the env before running any command.
76+
ENTRYPOINT ["./entrypoint.sh"]

Build/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ startingpath="$(pwd)"
66
basepath="$(cd "$(dirname "$0")" && pwd)"
77
cd "$basepath/../Source"
88

9-
Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go"
9+
Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingwasm.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go"
1010

1111
echo "Build act.win64.exe"
1212
export GOARCH="amd64"

Examples/RTTI/RTTI.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<binding language="CSharp" indentation="tabs" />
1717
<binding language="Go" indentation="tabs" />
1818
<binding language="Java" indentation="tabs" />
19+
<binding language="WASM" indentation="tabs" />
1920
</bindings>
2021

2122
<implementations>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*++
2+
3+
Copyright (C) 2021 ADSK
4+
5+
All rights reserved.
6+
7+
This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop.
8+
9+
Abstract: C++ Emscripten wrapper for WebAssembly
10+
11+
Interface version: 1.0.0
12+
13+
*/
14+
15+
#include <iostream>
16+
#include <vector>
17+
#include <string>
18+
#include <emscripten.h>
19+
#include <emscripten/bind.h>
20+
#include "Cpp/rtti_implicit.hpp"
21+
22+
using namespace emscripten;
23+
using namespace RTTI;
24+
25+
static void wrap_Wrapper_ReleaseInstance(CWrapper &self, PBase& Instance) {
26+
self.ReleaseInstance(classParam(Instance));
27+
}
28+
29+
static void wrap_Wrapper_AcquireInstance(CWrapper &self, PBase& Instance) {
30+
self.AcquireInstance(classParam(Instance));
31+
}
32+
33+
static emscripten::val wrap_AnimalIterator_GetNextOptinalAnimal(CAnimalIterator &self) {
34+
emscripten::val output = emscripten::val::object();
35+
PAnimal Animal;
36+
bool return_value = self.GetNextOptinalAnimal(Animal);
37+
output.set("return", return_value);
38+
output.set("Animal", Animal);
39+
return output;
40+
}
41+
42+
static emscripten::val wrap_AnimalIterator_GetNextMandatoryAnimal(CAnimalIterator &self) {
43+
emscripten::val output = emscripten::val::object();
44+
PAnimal Animal;
45+
bool return_value = self.GetNextMandatoryAnimal(Animal);
46+
output.set("return", return_value);
47+
output.set("Animal", Animal);
48+
return output;
49+
}
50+
51+
static emscripten::val wrap_Wrapper_GetVersion(CWrapper &self) {
52+
emscripten::val output = emscripten::val::object();
53+
RTTI_uint32 Major;
54+
RTTI_uint32 Minor;
55+
RTTI_uint32 Micro;
56+
self.GetVersion(Major, Minor, Micro);
57+
output.set("Major", Major);
58+
output.set("Minor", Minor);
59+
output.set("Micro", Micro);
60+
return output;
61+
}
62+
63+
static emscripten::val wrap_Wrapper_GetLastError(CWrapper &self, PBase& Instance) {
64+
emscripten::val output = emscripten::val::object();
65+
std::string ErrorMessage;
66+
bool return_value = self.GetLastError(classParam(Instance), ErrorMessage);
67+
output.set("return", return_value);
68+
output.set("ErrorMessage", ErrorMessage);
69+
return output;
70+
}
71+
72+
// ================== Emscripten Bindings ==================
73+
EMSCRIPTEN_BINDINGS(RTTI) {
74+
// Binding Methods
75+
class_<CBase>("CBase")
76+
.smart_ptr<std::shared_ptr<CBase>>("shared_ptr<CBase>")
77+
.function("ClassTypeId", &CBase::ClassTypeId)
78+
;
79+
class_<CAnimal, base<CBase>>("CAnimal")
80+
.smart_ptr<std::shared_ptr<CAnimal>>("shared_ptr<CAnimal>")
81+
.function("Name", &CAnimal::Name)
82+
;
83+
class_<CMammal, base<CAnimal>>("CMammal")
84+
.smart_ptr<std::shared_ptr<CMammal>>("shared_ptr<CMammal>")
85+
;
86+
class_<CReptile, base<CAnimal>>("CReptile")
87+
.smart_ptr<std::shared_ptr<CReptile>>("shared_ptr<CReptile>")
88+
;
89+
class_<CGiraffe, base<CMammal>>("CGiraffe")
90+
.smart_ptr<std::shared_ptr<CGiraffe>>("shared_ptr<CGiraffe>")
91+
;
92+
class_<CTiger, base<CMammal>>("CTiger")
93+
.smart_ptr<std::shared_ptr<CTiger>>("shared_ptr<CTiger>")
94+
.function("Roar", &CTiger::Roar)
95+
;
96+
class_<CSnake, base<CReptile>>("CSnake")
97+
.smart_ptr<std::shared_ptr<CSnake>>("shared_ptr<CSnake>")
98+
;
99+
class_<CTurtle, base<CReptile>>("CTurtle")
100+
.smart_ptr<std::shared_ptr<CTurtle>>("shared_ptr<CTurtle>")
101+
;
102+
class_<CAnimalIterator>("CAnimalIterator")
103+
.smart_ptr<std::shared_ptr<CAnimalIterator>>("shared_ptr<CAnimalIterator>")
104+
.function("GetNextAnimal", &CAnimalIterator::GetNextAnimal)
105+
.function("GetNextOptinalAnimal", &wrap_AnimalIterator_GetNextOptinalAnimal)
106+
.function("GetNextMandatoryAnimal", &wrap_AnimalIterator_GetNextMandatoryAnimal)
107+
;
108+
class_<CZoo>("CZoo")
109+
.smart_ptr<std::shared_ptr<CZoo>>("shared_ptr<CZoo>")
110+
.function("Iterator", &CZoo::Iterator)
111+
;
112+
// CWrapper global bindings
113+
class_<CWrapper>("CWrapper")
114+
.constructor<>()
115+
.function("GetVersion", &wrap_Wrapper_GetVersion)
116+
.function("GetLastError", &wrap_Wrapper_GetLastError)
117+
.function("ReleaseInstance", &wrap_Wrapper_ReleaseInstance)
118+
.function("AcquireInstance", &wrap_Wrapper_AcquireInstance)
119+
// .function("InjectComponent", &CWrapper::InjectComponent) // Explicitly skipped (Returns a void pointer)
120+
// .function("GetSymbolLookupMethod", &CWrapper::GetSymbolLookupMethod) // Explicitly skipped (Returns a void pointer)
121+
.function("CreateZoo", &CWrapper::CreateZoo)
122+
;
123+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*++
2+
3+
Copyright (C) 2021 ADSK
4+
5+
All rights reserved.
6+
7+
This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop.
8+
9+
Abstract: This is an autogenerated Node.js application that demonstrates the
10+
usage of the WebAssembly bindings of RTTI
11+
12+
Interface version: 1.0.0
13+
14+
*/
15+
16+
import initModule from './rttiBinding.js';
17+
18+
function assert(cond, msg) {
19+
if (!cond) throw new Error(msg || 'Assertion failed');
20+
}
21+
22+
try {
23+
console.log('Running RTTI WASM Example…');
24+
25+
const mod = await initModule(); // MODULARIZE + EXPORT_ES6
26+
console.log('✅ module initialized');
27+
28+
const wrapper = new mod.CWrapper();
29+
const { Major, Minor, Micro } = wrapper.GetVersion();
30+
console.log(`RTTI version: ${Major}.${Minor}.${Micro}`);
31+
32+
const zoo = wrapper.CreateZoo();
33+
assert(zoo, 'Failed to create Zoo');
34+
35+
const iter = zoo.Iterator();
36+
assert(iter, 'Failed to create Iterator');
37+
38+
let animal;
39+
let animalCount = 0;
40+
41+
while ((animal = iter.GetNextAnimal())) {
42+
animalCount++;
43+
const name = animal.Name();
44+
console.log(`Processing animal #${animalCount}: ${name}`);
45+
46+
if (name === 'Gerald Giraffe' || name === 'Gary Giraffe') {
47+
assert(animal instanceof mod.CGiraffe, `Expected ${name} to be a Giraffe`);
48+
} else if (name === 'Timmy Tiger' || name === 'Tony Tiger' || name === 'Travis Tiger') {
49+
assert(animal instanceof mod.CTiger, `Expected ${name} to be a Tiger`);
50+
animal.Roar();
51+
} else if (name === 'Sebastian Snake' || name === 'Slytherin Snake') {
52+
assert(animal instanceof mod.CSnake, `Expected ${name} to be a Snake`);
53+
} else if (name.includes('Turtle')) {
54+
assert(animal instanceof mod.CTurtle, `Expected ${name} to be a Turtle`);
55+
} else {
56+
console.warn(`Unknown animal type for ${name}`);
57+
}
58+
59+
animal.delete(); // free embind wrapper
60+
}
61+
62+
// Iterator should be exhausted now
63+
const noMore = iter.GetNextAnimal();
64+
assert(!noMore, 'Iterator should be exhausted after the loop');
65+
if (noMore) noMore.delete();
66+
67+
console.log(`\n✅ Successfully processed ${animalCount} animals.`);
68+
69+
iter.delete();
70+
zoo.delete();
71+
wrapper.delete();
72+
} catch (err) {
73+
console.error('Error:', err);
74+
process.exit(1);
75+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*++
2+
3+
Copyright (C) 2021 ADSK
4+
5+
All rights reserved.
6+
7+
This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop.
8+
9+
Abstract: This is an autogenerated Node.js application that demonstrates the
10+
usage of the WebAssembly bindings of RTTI
11+
12+
Interface version: 1.0.0
13+
14+
*/
15+
16+
// Autogenerated minimal ES module example for RTTI WASM
17+
// Run with: node example.mjs
18+
19+
import initModule from './rttiBinding.js';
20+
21+
console.log('Running RTTI WASM Example...');
22+
23+
const mod = await initModule(); // MODULARIZE + EXPORT_ES6 default export
24+
console.log('✅ RTTI WASM module initialized');
25+
26+
const wrapper = new mod.CWrapper();
27+
const { Major, Minor, Micro } = wrapper.GetVersion();
28+
console.log('RTTI version: ' + Major + '.' + Minor + '.' + Micro);
29+
30+
// TODO: add component-specific demo calls here

0 commit comments

Comments
 (0)