facebook / facebook/hermes

`shermes` emits GNU-style compile and link flags unconditionally, so its own default `SHERMES_CC` (= `CMAKE_C_COMPILER` = clang-cl) cannot link what it compiles on Windows

Open
#2,176 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

`shermes` compiles JavaScript to C and then invokes a C compiler to build and
link the result. That compiler defaults to CMake's C compiler —
`tools/shermes/CMakeLists.txt:18`:

```cmake
set(SHERMES_CC "${CMAKE_C_COMPILER}" CACHE STRING "C compiler to invoke")
```

so on a clang-cl configure, `SHERMES_CC` is **clang-cl**. But the command line
`shermes` builds for it is GNU-driver-shaped unconditionally
(`tools/shermes/compile.cpp`, `buildCCArgs`): `-fPIC` at `:225`,
`-fno-strict-overflow` at `:270`, `-L` at `:282` and `:289`, `-l` at
`:284`, `-lshermes_console` at `:310`, `-Wl,-rpath` at `:313` and `:317`, `-lm`
at `:325`, and `-lhermesvm` at `:334`.

clang-cl **warns and ignores** every one of those. The fatal one is
`-Wl,-rpath`: clang-cl does not understand `-Wl,`, so the **bare directory that
follows it** is left on the command line and the linker reads it as an input
file:

```
clang-cl: warning: unknown argument ignored in clang-cl: '-fPIC' [-Wunknown-argument]
clang-cl: warning: unknown argument ignored in clang-cl: '-lhermesvm' [-Wunknown-argument]
warning: unknown warning option '-Wl,-rpath'; did you mean '-Wformat'? [-Wunknown-warning-option]
LINK : fatal error LNK1181: cannot open input file '...\static-hermes\lib.obj'
```

**Pointing `SHERMES_CC` at the GNU-driver `clang.exe` that sits beside clang-cl
does not fix it**, which is worth saying because it is the obvious first
suggestion. That driver translates `-L` and `-l` correctly for an MSVC target,
but it still leaves the `-Wl,-rpath` operand as an input file (`LNK1181`, the
same message), and for `-exec` it rejects `-fPIC` outright before it gets that
far:

```
clang: error: unsupported option '-fPIC' for target 'x86_64-pc-windows-msvc'
```

The escape hatches that do exist are environment variables, read at
`compile.cpp:136-147` (`CC`, `CFLAGS`, `LDFLAGS`, `LDLIBS`), and they are
all-or-nothing: a non-empty `CFLAGS` replaces the include paths and `-D`s as well
as the flags, and a non-empty `LDFLAGS` is what skips the `-Wl,-rpath` and `-lm`
block (`compile.cpp:286-329`) — but the trailing `-lhermesvm` (`:334`) still
needs `-nohermeslibs` to suppress. With all three set, and every library named
by full path, a Windows AOT **executable** does build and run under plain
clang-cl. That is a user hand-writing the link line the tool exists to write.

Two further Windows gaps behind this one, both measured:

- **`-exec` cannot work on Windows even once the link line is right.** It
compiles to a shared object and `LoadLibrary`s it, but the generated C's
`main` carries no `__declspec(dllexport)`, so nothing exports it from a DLL:

```
Running library with args: ...\qa-five-lines.js-e5029a
GetProcAddress(main) error: 127
```

(127 is `ERROR_PROC_NOT_FOUND`.) The `-fPIC` / default-visibility assumption
in the `SharedObj` path is an ELF one.
- **`-static-link` is refused outright off `__APPLE__`** (`compile.cpp:305`):
`Static linking unsupported on this platform` — even though linking the static
`hermesvm_a.lib` by hand through `LDFLAGS` works perfectly on Windows and
produces a self-contained executable.

