`static_h`: the tree cannot be compiled with clang-cl — CMake's `/EHsc` is stripped and `hermes_update_cxx_flags` replaces it with GCC spellings clang-cl ignores
- Dominant language
- JavaScript
- Stars
- 11.3k
- Forks
- 859
- Avg merge
- 1h 30m
- Merged PRs (30d)
- 3
Description
Building `static_h` on Windows with **clang-cl** (the LLVM that ships inside
Visual Studio 2022) fails to compile: every C++ translation unit ends up with
**exceptions disabled**, and the API layer throws.
Two lines interact to produce it. Both were read out of the tree and then
confirmed in the generated `build.ninja`, rather than inferred:
1. **`cmake/modules/Hermes.cmake:261-265`** strips CMake's own default exception
flag under `if (MSVC)`:
```cmake
if (MSVC)
# Remove CMake's default exception handling flags to avoid D9025 warnings
# when we set our own in hermes_update_cxx_flags()
string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/EHsc" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
```
clang-cl **is** `MSVC` as far as CMake is concerned, so the strip happens.
2. **`hermes_update_cxx_flags` (`cmake/modules/Hermes.cmake:68-102`)** is then
expected to set the replacement — but it tests `GCC_COMPATIBLE` *before*
`MSVC`:
```cmake
if (HERMES_ENABLE_EH)
if (GCC_COMPATIBLE)
list(APPEND flags -fexceptions)
elseif (MSVC)
list(APPEND flags /EHsc)
endif ()
else ()
if (GCC_COMPATIBLE)
list(APPEND flags -fno-exceptions)
elseif (MSVC)
list(APPEND flags /EHs-c-)
endif ()
endif ()
```
and `GCC_COMPATIBLE` is set for clang-cl at **`cmake/modules/Hermes.cmake:17-19`**:
```cmake
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CLANG 1)
set(GCC_COMPATIBLE 1)
if (CMAKE_CXX_SIMULATE_ID MATCHES "MSVC")
set(CLANG_CL 1)
endif ()
```
So the GCC spellings are emitted — and **clang-cl ignores them**:
```
clang-cl: warning: unknown argument ignored in clang-cl: '-fno-exceptions' [-Wunknown-argument]
clang-cl: warning: unknown argument ignored in clang-cl: '-fno-rtti' [-Wunknown-argument]
```
Net effect: `/EHsc` removed, replacement ignored, so every TU compiles at
clang-cl's MSVC-compatible default of **exceptions off**, and the build stops at
the first `throw`:
```
jsi.cpp(113,5): error: cannot use 'throw' with exceptions disabled (x11)
FAILED: static-hermes/jsi/CMakeFiles/jsi.dir/jsi.cpp.obj
RuntimeTaskRunner.cpp(56,7): error: cannot use 'throw' with exceptions disabled
extensions/Intrinsics.cpp(76,5) and (99,3): error: cannot use 'try' with exceptions disabled
extensions/JSIUtils.cpp(21,5), (28,5)
FAILED: static-hermes/API/hermes/CMakeFiles/hermesapi_obj.dir/...
```
(Those paths carry a `static-hermes/` prefix because the tree was consumed by
`add_subdirectory` from a downstream project; the file names and the diagnostics
are the tree's own.)
Two further notes that may be useful:
- **The tree already knows it is clang-cl.** `CLANG_CL` is computed at
`cmake/modules/Hermes.cmake:21` from `CMAKE_CXX_SIMULATE_ID`, and
`hermes_update_cxx_flags` simply does not consult it.
- **Hermes v0.13.0 does not have this problem, by accident.** The `/EHsc` strip
at `:261-265` is new on `static_h`; on the release branch nothing removes
CMake's `/EHsc`, so `-fno-exceptions` being ignored leaves the tree built
*with* exceptions, which links and runs.
### Repro
```bat
git clone https://github.com/facebook/hermes.git hermes-static
cd hermes-static
git checkout 5cee10abc93667ea5538caecaf0a457c66fa5bdc
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
set CLANGCL=C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/Llvm/x64/bin/clang-cl.exe
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_C_COMPILER="%CLANGCL%" -DCMAKE_CXX_COMPILER="%CLANGCL%" ^
-DHERMES_ENABLE_TEST_SUITE=OFF -DHERMES_ENABLE_NAPI=OFF
```
(`HERMES_ENABLE_TEST_SUITE=OFF` and `HERMES_ENABLE_NAPI=OFF` only shorten the
build; neither is load-bearing for the failure below.) Then:
```bat
cmake --build build --target jsi
```
Expected: compiles. Actual: eleven `cannot use 'throw' with exceptions disabled`
in `API/jsi/jsi/jsi.cpp`. Building `hermesvm_a` fails the same way a little
later, in `API/hermes`.
Confirming the cause without building: `build/build.ninja` for a VM object
carries `/DWIN32 /D_WINDOWS ...` with the `/EHsc` gone and a double space where
it was; the same file from a v0.13.0 build under the same compiler carries
`/DWIN32 /D_WINDOWS /EHsc ...`.
### Environment
```
Windows 11 Pro 10.0.26200, x86-64
CMake 3.31.6-msvc6 (the one Visual Studio 2022 ships), Ninja 1.13.2
Visual Studio 2022 Community
> clang-cl --version
clang version 19.1.5
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin
```
### Proposed fix (one line)
In `hermes_update_cxx_flags`, test `MSVC` before `GCC_COMPATIBLE` — or branch on
the `CLANG_CL` the tree already computes — so a clang-cl build gets `/EHsc`,
`/EHs-c-`, `/GR` and `/GR-` rather than the `-f...` spellings it ignores.
### Related issues
- #1247 (2024): the maintainers' answer was that Static Hermes would support Windows "only via Clang", and that the tree needs to "figure out how to automatically detect this particular configuration - Clang on Windows with MSVC-compatible CLI". The `CLANG_CL` variable at `cmake/modules/Hermes.cmake:21` is that detection; this issue is about the one function that does not consult it.
- #1627 (open): a different failure under the Visual Studio generator's `-T ClangCL` (the boost-context MASM file); not the same defect.
- #2174: the same compiler-ID mistake in `API/jsi/jsi/CMakeLists.txt`, which `hermes_update_cxx_flags` never reaches.
- The Windows CI job (`.github/workflows/build-hermesc-windows.yml`) builds `hermesc` with the Visual Studio generator and MSVC's `cl`, so a clang-cl configure is not exercised by CI — which is consistent with every failure here being CMake meeting that compiler for the first time.
### Where this came from
Measured while embedding `static_h` as the second runtime of a native game host on Windows (the repository is private at the time of filing, so no links). **No file under the Hermes checkout was edited** — the workaround is a directory-scope `add_compile_options("$<$:/EHsc>")` in the consuming project, before `add_subdirectory`, which is what lets the whole tree compile and is how every number in this report was reached.
Contributor guide
Research direction
Start in cmake/modules/Hermes.cmake, reading hermes_update_cxx_flags and the CLANG_CL detection around lines 17-21 and 68-102. Reproduce with clang-cl using the provided CMake and Ninja commands, then build the jsi target. Done means the generated flags use the appropriate exception and RTTI spellings and the target compiles without the reported errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cmake, cpp
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100