Skip to content

experiment: add Webassembly support #727

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

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ compile_commands.json
/smoke-test-*
/*.tgz
/build-artifacts
.parcel-cache/
dist/
*.wasm
57 changes: 54 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,42 @@ if(${ZMQ_ENABLE_SANITIZER_UNDEFINED})
set(ENABLE_SANITIZER_UNDEFINED "ENABLE_SANITIZER_UNDEFINED")
endif()

option(ZMQ_WASM "Build for WebAssembly" OFF)
set_option_from_env(ZMQ_WASM)

if(ZMQ_WASM OR CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set(ZMQ_WASM TRUE)

# Compile for WebAssembly
set(VCPKG_HOST_TRIPLET wasm32-emscripten)
set(VCPKG_TARGET_TRIPLET wasm32-emscripten)

if ("$ENV{EMSCRIPTEN_ROOT}" STREQUAL "")
find_program(EMCC emcc REQUIRED)
get_filename_component(EMSCRIPTEN_ROOT "${EMCC}" DIRECTORY)
else()
set(EMSCRIPTEN_ROOT "$ENV{EMSCRIPTEN_ROOT}")
endif()

include(${EMSCRIPTEN_ROOT}/cmake/Modules/Platform/Emscripten.cmake)

set(CMAKE_LINKER "${EMSCRIPTEN_ROOT}/bin/lld${CMAKE_EXECUTABLE_SUFFIX}" CACHE STRING "Linker" FORCE)

add_compile_definitions("-D__wasm__" "-D__EMSCRIPTEN__")

# export an indirect function table for napi-wasm
add_link_options("--export-table")

# Add WebAssembly-specific exports for napi-wasm
add_link_options("SHELL:-s EXPORTED_FUNCTIONS=['_malloc','_free','_napi_register_wasm_v1']")
add_link_options("SHELL:-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap']")
add_link_options("SHELL:-s ALLOW_MEMORY_GROWTH=1")
add_link_options("SHELL:-s ENVIRONMENT='node'")
add_link_options("SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=0")
add_link_options("SHELL:-s DISABLE_EXCEPTION_CATCHING=0")
add_link_options("--no-entry")
endif()

# Set MacOS deployment target
if(APPLE)
option(MACOSX_DEPLOYMENT_TARGET "MacOS deployment target" "10.15")
Expand Down Expand Up @@ -124,10 +160,17 @@ project_options(
ENABLE_CACHE
ENABLE_COMPILE_COMMANDS_SYMLINK
${ENABLE_SANITIZER_UNDEFINED}
ENABLE_INTERPROCEDURAL_OPTIMIZATION
)

file(GLOB_RECURSE SOURCES "./src/*.cc")
add_library(addon SHARED ${SOURCES})
file(GLOB_RECURSE SOURCES "./src/*.cc" "./src/*.cpp")

if(ZMQ_WASM)
# For WebAssembly, create an executable instead of a shared library
add_executable(addon ${SOURCES})
else()
add_library(addon SHARED ${SOURCES})
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL GNU
OR CMAKE_CXX_COMPILER_ID STREQUAL Clang
Expand All @@ -145,6 +188,10 @@ if(ZMQ_NO_SYNC_RESOLVE)
target_compile_definitions(addon PRIVATE ZMQ_NO_SYNC_RESOLVE)
endif()

if(ZMQ_WASM)
target_compile_definitions(addon PRIVATE ZMQ_WASM)
endif()

# ZeroMQ
find_package(ZeroMQ CONFIG REQUIRED)
target_link_system_libraries(addon PRIVATE libzmq libzmq-static)
Expand All @@ -168,7 +215,11 @@ if(WIN32)
endif()

# Use `.node` for the library without any "lib" prefix
set_target_properties(addon PROPERTIES PREFIX "" SUFFIX ".node")
if(ZMQ_WASM)
set_target_properties(addon PROPERTIES PREFIX "" SUFFIX ".wasm")
else()
set_target_properties(addon PROPERTIES PREFIX "" SUFFIX ".node")
endif()

# Windows delay load node.exe
if(WIN32)
Expand Down
7 changes: 7 additions & 0 deletions examples/wasm/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<title>ZeroMQ</title>
<body>
<script type="module" src="./index.mjs"></script>
</body>
</html>
40 changes: 40 additions & 0 deletions examples/wasm/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {Environment, napi} from "napi-wasm"

async function getWasm() {
if (typeof window === "undefined") {
// Nodejs
const fs = await import("fs/promises")
const path = await import("path")
const url = await import("url")

const __dirname = path.dirname(url.fileURLToPath(import.meta.url))

return (await fs.readFile(path.join(__dirname, "addon.wasm")))
}

// Browser
const response = await fetch("./addon.wasm")
if (!response.ok) {
throw new Error(`Failed to fetch wasm: ${response.statusText}`)
}
return await response.arrayBuffer()
}

async function main() {
const wasm = await getWasm()

const {instance} = await WebAssembly.instantiate(wasm, {
napi: napi,
env: {} // The env imports will be provided by napi-wasm
})

// Create an environment.
let env = new Environment(instance)
let exports = env.exports

console.log(exports)
}

main().catch(err => {
throw err
})
15 changes: 15 additions & 0 deletions examples/wasm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "browser",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "parcel serve index.html"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "[email protected]",
"devDependencies": {
"parcel": "^2.15.4"
}
}
Loading
Loading