Skip to content

Normalize case for key config pathnames on Windows - #27626

Merged
sbc100 merged 2 commits into
emscripten-core:mainfrom
sbc100:windows-path-normcase
Aug 31, 2026
Merged

Normalize case for key config pathnames on Windows#27626
sbc100 merged 2 commits into
emscripten-core:mainfrom
sbc100:windows-path-normcase

Conversation

@sbc100

@sbc100 sbc100 commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

On Windows, file systems are case-insensitive. When tools like VS Code
CMake Tools invoke the compiler through an all-lowercased path (e.g. via
its platformNormalizePath helper), the caller-specified casing affects
the value of __file__ in Python which then propagates all the way to
LLVM_ROOT and the sanity.txt file (at least in the emsdk case):
__file__ -> __rootpath__ -> EM_CONFIG -> CFGDIR -> LLVM_ROOT
-> sanity.txt.

Address this in two places:

  1. In tools/utils.py, normalize __file__ using normcase when
    initializing __rootpath__. This prevents caller-specified casing
    from polluting paths derived from the script location (default
    EM_CONFIG, $CFGDIR, and default CACHE).
  2. In tools/shared.py, normalize config.LLVM_ROOT using normcase
    in generate_sanity(). This ensures that even if LLVM_ROOT was
    configured explicitly (e.g. via EM_LLVM_ROOT or an absolute path
    in .emscripten), the string written to sanity.txt is always
    canonical on Windows.

The proximate cause of the issue was that the VS Code CMake Tools
extension explicitly lowercases compiler paths on Windows:
https://github.com/microsoft/vscode-cmake-tools/blob/09dd68b54de06469bd1b81d84d6b8666aa2dce1d/src/util.ts#L113-L115
By forcing the command line to all-lowercase, the above chain of events
kicked in whenever the IDE queried compiler settings.

See: #27624

On Windows, file systems are case-insensitive. When tools like VS Code
CMake Tools invoke the compiler through an all-lowercased path (e.g. via
its `platformNormalizePath` helper), the caller-specified casing affects
the value of `__file__` in Python which then propagates all the way to
`LLVM_ROOT` and the `sanity.txt` file (at least in the emsdk case):
`__file__` -> `__rootpath__` -> `EM_CONFIG` -> `CFGDIR` -> `LLVM_ROOT`
-> `sanity.txt`.

Address this in two places:
1. In `tools/utils.py`, normalize `__file__` using `normcase` when
   initializing `__rootpath__`. This prevents caller-specified casing
   from polluting paths derived from the script location (default
   `EM_CONFIG`, `$CFGDIR`, and default `CACHE`).
2. In `tools/shared.py`, normalize `config.LLVM_ROOT` using `normcase`
   in `generate_sanity()`. This ensures that even if `LLVM_ROOT` was
   configured explicitly (e.g. via `EM_LLVM_ROOT` or an absolute path
   in `.emscripten`), the string written to `sanity.txt` is always
   canonical on Windows.

See: emscripten-core#27624
@sbc100
sbc100 force-pushed the windows-path-normcase branch from d76fbf7 to b43b171 Compare August 30, 2026 22:33
@sbc100
sbc100 requested review from dschuff and kripken August 30, 2026 22:34
@sbc100 sbc100 changed the title Normalize case for key config pathnames on Windows [config] Normalize case for key config pathnames on Windows Aug 30, 2026
@sbc100 sbc100 changed the title [config] Normalize case for key config pathnames on Windows Normalize case for key config pathnames on Windows Aug 30, 2026
Comment thread test/test_sanity.py Outdated
Comment thread test/test_sanity.py

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm % comments

@sbc100
sbc100 merged commit e88bf43 into emscripten-core:main Aug 31, 2026
42 checks passed
@sbc100
sbc100 deleted the windows-path-normcase branch August 31, 2026 20:40
@dschuff

dschuff commented Sep 1, 2026

Copy link
Copy Markdown
Member

sbc100 added a commit that referenced this pull request Sep 1, 2026
Sanity tests previously ran on Windows via `--crossplatform-only` after
#22492, but were removed in #26365 when all tests were combined into a
single parallel test suite (which made mixing parallel and non-parallel
modules an error).

Run `sanity.test_windows_path_casing` as a dedicated step in
`test-windows` so the Windows path casing regression test from #27626 is
exercised in CI.
sbc100 added a commit that referenced this pull request Sep 1, 2026
In #27626, `__rootpath__` was changed to use
`os.path.normcase(__file__)` to prevent caller-specified casing from
propagating to `config.LLVM_ROOT` and `sanity.txt`.

However, on Windows `normcase` unconditionally converts the entire path
including the drive letter to lowercase (`c:\...`). This lowercases all
paths derived from `path_from_root`, including default `config.CACHE`
and `EMSCRIPTEN_SYSROOT`.

When CMake runs on Windows, imported targets and compiler autodetection
preserve uppercase drive letters (`C:\...`). Because CMake's implicit
include directory checks are case-sensitive string comparisons, the
mismatch between `c:\...` and `C:\...` caused CMake to treat the sysroot
include directory as an explicit non-system include, injecting
`-isystem` and disrupting libc++ header search order in
`test_cmake_install`. It also caused assertion failures in
`test_cmake_compile_commands_noforce`.

The normalization of `config.LLVM_ROOT` in `generate_sanity()` in
`tools/shared.py` (also added in #27626) is already sufficient to ensure
that `sanity.txt` remains consistent regardless of the casing used to
invoke `emcc`. Normalizing `__rootpath__` is unnecessary and harmful.

See: #27626
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants