Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.

6.0.6 (in development)
----------------------
- `DEFAULT_TO_CXX` is now disabled by default. This means that `em++` is now
required when linking C++ programs, matching the behavior of clang and gcc.
The old behavior is still available using `-sDEFAULT_TO_CXX`. (#11121)
- libcxx and libcxxabi were updated to LLVM 22.1.8. (#27428)

6.0.5 - 07/29/26
Expand Down
8 changes: 3 additions & 5 deletions site/source/docs/tools_reference/settings_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1770,7 +1770,6 @@ Changes enabled by this:
- IGNORE_MISSING_MAIN is disabled.
- AUTO_JS_LIBRARIES is disabled.
- AUTO_NATIVE_LIBRARIES is disabled.
- DEFAULT_TO_CXX is disabled.
- ALLOW_UNIMPLEMENTED_SYSCALLS is disabled.
- INCOMING_MODULE_JS_API is set to empty by default.

Expand Down Expand Up @@ -3180,11 +3179,10 @@ Default value: []
DEFAULT_TO_CXX
==============

Default to c++ mode even when run as ``emcc`` rather than ``emc++``.
When this is disabled ``em++`` is required when linking C++ programs.
Disabling this will match the behaviour of gcc/g++ and clang/clang++.
Default to c++ mode even when run as ``emcc`` rather than ``em++``.
By default, ``em++`` is required when linking C++ programs.

Default value: true
Default value: false

.. _printf_long_double:

Expand Down
8 changes: 3 additions & 5 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,6 @@ var LINKABLE = false;
// - IGNORE_MISSING_MAIN is disabled.
// - AUTO_JS_LIBRARIES is disabled.
// - AUTO_NATIVE_LIBRARIES is disabled.
// - DEFAULT_TO_CXX is disabled.
// - ALLOW_UNIMPLEMENTED_SYSCALLS is disabled.
// - INCOMING_MODULE_JS_API is set to empty by default.
// [compile+link]
Expand Down Expand Up @@ -2116,11 +2115,10 @@ var ASAN_SHADOW_SIZE = -1;
// [link]
var SOURCE_MAP_PREFIXES = [];

// Default to c++ mode even when run as ``emcc`` rather than ``emc++``.
// When this is disabled ``em++`` is required when linking C++ programs.
// Disabling this will match the behaviour of gcc/g++ and clang/clang++.
// Default to c++ mode even when run as ``emcc`` rather than ``em++``.
// By default, ``em++`` is required when linking C++ programs.
// [link]
var DEFAULT_TO_CXX = true;
var DEFAULT_TO_CXX = false;

// While LLVM's wasm32 has long double = float128, we don't support printing
// that at full precision by default. Instead we print as 64-bit doubles, which
Expand Down
5 changes: 0 additions & 5 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,11 +741,6 @@ def setUp(self):
self.skip_exec = None
self.flaky = False
self.cflags = ['-Wclosure', '-Werror', '-Wno-limited-postlink-optimizations']
# TODO(https://github.com/emscripten-core/emscripten/issues/11121)
# For historical reasons emcc compiles and links as C++ by default.
# However we want to run our tests in a more strict manner. We can
# remove this if the issue above is ever fixed.
self.set_setting('NO_DEFAULT_TO_CXX')
self.ldflags = []
# Increase the stack trace limit to maximise usefulness of test failure reports.
# Also, include backtrace for all uncaught exceptions (not just Error).
Expand Down
1 change: 0 additions & 1 deletion test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10076,7 +10076,6 @@ def setUp(self):
esm_integration = make_run('esm_integration', init=lambda self: self.setup_esm_integration())
instance = make_run('instance', cflags=['-Wno-experimental'], settings={'MODULARIZE': 'instance'})

# Add DEFAULT_TO_CXX=0
strict = make_run('strict', cflags=[], settings={'STRICT': 1})
strict_js = make_run('strict_js', cflags=[], settings={'STRICT_JS': 1})

Expand Down
15 changes: 9 additions & 6 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -2951,6 +2951,9 @@ def test_undefined_data_symbols(self):
self.run_process([EMCC, 'main.c', '-Wl,--unresolved-symbols=ignore-all'])
self.run_process([EMCC, 'main.c', '-Wl,--allow-undefined'])

def test_emcc_cxx_link_hint(self):
self.assert_fail([EMCC, test_file('hello_libcxx.cpp')], "warning: link failed with undefined C++ symbols. Try linking with 'em++' or passing '-sDEFAULT_TO_CXX'")

def test_GetProcAddress_LEGACY_GL_EMULATION(self):
# without legacy gl emulation, getting a proc from there should fail
self.do_other_test('test_GetProcAddress_LEGACY_GL_EMULATION.c', args=['0'], cflags=['-sLEGACY_GL_EMULATION=0', '-sGL_ENABLE_GET_PROC_ADDRESS'])
Expand Down Expand Up @@ -3757,12 +3760,12 @@ def test_embind_tsgen_test_embind(self):

def test_embind_tsgen_val(self):
# Check that any dependencies from val still works with TS generation enabled.
self.run_process([EMCC, test_file('other/embind_tsgen_val.cpp'),
self.run_process([EMXX, test_file('other/embind_tsgen_val.cpp'),
'-lembind', '--emit-tsd', 'embind_tsgen_val.d.ts'])
self.assertExists('embind_tsgen_val.d.ts')

def test_embind_tsgen_constant_only(self):
self.run_process([EMCC, test_file('other/embind_tsgen_constant_only.cpp'),
self.run_process([EMXX, test_file('other/embind_tsgen_constant_only.cpp'),
'-lembind', '--emit-tsd', 'out.d.ts'])
self.assertFilesMatch(test_file('other/embind_tsgen_constant_only.d.ts'), 'out.d.ts')

Expand Down Expand Up @@ -9373,7 +9376,7 @@ def test(check, extra):
'gl_emu': (['-sLEGACY_GL_EMULATION', '-sMAXIMUM_MEMORY=4GB', '-sALLOW_MEMORY_GROWTH'],),
'no_exception_throwing': (['-sDISABLE_EXCEPTION_THROWING'],),
'minimal_runtime': (['-sMINIMAL_RUNTIME'],),
'embind': (['-lembind'],),
'embind': (['-lembind', '-sDEFAULT_TO_CXX'],),
})
def test_full_js_library(self, args):
self.run_process([EMCC, test_file('hello_world.c'), '-sSTRICT_JS', '-sINCLUDE_FULL_LIBRARY'] + args)
Expand Down Expand Up @@ -11670,7 +11673,7 @@ def test_exceptions_with_closure_and_without_catching(self):
throw 5;
}
''')
self.run_process([EMCC, 'src.cpp', '-fexceptions', '--closure=1'])
self.run_process([EMXX, 'src.cpp', '-fexceptions', '--closure=1'])

def test_assertions_on_incoming_module_api_changes(self):
create_file('pre.js', 'Module.read = () => {};')
Expand Down Expand Up @@ -14798,7 +14801,7 @@ def test_no_minify(self):

def test_no_minify_and_later_closure(self):
# test that running closure after --minify=0 works
self.run_process([EMCC, test_file('hello_libcxx.cpp'), '-O2', '--minify=0'])
self.run_process([EMXX, test_file('hello_libcxx.cpp'), '-O2', '--minify=0'])
temp = building.closure_compiler('a.out.js',
advanced=True,
extra_closure_args=['--formatting', 'PRETTY_PRINT'])
Expand Down Expand Up @@ -15753,7 +15756,7 @@ def test_empath_split(self):
/emsdk/emscripten/system/lib/libcxx
''')

self.run_process([EMCC, 'main.cpp', 'foo.cpp', '-gsource-map', '-g2', '-o', 'test.js'])
self.run_process([EMXX, 'main.cpp', 'foo.cpp', '-gsource-map', '-g2', '-o', 'test.js'])
empath_split_cmd = [empath_split, 'test.wasm', 'path_list.txt', '-g', '-o', 'test_primary.wasm', '--out-prefix=test_', '-v']
out = self.run_process(empath_split_cmd, stdout=PIPE).stdout

Expand Down
4 changes: 2 additions & 2 deletions test/test_sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

from tools import building, cache, ports, response_file, shared, utils
from tools.config import EM_CONFIG
from tools.shared import EMCC, config
from tools.shared import EMCC, EMXX, config
from tools.utils import delete_dir, delete_file

SANITY_FILE = cache.get_path('sanity.txt')
Expand Down Expand Up @@ -419,7 +419,7 @@ def test_emcc_caching(self):
for i in range(3):
print(i)
self.clear()
output = self.do([EMCC, '-O' + str(i), test_file('hello_libcxx.cpp'), '-sDISABLE_EXCEPTION_CATCHING=0'])
output = self.do([EMXX, '-O' + str(i), test_file('hello_libcxx.cpp'), '-sDISABLE_EXCEPTION_CATCHING=0'])
if i == 0:
libname = cache.get_lib_name('libc++-debug.a')
else:
Expand Down
23 changes: 22 additions & 1 deletion tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from . import (
cache,
cmdline,
colored_logger,
config,
diagnostics,
js_optimizer,
Expand Down Expand Up @@ -344,7 +345,27 @@ def link_lld(args, target, external_symbols=None, linker_inputs=None):
cmd += lld_flags_for_executable(external_symbols)
cmd += lld_flags(args, linker_inputs)
cmd = get_command_with_possible_response_file(cmd)
check_call(cmd)
if settings.LINK_AS_CXX:
check_call(cmd)
else:
# When not running C++ mode we currently capture the stderr of the linker
# so that we can recommend using `em++` when there are libc++ symbols missing.
# TODO: Remove this extra complexity one day.
if colored_logger.ansi_color_available():
# We force color diagnostics from wasm-ld when we know that they are available
# in the current TTY. Without this, the use of stderr=PIPE would cause
# wasm-ld to always disable color output.
cmd.append('--color-diagnostics=always')
Comment thread
sbc100 marked this conversation as resolved.
try:
proc = shared.run_process(cmd, stderr=subprocess.PIPE)
if proc.stderr:
sys.stderr.write(proc.stderr)
except subprocess.CalledProcessError as e:
sys.stderr.write(e.stderr)
cxx_symbols = ('std::', 'operator new', 'operator delete', 'vtable for', 'typeinfo for', '__cxa_')
if any(sym in e.stderr for sym in cxx_symbols):
diagnostics.warn("link failed with undefined C++ symbols. Try linking with 'em++' or passing '-sDEFAULT_TO_CXX'")
exit_with_error("'%s' failed (%s)", shlex.join(cmd), shared.returncode_to_str(e.returncode))


def get_command_with_possible_response_file(cmd):
Expand Down
1 change: 0 additions & 1 deletion tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,6 @@ def limit_incoming_module_api():
if settings.STRICT:
if not settings.EXPORT_ES6:
default_setting('STRICT_JS', 1)
default_setting('DEFAULT_TO_CXX', 0)
default_setting('IGNORE_MISSING_MAIN', 0)
default_setting('AUTO_NATIVE_LIBRARIES', 0)
if settings.MAIN_MODULE != 1:
Expand Down
16 changes: 11 additions & 5 deletions tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,25 @@ def get_finished_process():
return [x[1] for x in std_outs]


def check_call(cmd, *args, **kw):
"""Like `run_process` above but treat failures as fatal and exit_with_error."""
def run_process(cmd, *args, **kw):
"""Wrapper around utils.run_process used to running compiler sub-processes."""
print_compiler_stage(cmd)
if SKIP_SUBPROCS:
return 0
return subprocess.CompletedProcess(cmd, 0, stdout='', stderr='')
try:
return utils.run_process(cmd, *args, **kw)
except subprocess.CalledProcessError as e:
exit_with_error("'%s' failed (%s)", shlex.join(cmd), returncode_to_str(e.returncode))
except OSError as e:
exit_with_error("'%s' failed: %s", shlex.join(cmd), e)


def check_call(cmd, *args, **kw):
"""Like `run_process` above but treat failures as fatal and exit_with_error."""
try:
return run_process(cmd, *args, **kw)
except subprocess.CalledProcessError as e:
exit_with_error("'%s' failed (%s)", shlex.join(cmd), returncode_to_str(e.returncode))


def exec_process(cmd):
print_compiler_stage(cmd)
utils.exec(cmd)
Expand Down
Loading