facebook / facebook/hermes

`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

Open Beginner friendly
#2,173 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.