**This is separate from, and downstream of,
`/WHOLEARCHIVE:hermesvm_a` not resolving under `lld-link`** (#2175):
even with a linked `hermesvm.dll`, the flags above would still not work.
Together the two make the AOT path unreachable on a Windows/clang-cl toolchain.

### 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 shermes-dep
echo print("hello"); > hello.js
build\bin\shermes.exe -v -exec hello.js
build\bin\shermes.exe -v hello.js -o hello.exe
```

`shermes.exe` itself builds and compiles the JavaScript fine; the failure is
everything after the C it emits, and `-v` prints the `SHERMES_CC` command line it
constructed. Both invocations end in `LNK1181: cannot open input file
'...\lib.obj'`.

(`shermes-dep` will itself stop earlier at
`lld-link: error: could not open 'hermesvm_a'` — that is #2175, filed on its own. Building the `shermes` target alone is
enough to reach the failure described here.)

### 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, plus a follow-on)

The one-line version: make `SHERMES_CC` not default to an MSVC-driver compiler —
at `tools/shermes/CMakeLists.txt:18`, fall back to a GNU-driver `clang` when
`CMAKE_C_COMPILER` is clang-cl (CMake computes `CMAKE_C_SIMULATE_ID` and the
tree already computes `CLANG_CL`), or fail the configure with a message naming
`SHERMES_CC` rather than deferring the failure to the first `-exec`.

The complete fix is larger and is really the ask: the command-line builder in
`tools/shermes/compile.cpp` should select MSVC spellings when the configured
`SHERMES_CC` is an MSVC-driver compiler — no `-fPIC`, no `-Wl,-rpath` (Windows
has no rpath; the DLL search path is the caller's problem), no `-lm`, and
`/LIBPATH:` plus a plain `name.lib` for the library search. The `-fPIC` at
`:225` in particular is emitted from the output-level switch **before** any of
the `CFLAGS` overrides are consulted, so no environment variable can get out of
its way.

Two smaller ones, if they are wanted separately: give the generated C's `main` a
`__declspec(dllexport)` (or export it from the link) so `-exec` can find it on
Windows, and allow `-static-link` off `__APPLE__` — linking `hermesvm_a.lib`,
`shermes_console_a.lib`, `jsi.lib` and `boost_context.lib` by hand works and
produces a self-contained 4.85 MB executable, which is exactly what
`-static-link` claims to do.

### Related issues

- #1994 / #2089 fixed the `:`-vs-`;` path-list splitting in `tools/shermes/compile.cpp` at `de1526b` (2026-09-02). **Everything above was measured at `5cee10a`, which includes that fix** — the `-I` / `-L` directories now arrive whole, and what remains is that the flags around them are GNU-driver spellings handed to an MSVC-driver compiler. In #1994 the reporter was driving a GNU-driver `clang.exe`; this report is about the DEFAULT `SHERMES_CC` on a clang-cl configure, and about the GNU driver not being a complete escape either (`-fPIC`, `-Wl,-rpath`).
- #1627 (open) reached `dlfcn.h` in `compile.cpp` in early 2025; `f13b0d9` has since ported the `-exec` loading to `LoadLibrary`, and the `GetProcAddress(main)` failure above is the next step on that same path.
- The `/WHOLEARCHIVE:hermesvm_a` bare-name issue, #2175.

### 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). The AOT reading was attempted, time-boxed and recorded as NOT MEASURED — there is no AOT column in that project's tables and no number invented for one. **No file under the Hermes checkout was edited.**

Contributor guide

Open the contributing guide

Research direction

Start with tools/shermes/CMakeLists.txt and tools/shermes/compile.cpp, especially buildCCArgs and the SHERMES_CC, CFLAGS, LDFLAGS, and LDLIBS handling. Reproduce the Windows clang-cl commands from the issue and inspect the generated command line with -v. Done means the requested AOT paths use compatible Windows compiler and linker flags, with the reported -exec and -static-link gaps addressed if included in scope.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, cpp, javascript
Domain
build-system, operating-systems, tooling
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.