#2175·hermes

`lib/CMakeLists.txt` passes `/WHOLEARCHIVE:hermesvm_a` as a bare name; `lld-link` cannot open it, so the shared `hermesvm` (and therefore `shermes`) does not link under clang-cl

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

lib/CMakeLists.txt:246-252 forces the whole static VM into the shared library:

cmake
if(APPLE)
  target_link_libraries(hermesvm PRIVATE -force_load hermesvm_a)
  target_link_libraries(hermesvmlean PRIVATE -force_load hermesvmlean_a)
elseif(MSVC)
  target_link_libraries(hermesvm PRIVATE hermesvm_a)
  target_link_options(hermesvm PRIVATE /WHOLEARCHIVE:hermesvm_a)
  target_link_libraries(hermesvmlean PRIVATE hermesvmlean_a)
  target_link_options(hermesvmlean PRIVATE /WHOLEARCHIVE:hermesvmlean_a)

The argument to /WHOLEARCHIVE: at :250 is the bare CMake target name, with no path and no extension. link.exe may resolve that; lld-link — which is what a clang-cl toolchain reaches for — does not:

FAILED: lib/hermesvm.dll lib/hermesvm.lib
lld-link: error: could not open 'hermesvm_a': no such file or directory

The archive itself is built and present; the full link line CMake generates has it twice — once as the bare /WHOLEARCHIVE: argument and once as a real relative path:

lld-link.exe /nologo lib\CMakeFiles\hermesvm.dir\dummy.cpp.obj /out:lib\hermesvm.dll
  /implib:lib\hermesvm.lib /pdb:lib\hermesvm.pdb /dll /version:0.0 /machine:x64
  /INCREMENTAL:NO  /WHOLEARCHIVE:hermesvm_a  lib\hermesvm_a.lib  ...

and only the first spelling is the one that fails. lld-link does not search the library path for a /WHOLEARCHIVE: operand, and does not append .lib to it. Measured by re-running that exact command by hand, twice:

+ /LIBPATH:lib, argument left as the bare name  -> could not open 'hermesvm_a'
  /WHOLEARCHIVE:lib\hermesvm_a.lib              -> links, 4,887,552-byte DLL

So adding a search path is not a workaround, and the path spelling is a complete fix.

Consequence beyond the shared library: tools/shermes links hermesvm, so the AOT compiler's own dependency target (shermes-dep, tools/shermes/CMakeLists.txt) cannot be built, and shermes's default link line — which ends in -lhermesvm — has no library to name. hermesvm_a (static) and every tool that uses it — hermes, hermesc, and a JSI embedder linking hermesvm_a directly — build and run perfectly; it is only the SHARED library that fails. (A shermes-compiled program CAN be linked against the static archives instead, by hand, via the LDFLAGS environment variable. That is a workaround for a downstream consumer, not a reason to leave the bare name.)

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 hermesvm

Expected: links lib/hermesvm.dll. Actual: lld-link: error: could not open 'hermesvm_a': no such file or directory, after lib/hermesvm_a.lib has been built successfully.

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 each)

Pass a path the linker can open, using the generator expression CMake already provides:

cmake
target_link_options(hermesvm PRIVATE "/WHOLEARCHIVE:$<TARGET_FILE:hermesvm_a>")
target_link_options(hermesvmlean PRIVATE "/WHOLEARCHIVE:$<TARGET_FILE:hermesvmlean_a>")

The same applies to /WHOLEARCHIVE:hermesNapi at lib/CMakeLists.txt:271 and :273, which is reached when HERMES_ENABLE_NAPI is on and has not been exercised here.

The comment above the block notes that LINK_LIBRARY generator-expression support was waiting on CMake 3.24; $<TARGET_FILE:...> needs nothing newer than the tree's existing cmake_minimum_required(VERSION 3.21.0).

Related issues

  • #1994 / #2089 fixed shermes's Windows path-list splitting at de1526b (2026-09-02). The sha above (5cee10a, one hour later) INCLUDES that fix; this is a different, earlier failure — the shared library shermes links against does not build at all under lld-link.
  • #1247 (2024): Windows support "only via Clang", which on Windows means clang-cl and lld-link.
  • #2176: the shermes GNU-flags issue that sits downstream of this one.

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). That host links the static hermesvm_a and is unaffected; this issue is what stopped it taking an AOT reading through the default shermes link line. No file under the Hermes checkout was edited, and no workaround for this one was found from outside it — the bare name is not on any search path the linker consults.