Skip to content

Instantly share code, notes, and snippets.

@WesThorburn
Last active September 11, 2024 07:17
Show Gist options
  • Save WesThorburn/00c47b267a0e8c8431e06b14997778e4 to your computer and use it in GitHub Desktop.
Save WesThorburn/00c47b267a0e8c8431e06b14997778e4 to your computer and use it in GitHub Desktop.
Linux: Compile C++ to WebAssembly and JavaScript using Emscripten and CMake

Linux: Compile C++ to WebAssembly and JavaScript using Emscripten and CMake

Download and Install Emscripten

  • My preferred installation location is /home/user
  • Get the latest sdk: git clone https://github.com/emscripten-core/emsdk.git
  • Enter the cloned directory: cd emsdk
  • Checkout main: git checkout main
  • Install the lastest sdk tools: ./emsdk install latest
  • Activate the latest sdk tools: ./emsdk activate latest
  • Activate path variables: source ./emsdk_env.sh
  • Configure emsdk in your bash profile by running: echo 'source "/home/user/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile

Your project structure

build/  
include/  
src/  
public/  
CMakeLists.txt  
  • Build directory starts empty, place header (.h) files inside /include and source (.cpp) files inside /src.

  • The public folder is where the output files will go, also keep the index.html, images and css here too.

  • CMakeLists.txt stays the root directory.

  • In the root directory, set the CMAKE Toolchain file by running: CMAKE_TOOLCHAIN_FILE=/home/user/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake Ensure you update your path with the location of your emsdk installation location

Compile to WASM (default)

cd build  
emcmake cmake ..  
make

Compile to JS

cd build  
emcmake cmake .. -DJS_ONLY=ON  
make

After Editing Files

make

After Adding/Removing Files

emcmake cmake ..  
make

If the emsdk directory is moved

  • From the project root directory run: CMAKE_TOOLCHAIN_FILE=[/path/to]/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake

  • Then from the /build directory, run: emcmake cmake ..

  • Output .js, .wasm.js and wasm.wasm files will appear in the public directory.

  • Make sure you web server serves from /public.

  • The index.html will load a.js if WebAssembly isn't supported by the client browser.

If the error: fatal error: emscripten.h: No such file or directory or The program 'emcmake' is currently not installed or The program 'emconfigure' is currently not installed is seen, check that your CMAKE_TOOLCHAIN_FILE path is correct as this is often the cause. You could also try re-installing by removing the /build directory, and run through emscripten installation again, making sure you run: source ./emsdk_env.sh at end. Then recreate the /build directory and run emcmake cmake .. and make again.

project (client)
cmake_minimum_required(VERSION 3.5.1)
option(JS_ONLY "Compiles to native JS (No WASM)" OFF)
add_definitions(-std=c++11 -O3)
include_directories(include)
file(GLOB SOURCES src/*.cpp)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/public")
add_executable(a ${SOURCES})
if(JS_ONLY)
message(STATUS "Setting compilation target to native JavaScript")
set(CMAKE_EXECUTABLE_SUFFIX ".js")
set_target_properties(a PROPERTIES LINK_FLAGS "-s WASM=0 -s EXPORTED_FUNCTIONS='[_main]'")
else(JS_ONLY)
message(STATUS "Setting compilation target to WASM")
set(CMAKE_EXECUTABLE_SUFFIX ".wasm.js")
set_target_properties(a PROPERTIES LINK_FLAGS "-s WASM=1 -s EXPORTED_FUNCTIONS='[_main]'")
endif(JS_ONLY)
<!DOCTYPE html>
<html>
<head></head>
<body>
<script>
!function(e, t){
console.log("Loading client...");
var n = "a.wasm.js";
if(!e.WebAssembly){
n = "a.js"
}
console.log("Script set to " + n);
var o = t.createElement("script");
o.async = !0, o.type = "text/javascript", o.src = n, o.onerror = function(t) {
console.error("Script Error"), console.error(t), setTimeout(function() {
e.location.reload(!0)
}, 3e3)
};
var r = t.getElementsByTagName("script")[0];
r.parentNode.insertBefore(o, r)
}(window, document);
</script>
</body>
</html>
@IvaOsi
Copy link

IvaOsi commented Jul 16, 2019

Hey!
I have a problem with write on console emconfigure cmake

So i put in console this (in buld dir)
emconfigure cmake

and i see this:

shared:ERROR: Error running configure: "cmake -DCMAKE_CROSSCOMPILING_EMULATOR="C:/cpp/emsdk/node/8.9.1_64bit/bin/node.exe" -DCMAKE_TOOLCHAIN_FILE=C:\cpp\emsdk\upstream
\emscripten\cmake\Modules\Platform\Emscripten.cmake"
Traceback (most recent call last):
  File "C:\cpp\emsdk\upstream\emscripten\emconfigure.py", line 59, in <module>
    run()
  File "C:\cpp\emsdk\upstream\emscripten\emconfigure.py", line 53, in run
    shared.Building.configure(sys.argv[1:])
  File "C:\cpp\emsdk\upstream\emscripten\tools\shared.py", line 1677, in configure
    res = run_process(args, check=False, stdout=stdout, stderr=stderr, env=env)
  File "C:\cpp\emsdk\upstream\emscripten\tools\shared.py", line 177, in run_process
    proc = Popen(cmd, *args, **kw)
  File "C:\cpp\emsdk\python\2.7.13.1_64bit\python-2.7.13.amd64\lib\subprocess.py", line 390, in __init__
    errread, errwrite)
  File "C:\cpp\emsdk\python\2.7.13.1_64bit\python-2.7.13.amd64\lib\subprocess.py", line 640, in _execute_child
    startupinfo)
WindowsError: [Error 2]

@WesThorburn
Copy link
Author

I suspect your path doesn't include cmake. Try running this from the terminal:

where emcmake
where cmake

If either of those commands don't print anything to the terminal, or they give an error, then you'll need to add them to your path manually.

I've updated the Gist title to specify this Gist contains instructions for Linux which will hopefully stop Googlers from landing here looking for Windows-specific instructions.

@scimad
Copy link

scimad commented Dec 4, 2020

Does this generalize to building large libraries like OpenCV and similar?

@WesThorburn
Copy link
Author

WesThorburn commented Dec 5, 2020

Does this generalize to building large libraries like OpenCV and similar?

No, only the system libraries that are included with Emscripten are automatically linked when you compile (just the necessary parts). This includes libc, libc++ (C++ standard library) and SDL.

Libraries not included with Emscripten like OpenCV and similar must be compiled and linked with the program just as if they were a module in the project.

https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-link-against-system-libraries-like-sdl-boost-etc

@scimad
Copy link

scimad commented Dec 5, 2020

Thanks a lot @WesThorburn.

@aykejriw
Copy link

[ 5%] Linking CXX executable ../output/pimWasm.js
em++: error: no input files
CMakeFiles/pimWasm.dir/build.make:373: recipe for target '../output/pimWasm.js' failed
make[2]: *** [../output/pimWasm.js] Error 1
CMakeFiles/Makefile2:82: recipe for target 'CMakeFiles/pimWasm.dir/all' failed
make[1]: *** [CMakeFiles/pimWasm.dir/all] Error 2
Makefile:90: recipe for target 'all' failed
make: *** [all] Error 2

I am getting above error. Can someone please guide here?

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