#2173·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

Author: altweenCreated Sep 7, 2026Updated Sep 11, 2026

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("$<$<COMPILE_LANGUAGE:CXX>:/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.