`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
- Dominant language
- JavaScript
- Stars
- 11.3k
- Forks
- 859
- Avg merge
- 1h 30m
- Merged PRs (30d)
- 3
Description
`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_link_options(hermesvmlean PRIVATE "/WHOLEARCHIVE:$")
```
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; `$` 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.
Contributor guide
Research direction
Start in lib/CMakeLists.txt:246-252 and inspect the MSVC linker-options block, then review the related hermesNapi options at lines 271 and 273. Configure the project with clang-cl and Ninja using the reproduction commands, update the whole-archive operands to use target-file paths, and verify that hermesvm and the NAPI-enabled configuration link successfully.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cmake
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100