Skip to content

Commit 209b704

Browse files
authored
[cmake] Add emscan-deps tool to enable C++20 (#21987)
This is wrapper around clang-scan-deps. Currently this is just enough to make C++20 work under cmake. We don't currently have any actaully tests of C++20 modules. See: #21042 Fixes: #21866 #22305
1 parent 5949d5a commit 209b704

File tree

11 files changed

+168
-1
lines changed

11 files changed

+168
-1
lines changed

Diff for: ChangeLog.md

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.
2020

2121
4.0.3 (in development)
2222
----------------------
23+
- emscan-deps tools was added. This tool wraps clang-scan-deps and injects the
24+
needed `--target` and `--sysroot` argument that would normally be injected by
25+
emcc itself. This enables support for C++20 in cmake projects. (#21987)
2326

2427
4.0.2 - 01/30/25
2528
----------------

Diff for: cmake/Modules/Platform/Emscripten.cmake

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ set(CMAKE_C_COMPILER_AR "${CMAKE_AR}")
9393
set(CMAKE_CXX_COMPILER_AR "${CMAKE_AR}")
9494
set(CMAKE_C_COMPILER_RANLIB "${CMAKE_RANLIB}")
9595
set(CMAKE_CXX_COMPILER_RANLIB "${CMAKE_RANLIB}")
96+
set(CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS "${EMSCRIPTEN_ROOT_PATH}/emscan-deps")
9697

9798
# Capture the Emscripten version to EMSCRIPTEN_VERSION variable.
9899
if (NOT EMSCRIPTEN_VERSION)

Diff for: emscan-deps

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/sh
2+
# Copyright 2020 The Emscripten Authors. All rights reserved.
3+
# Emscripten is available under two separate licenses, the MIT license and the
4+
# University of Illinois/NCSA Open Source License. Both these licenses can be
5+
# found in the LICENSE file.
6+
#
7+
# Entry point for running python scripts on UNIX systems.
8+
#
9+
# Automatically generated by `create_entry_points.py`; DO NOT EDIT.
10+
#
11+
# To make modifications to this file, edit `tools/run_python.sh` and then run
12+
# `tools/maint/create_entry_points.py`
13+
14+
# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal
15+
# of cpython used in cross compilation via setup.py.
16+
unset _PYTHON_SYSCONFIGDATA_NAME
17+
18+
if [ -z "$PYTHON" ]; then
19+
PYTHON=$EMSDK_PYTHON
20+
fi
21+
22+
if [ -z "$PYTHON" ]; then
23+
PYTHON=$(command -v python3 2> /dev/null)
24+
fi
25+
26+
if [ -z "$PYTHON" ]; then
27+
PYTHON=$(command -v python 2> /dev/null)
28+
fi
29+
30+
if [ -z "$PYTHON" ]; then
31+
echo 'unable to find python in $PATH'
32+
exit 1
33+
fi
34+
35+
exec "$PYTHON" -E "$0.py" "$@"

Diff for: emscan-deps.bat

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
:: Entry point for running python scripts on windows systems.
2+
::
3+
:: Automatically generated by `create_entry_points.py`; DO NOT EDIT.
4+
::
5+
:: To make modifications to this file, edit `tools/run_python.bat` and then run
6+
:: `tools/maint/create_entry_points.py`
7+
8+
:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks,
9+
:: or there will be a parsing error.
10+
11+
:: All env. vars specified in this file are to be local only to this script.
12+
@setlocal
13+
:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal
14+
:: of cpython used in cross compilation via setup.py.
15+
@set _PYTHON_SYSCONFIGDATA_NAME=
16+
@set EM_PY=%EMSDK_PYTHON%
17+
@if "%EM_PY%"=="" (
18+
set EM_PY=python
19+
)
20+
21+
:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this
22+
:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and
23+
:: %~dp0 expansions will not work.
24+
:: So first try if %~dp0 might work, and if not, manually look up this script from PATH.
25+
@if exist "%~f0" (
26+
set MYDIR=%~dp0
27+
goto FOUND_MYDIR
28+
)
29+
@for %%I in (%~n0.bat) do (
30+
@if exist %%~$PATH:I (
31+
set MYDIR=%%~dp$PATH:I
32+
) else (
33+
echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat.
34+
echo To help this issue, try removing unnecessary quotes in the invocation of emcc,
35+
echo or add Emscripten directory to PATH.
36+
echo See github.com/microsoft/terminal/issues/15212 and
37+
echo github.com/emscripten-core/emscripten/issues/19207 for more details.
38+
)
39+
)
40+
:FOUND_MYDIR
41+
42+
:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a
43+
:: shared stdin handle from the parent process, and that parent process stdin handle is in
44+
:: a certain state, running python.exe might hang here. To work around this, if
45+
:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid
46+
:: sharing the parent's stdin handle to it, avoiding the hang.
47+
48+
:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel,
49+
:: even when the python executable above did succeed and quit with errorlevel 0 above.
50+
:: On Windows 8 and newer, this issue has not been observed. It is possible that this
51+
:: issue is related to the above python bug, but this has not been conclusively confirmed,
52+
:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known
53+
:: workaround this issue, which is to explicitly quit the calling process with the previous
54+
:: errorlevel from the above command.
55+
56+
:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from
57+
:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that
58+
:: would throw off the parsing of the cmdline arg.
59+
@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" (
60+
@if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" (
61+
goto NORMAL
62+
) else (
63+
goto NORMAL_EXIT
64+
)
65+
) else (
66+
@if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" (
67+
goto MUTE_STDIN
68+
) else (
69+
goto MUTE_STDIN_EXIT
70+
)
71+
)
72+
73+
:NORMAL_EXIT
74+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %*
75+
@exit %ERRORLEVEL%
76+
77+
:MUTE_STDIN
78+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL
79+
@exit /b %ERRORLEVEL%
80+
81+
:MUTE_STDIN_EXIT
82+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL
83+
@exit %ERRORLEVEL%
84+
85+
:NORMAL
86+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %*

