Skip to content

Instantly share code, notes, and snippets.

@igormcoelho
Last active May 28, 2020 21:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igormcoelho/5d7e1ba721c0f1e4ec9eec07b6c97d41 to your computer and use it in GitHub Desktop.
Save igormcoelho/5d7e1ba721c0f1e4ec9eec07b6c97d41 to your computer and use it in GitHub Desktop.
Bundle (webkit) C++ on emscripten (1.39.4) with external node.js dependency
#!/bin/bash
docker run --rm -v $(pwd):/src trzeci/emscripten em++ --pre-js prefix-node-require.js --std=c++17 -g -O3 -Wall -s WASM=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "UTF8ToString", "stringToUTF8"]' -s DISABLE_EXCEPTION_CATCHING=1 -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s 'EXPORT_NAME="ourtest"' --js-library general-exports.js -I./thirdparty/my-cpp-library/ -o ourtest.js mycode.cpp
mergeInto(
LibraryManager.library, {
// 'strInput' is a null terminated char array
// 'ptr_out' is a 'unsigned char*' array of size 'sz_out' (it's pre-allocated by its user in C)
external_sha256: function (strInput, ptr_out, sz_out) {
// gets external js library
let myCryptoJS = Module['CryptoJS'];
// converts 'char*' to 'js string'
var hexv1 = Module.UTF8ToString(strInput);
// invokes external library
var hexEnc1 = myCryptoJS.enc.Hex.parse(hexv1);
// use external desired function (SHA256)
var outEnc1 = myCryptoJS.SHA256(hexEnc1);
// prepares output to put on 'byte*' array
var hex_out = myCryptoJS.enc.Hex.stringify(outEnc1);
const fromHexString = hexString =>
new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
var vout = fromHexString(hex_out);
// copy output into C memory
Module.HEAPU8.set(vout, ptr_out); // could check 'sz_out' for better safety
// returns used length
return vout.length;
}
}
);
<!doctype html>
<script src="./dist/bundle.js"></script>
<script>
myLib.myOnInit(
function () {
console.log(myLib.add_10(50)); // result is 60
}
);
</script>
import ourtest from './ourtest.js';
import ourtestWasm from './ourtest.wasm';
// 'index.html' could access 'myLib.mymodule'
export const mymodule = ourtest({
locateFile(path) {
if(path.endsWith('.wasm')) {
return ourtestWasm;
}
return path;
}
});
// 'index.html' could access 'myLib.myadd_10'
export const myadd_10 = mymodule._add_10;
// 'index.html' could access 'myLib.c_myfunction'
export const c_myfunction = mymodule.cwrap('c_myfunction', 'string', ['string']);
// initialization callback
let myFunc = function(){console.log("initialized wasm");};
export function myOnInit(f){
myFunc = f;
};
mymodule.onRuntimeInitialized = () => {
console.log(mymodule._add_10(20)); // outputs 30
myFunc();
};
// 'index.html' could access 'myLib.CryptoJS'
export const CryptoJS = require('crypto-js');
#include <my-cpp-project/cpp-bindings.hpp>
// C++ function: std::string myCppFunction(std::string s) { /* c++ implementation */ }
// C function (for JS): extern "C" int external_sha256(const char* input_hexstring, unsigned char* output_bytes, int sz_output);
EMSCRIPTEN_KEEPALIVE
extern "C"
const char* c_myfunction(const char* cs_in) {
std::string sin(cs_in);
std::string s_out = myCppFunction(sin);
return s_out.c_str(); // is it really safe?
}
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
extern "C"
int add_10(int n) {
return n+10;
}
int
main()
{
return 0;
}
{
"name": "lol",
"scripts": {
"build:codec": "./compile.sh",
"build:bundle": "webpack",
"build": "npm run build:codec && npm run build:bundle",
"serve": "http-server",
"start": "npm run build && npm run serve"
},
"devDependencies": {
"exports-loader": "^0.7.0",
"file-loader": "^1.1.11",
"http-server": "^0.11.1",
"webpack": "^4.8.2",
"webpack-cli": "^3.1.2"
},
"dependencies": {
"crypto-js": "",
}
}
// this is only valid for node.js (but after bundle process, it will be usable on web too)
let lt_cryptojs = require('crypto-js');
Module["CryptoJS"] = lt_cryptojs;
// based on: https://gist.github.com/surma/b2705b6cca29357ebea1c9e6e15684cc
const webpack = require("webpack");
const path = require("path");
module.exports = {
mode: "development",
context: path.resolve(__dirname, "."),
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
library: 'myLib',
libraryTarget: 'umd'
},
node: {
fs: "empty"
},
module: {
rules: [
{
test: /ourtest\.js$/,
loader: "exports-loader"
},
{
test: /ourtest\.wasm$/,
type: "javascript/auto",
loader: "file-loader",
options: {
publicPath: "dist/"
}
}
]
},
};
@vncoelho
Copy link

Nice example, brother! This may be useful to someone else and save a couple of hours.

Can you remove the , from:

  "dependencies": {
    "crypto-js": "",
  }

File #include <my-cpp-project/cpp-bindings.hpp> is missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment