Suppose you want to set up VSCode with Emscripten. In my case, I am using devcontainers with Docker and Emscripten's official Docker image (emscripten/emsdk), but any VSCode installation should do.
Run emconfigure env and you should get an output similar to this:
...
EM_CONFIG=/emsdk/.emscripten
CC=/emsdk/upstream/emscripten/emcc
CXX=/emsdk/upstream/emscripten/em++
AR=/emsdk/upstream/emscripten/emar
LD=/emsdk/upstream/emscripten/emcc
NM=/emsdk/upstream/bin/llvm-nm
LDSHARED=/emsdk/upstream/emscripten/emcc
RANLIB=/emsdk/upstream/emscripten/emranlib
EMSCRIPTEN_TOOLS=/emsdk/upstream/emscripten/tools
HOST_CC=/emsdk/upstream/bin/clang
HOST_CXX=/emsdk/upstream/bin/clang++
HOST_CFLAGS=-W
HOST_CXXFLAGS=-W
PKG_CONFIG_LIBDIR=/emsdk/upstream/emscripten/cache/sysroot/local/lib/pkgconfig:/emsdk/upstream/emscripten/cache/sysroot/lib/pkgconfig
PKG_CONFIG_PATH=
EMSCRIPTEN=/emsdk/upstream/emscripten
ACLOCAL_PATH=/emsdk/upstream/emscripten/cache/sysroot/share/aclocal
CROSS_COMPILE=/emsdk/upstream/emscripten/em
EMMAKEN_JUST_CONFIGURE=1The specific environment variables you're looking for is HOST_CC or HOST_CXX. By default, you will be using HOST_CXX even when compiling only C, since DEFAULT_TO_CXX is enabled by default.
The reason for this is we need to set up the Compiler Path for the C/C++ VSCode extension. The thing is, our typical entrypoint emcc won't work since it's set up to only work with MSVC, gcc, or Clang, (See emscripten-core/emscripten#11163, microsoft/vscode-cpptools#4269). You could install a different extension for this (e.g. ms-vscode.cmake-tools) for additional configurations, but there is no official Emscripten VSCode extension unfortunately.
If you run emcc --cflags, you'll see output like this:
❯ emcc --cflags
-target wasm32-unknown-emscripten -fignore-exceptions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/emsdk/upstream/emscripten/cache/sysroot -DEMSCRIPTEN -Xclang -iwithsysroot/include/fakesdl -Xclang -iwithsysroot/include/compatIf you check out the emcc documentation, it says:
--cflags[other] Prints out the flagsemccwould pass toclangto compile source code to object form. You can use this to invokeclangyourself, and then runemccon those outputs just for the final linking+conversion to JS.
Somewhat annoyingly, though, compilerArgs in VSCode must be an array of strings and can't be a string. So, using jq (which comes pre-installed in macOS Sequoia), we can do something like this:
jq --arg cflags "$(emcc --cflags)" '.env.EMSCRIPTEN_CFLAGS = ($cflags | split(" "))' << EOF > .vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "Emscripten",
"compilerPath": "/emsdk/upstream/bin/clang++",
"compilerArgs": ["\${EMSCRIPTEN_CFLAGS}"],
"intelliSenseMode": "linux-clang-x64",
"includePath": ["\${workspaceFolder}/local/include"]
}
],
"version": 4,
"enableConfigurationSquiggles": true
}
EOFwhich will output this JSON to .vscode/c_cpp_properties.json and update the VSCode C/C++ settings:
{
"env": {
"EMSCRIPTEN_CFLAGS": [
"-target",
"wasm32-unknown-emscripten",
"-fignore-exceptions",
"-mllvm",
"-combiner-global-alias-analysis=false",
"-mllvm",
"-enable-emscripten-sjlj",
"-mllvm",
"-disable-lsr",
"--sysroot=/emsdk/upstream/emscripten/cache/sysroot",
"-DEMSCRIPTEN",
"-Xclang",
"-iwithsysroot/include/fakesdl",
"-Xclang",
"-iwithsysroot/include/compat"
]
}
"configurations": [
{
"name": "Emscripten",
"compilerPath": "/emsdk/upstream/bin/clang++",
"compilerArgs": ["${EMSCRIPTEN_CFLAGS}"],
"intelliSenseMode": "linux-clang-x64",
"includePath": ["${workspaceFolder}/local/include"]
}
],
"version": 4,
"enableConfigurationSquiggles": true
}where configurations[0].compilerPath is $HOST_CXX and intelliSenseMode is your OS (windows, linux, macos) -clang-x64 (Interestingly, when I tried -clang-arm64 it kept rejecting it and switching to x64 even on an ARM system).
You would only have to run this command once. There's probably a way to make it always up-to-date using Go templates or using the aforementioned devcontainers, but I can't imagine the Emscripten changes C flags incredibly frequently.
I included an includePath to my ${workspaceFolder}/local directory in case you wanted to do that, but if all your libraries headers' are all in the system include directories (e.g. /usr/include, /usr/local/include), it's not necessary.
That's all. There's probably a couple of different ways to do this such as using CMake (Emscripten includes an Emscripten.cmake file) or just listing out Emscripten's include directories in includePath (e.g. /emsdk/upstream/emscripten/cache/sysroot/include) but IMO this way is probably as close as you'll get to replicating the behavior of emcc.