Diff for: emscan-deps.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2025 The Emscripten Authors. All rights reserved.
3+
# Emscripten is available under two separate licenses, the MIT license and the
4+
# University of Illinois/NCSA Open Source License. Both these licenses can be
5+
# found in the LICENSE file.
6+
7+
"""emscan-deps - clang-scan-deps helper script
8+
9+
This script acts as a frontend replacement for clang-scan-deps.
10+
"""
11+
12+
import sys
13+
from tools import shared, cache
14+
15+
args = sys.argv[1:]
16+
args.append('--sysroot=' + cache.get_sysroot(absolute=True))
17+
args.append('--target=' + shared.get_llvm_target())
18+
shared.exec_process([shared.CLANG_SCAN_DEPS] + args)

Diff for: test/cmake/cxx20/CMakeLists.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
cmake_minimum_required(VERSION 3.5...3.28)
2+
3+
project(cxx20test)
4+
5+
set(CMAKE_CXX_STANDARD 20)
6+
7+
add_executable(cxx20test main.cpp)

Diff for: test/cmake/cxx20/main.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2025 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
6+
#include <stdio.h>
7+
8+
class Test {}; // This will fail in C mode
9+
10+
int main() {
11+
printf("hello, world!\n");
12+
return 0;
13+
}

Diff for: test/cmake/cxx20/out.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello, world!

Diff for: test/test_other.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,7 @@ def test_emstrip(self):
906906
'whole_archive': ('whole_archive', 'whole.js', []),
907907
'stdproperty': ('stdproperty', 'helloworld.js', []),
908908
'post_build': ('post_build', 'hello.js', []),
909+
'cxx20': ('cxx20', 'cxx20test.js', []),
909910
})
910911
def test_cmake(self, test_dir, output_file, cmake_args):
911912
if test_dir == 'whole_archive' and 'EMTEST_SKIP_NEW_CMAKE' in os.environ:
@@ -956,7 +957,7 @@ def test_cmake(self, test_dir, output_file, cmake_args):
956957
# Run through node, if CMake produced a .js file.
957958
if output_file.endswith('.js'):
958959
ret = self.run_js(output_file)
959-
self.assertTextDataIdentical(read_file(cmakelistsdir + '/out.txt').strip(), ret.strip())
960+
self.assertFileContents(os.path.join(cmakelistsdir, 'out.txt'), ret)
960961

961962
if test_dir == 'post_build':
962963
ret = self.run_process(['ctest'], env=env)

Diff for: tools/maint/create_entry_points.py

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
emnm
4444
emstrip
4545
emsymbolizer
46+
emscan-deps
4647
system/bin/sdl-config
4748
system/bin/sdl2-config
4849
tools/file_packager

Diff for: tools/shared.py

+1
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,7 @@ class OFormat(Enum):
790790

791791
CLANG_CC = os.path.expanduser(build_clang_tool_path(exe_suffix('clang')))
792792
CLANG_CXX = os.path.expanduser(build_clang_tool_path(exe_suffix('clang++')))
793+
CLANG_SCAN_DEPS = build_llvm_tool_path(exe_suffix('clang-scan-deps'))
793794
LLVM_AR = build_llvm_tool_path(exe_suffix('llvm-ar'))
794795
LLVM_DWP = build_llvm_tool_path(exe_suffix('llvm-dwp'))
795796
LLVM_RANLIB = build_llvm_tool_path(exe_suffix('llvm-ranlib'))

0 commit comments

Comments
 (0)