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
5 changes: 5 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ See docs/process.md for more on how version tagging works.
e.g. Intel x64 Macs and Windows-on-ARM, downloading Java SE Development Kit
21.0.11 from https://www.oracle.com/europe/java/technologies/downloads/#java21
is required in order to use Emscripten's Closure Compiler integration.
- The `FAKE_DYLIBS` setting is now disabled by default. This means that
`-shared` will produce real dynamic libraries by default (`-sSIDE_MODULE` is
implied). Also, if you include real dynamic libraries in your link command
emscripten will now automatically produce a dynamically linked program
(`-sMAIN_MODULE=2` is implied). (#25930)

5.0.7 - 04/30/26
----------------
Expand Down
9 changes: 8 additions & 1 deletion cmake/Modules/Platform/Emscripten.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ set(CMAKE_SYSTEM_NAME Emscripten)
set(CMAKE_SYSTEM_VERSION 1)

set(CMAKE_CROSSCOMPILING TRUE)
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)

# Certain cmake versions are not compatible with dynnamic linking due to
# https://gitlab.kitware.com/cmake/cmake/-/work_items/27240
if (("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "4.2.0" AND "${CMAKE_VERSION}" VERSION_LESS "4.2.6") OR
("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "4.3.0" AND "${CMAKE_VERSION}" VERSION_LESS "4.3.3"))
message(WARNING "This version of cmake (${CMAKE_VERSION}) does not support emscripten shared libraries. Use cmake < 4.2.0 or cmake > 4.3.3 if you need shared library support")
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
endif()

# Advertise Emscripten as a 32-bit platform (as opposed to
# CMAKE_SYSTEM_PROCESSOR=x86_64 for 64-bit platform), since some projects (e.g.
Expand Down
21 changes: 10 additions & 11 deletions site/source/docs/compiling/Dynamic-Linking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ before your application starts to run.
- Build one part of your code as the main module, linking it using
``-sMAIN_MODULE`` (See :ref:`MAIN_MODULE`).
- Build other parts of your code as side modules, linking it using
``-sSIDE_MODULE`` (See :ref:`SIDE_MODULE`).
``-shared``. You can also used the emscripten-specific :ref:`SIDE_MODULE`
setting which does the same thing by default.

For the main module the output suffix should be ``.js`` (the WebAssembly
file will be generated alongside it just like normal). For the side
Expand Down Expand Up @@ -149,19 +150,17 @@ Building Dynamic Libraries using ``-shared``
============================================

In traditional toolchains the ``-shared`` flag is used to generated dynamic
libraries. However, because dynamic linking in Emscripten comes with caveats
and has some overhead, Emscripten does not currently produce real dynamic
libraries when this flag is used. Instead, Emscripten will produce a fake
dynamic library (along with a warning) that is actually a single static object
file. When your main program is linked against this fake dynamic library it
gets linked into your main program like any other object file.
libraries. Historically, due to early limitations, Emscripten would produce a
fake dynamic library (along with a warning) when the ``-shared`` flag was used.
When your main program is linked against these fake dynamic library they would
be linked into your main program like regular static other object files.

The reason for this behaviour is to allow projects (and build systems) that
assume a working ``-shared`` flag to build successfully (albeit using static
linking).
These days Emscripten will produce real dynamic libraries by default and
``-shared`` is essentially the same as ``-sSIDE_MODULE``.

This behaviour can be controlled using the :ref:`FAKE_DYLIBS` settings. If you
disable `FAKE_DYLIBS` then ``-shared`` will act like ``-sSIDE_MODULE``.
prefer the older behaviour with fake dynamic libraryies you can enable this
setting.

Code Size
=========
Expand Down
12 changes: 6 additions & 6 deletions site/source/docs/tools_reference/settings_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3356,13 +3356,13 @@ Default value: false
FAKE_DYLIBS
===========

This setting changes the behaviour of the ``-shared`` flag. The default
setting of ``true`` means the ``-shared`` flag actually produces a normal
object file (i.e. ``ld -r``). Setting this to false will cause ``-shared``
to behave like :ref:`SIDE_MODULE` and produce a dynamically linked
library.
This setting changes the behaviour of the ``-shared`` flag. When set to true
you get the old emscripten behaviour where the ``-shared`` flag actually
produces a normal object file (i.e. ``ld -r``). When set to true (the
default) the ``-shared`` flag is equivelent to :ref:`SIDE_MODULE` and will
produce a Wasn dynamic library.

Default value: true
Default value: false

.. _executable:

Expand Down
12 changes: 6 additions & 6 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2201,12 +2201,12 @@ var GROWABLE_ARRAYBUFFERS = false;
// indirectly using `importScripts`
var CROSS_ORIGIN = false;

// This setting changes the behaviour of the ``-shared`` flag. The default
// setting of ``true`` means the ``-shared`` flag actually produces a normal
// object file (i.e. ``ld -r``). Setting this to false will cause ``-shared``
// to behave like :ref:`SIDE_MODULE` and produce a dynamically linked
// library.
var FAKE_DYLIBS = true;
// This setting changes the behaviour of the ``-shared`` flag. When set to true
// you get the old emscripten behaviour where the ``-shared`` flag actually
// produces a normal object file (i.e. ``ld -r``). When set to true (the
// default) the ``-shared`` flag is equivelent to :ref:`SIDE_MODULE` and will
// produce a Wasn dynamic library.
var FAKE_DYLIBS = false;

// Add a #! line to generated JS file and make it executable. This is useful
// for building command line tools that run under node.
Expand Down
12 changes: 8 additions & 4 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1517,7 +1517,7 @@ def get_poppler_library(self, env_init=None):

return poppler + freetype

def get_zlib_library(self, cmake, cflags=None):
def get_zlib_library(self, cmake, cflags=None, target='libz.a'):
Comment thread
sbc100 marked this conversation as resolved.
assert cmake or not WINDOWS, 'on windows, get_zlib_library only supports cmake'

old_args = self.cflags.copy()
Expand All @@ -1531,12 +1531,16 @@ def get_zlib_library(self, cmake, cflags=None):
# https://github.com/emscripten-core/emscripten/issues/16908 is fixed
self.cflags.append('-Wno-pointer-sign')
if cmake:
rtn = self.get_library(os.path.join('third_party', 'zlib'), os.path.join('libz.a'),
configure=['cmake', '.'],
if target == 'libz.a':
cmake_cmd = ['cmake', '-DBUILD_SHARED_LIBS=OFF', '.']
else:
cmake_cmd = ['cmake', '.']
rtn = self.get_library(os.path.join('third_party', 'zlib'), target,
configure=cmake_cmd,
make=['cmake', '--build', '.', '--'],
make_args=[])
else:
rtn = self.get_library(os.path.join('third_party', 'zlib'), os.path.join('libz.a'), make_args=['libz.a'])
rtn = self.get_library(os.path.join('third_party', 'zlib'), target, make_args=['libz.a', target])
self.cflags = old_args
return rtn

Expand Down
39 changes: 27 additions & 12 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4051,8 +4051,8 @@ def dylink_testf(self, main, side=None, expected=None, force_c=False, main_cflag

shutil.move(so_file, so_file + '.orig')

# Verify that building with -sSIDE_MODULE is essentially the same as building with `-shared -fPIC -sFAKE_DYLIBS=0`.
flags = ['-shared', '-fPIC', '-sFAKE_DYLIBS=0']
# Verify that building with -sSIDE_MODULE is essentially the same as building with `-shared -fPIC`
flags = ['-shared', '-fPIC']
if isinstance(side, list):
# side is just a library
self.run_process([EMCC] + side + self.get_cflags() + flags + ['-o', so_file])
Expand Down Expand Up @@ -5079,7 +5079,8 @@ def do_run(src, expected_output, cflags=None):

@with_dylink_reversed
def test_dylink_dot_a(self):
# .a linking must force all .o files inside it, when in a shared module
# Tests that it's possible to link a .a archive into a dynamic library using
# `-Wl,--whole-archive`
create_file('third.c', 'int sidef() { return 36; }')
create_file('fourth.c', 'int sideg() { return 17; }')

Expand All @@ -5097,7 +5098,7 @@ def test_dylink_dot_a(self):
}
''',
# contents of libfourth.a must be included, even if they aren't referred to!
side=['libfourth.a', 'third.o'],
side=['-Wl,--whole-archive', 'libfourth.a', '-Wl,--no-whole-archive', 'third.o'],
expected=['sidef: 36, sideg: 17.\n'], force_c=True)

@with_dylink_reversed
Expand Down Expand Up @@ -5141,16 +5142,30 @@ def test_dylink_spaghetti(self):
'''])

@needs_make('mingw32-make')
@with_dylink_reversed
def test_dylink_zlib(self):
zlib_archive = self.get_zlib_library(cmake=WINDOWS, cflags=['-fPIC'])
@needs_dylink
@parameterized({
'cmake': (True,),
'configure': (False,),
})
def test_dylink_zlib(self, cmake):
if cmake:
output = self.run_process(['cmake', '--version'], stdout=PIPE).stdout
cmake_version = output.splitlines()[0].split()[-1].strip()
cmake_version = tuple(int(part) for part in cmake_version.split('.'))
# We don't support dynamic linking with certain versions of cmake
# See https://gitlab.kitware.com/cmake/cmake/-/work_items/27240
if (cmake_version >= (4, 2, 0) and cmake_version < (4, 2, 6)) or \
(cmake_version >= (4, 3, 0) and cmake_version < (4, 3, 3)):
self.skipTest(f'incompatible cmake version {cmake_version}')
zlib_archive = self.get_zlib_library(cmake=cmake, target='libz.so.1.2.5', cflags=['-fPIC'])[0]
zlib_basename = os.path.basename(zlib_archive)
shutil.copyfile(zlib_archive, zlib_basename)
# example.c uses K&R style function declarations
self.cflags.append('-Wno-deprecated-non-prototype')
self.cflags.append('-I' + test_file('third_party/zlib'))
self.dylink_test(main=read_file(test_file('third_party/zlib/example.c')),
side=zlib_archive,
expected=read_file(test_file('core/test_zlib.out')),
force_c=True)
self.do_runf(test_file('third_party/zlib/example.c'),
read_file(test_file('core/test_zlib.out')),
cflags=['-L.', zlib_archive])

# @with_dylink_reversed
# def test_dylink_bullet(self):
Expand Down Expand Up @@ -6958,7 +6973,7 @@ def line_splitter(data):
'codec/CMakeFiles/j2k_to_image.dir/__/common/color.c.o',
'codec/CMakeFiles/j2k_to_image.dir/__/common/getopt.c.o',
'bin/libopenjpeg.a'],
configure=['cmake', '.'],
configure=['cmake', '.', '-DBUILD_SHARED_LIBS=OFF'],
# configure_args=['--enable-tiff=no', '--enable-jp3d=no', '--enable-png=no'],
make_args=[]) # no -j 2, since parallel builds can fail

Expand Down
Loading
Loading