`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
lib/CMakeLists.txt:246-252 forces the whole static VM into the shared library:
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 directoryThe 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 DLLSo 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
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:
cmake --build build --target hermesvmExpected: 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\binProposed fix (one line each)
Pass a path the linker can open, using the generator expression CMake already provides:
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 atde1526b(2026-09-02). The sha above (5cee10a, one hour later) INCLUDES that fix; this is a different, earlier failure — the shared libraryshermeslinks against does not build at all underlld-link. - #1247 (2024): Windows support "only via Clang", which on Windows means clang-cl and
lld-link. - #2176: the
shermesGNU-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.
Source: facebook/hermes