From 165a464390f4e02c25a55b814d6102c16a51e76d Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 28 Jan 2022 14:55:37 +0100 Subject: [PATCH 1/9] bpo-40280: Omit wait, getrusage, and prlimit on Emscripten --- Lib/test/support/__init__.py | 2 ++ Lib/test/support/os_helper.py | 2 +- Lib/test/test_capi.py | 2 ++ Lib/test/test_faulthandler.py | 3 +++ Lib/test/test_resource.py | 1 + Modules/clinic/resource.c.h | 10 +++++++++- Modules/resource.c | 2 ++ Modules/timemodule.c | 2 +- Tools/wasm/config.site-wasm32-emscripten | 4 ++++ configure | 2 +- configure.ac | 2 +- pyconfig.h.in | 3 +++ 12 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index e5eb66e9068e71..5f9a03de83e131 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1278,6 +1278,8 @@ def reap_children(): # Need os.waitpid(-1, os.WNOHANG): Windows is not supported if not (hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG')): return + if not has_subprocess_support: + return # Reap all our dead child processes so we don't leave zombies around. # These hog resources and might be causing some of the buildbots to die. diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 50aa7a7176c0a4..c761d7bd3d2b6d 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -502,7 +502,7 @@ def __fspath__(self): def fd_count(): """Count the number of open file descriptors. """ - if sys.platform.startswith(('linux', 'freebsd')): + if sys.platform.startswith(('linux', 'freebsd', 'emscripten')): try: names = os.listdir("/proc/self/fd") # Subtract one because listdir() internally opens a file diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index ccf8ceda49831e..089088d97a66e2 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -611,6 +611,7 @@ def check_fatal_error(self, code, expected, not_expected=()): self.assertNotIn(name, modules) self.assertEqual(len(modules), total) + @support.requires_subprocess() def test_fatal_error(self): # By default, stdlib extension modules are ignored, # but not test modules. @@ -880,6 +881,7 @@ class Test_testinternalcapi(unittest.TestCase): if name.startswith('test_')) +@support.requires_subprocess() class PyMemDebugTests(unittest.TestCase): PYTHONMALLOC = 'debug' # '0x04c06e0' or '04C06E0' diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index daacdeef5bc801..8d106daaf6520a 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -19,6 +19,9 @@ except ImportError: _testcapi = None +if not support.has_subprocess_support: + raise unittest.SkipTest("test module requires subprocess") + TIMEOUT = 0.5 MS_WINDOWS = (os.name == 'nt') diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index f2642c6ba181db..317e7ca8f8c853 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -98,6 +98,7 @@ def test_fsize_toobig(self): except (OverflowError, ValueError): pass + @unittest.skipUnless(hasattr(resource, "getrusage"), "needs getrusage") def test_getrusage(self): self.assertRaises(TypeError, resource.getrusage) self.assertRaises(TypeError, resource.getrusage, 42, 42) diff --git a/Modules/clinic/resource.c.h b/Modules/clinic/resource.c.h index 32c092ad7a94a3..f31f7e8265e795 100644 --- a/Modules/clinic/resource.c.h +++ b/Modules/clinic/resource.c.h @@ -2,6 +2,8 @@ preserve [clinic start generated code]*/ +#if defined(HAVE_GETRUSAGE) + PyDoc_STRVAR(resource_getrusage__doc__, "getrusage($module, who, /)\n" "--\n" @@ -29,6 +31,8 @@ resource_getrusage(PyObject *module, PyObject *arg) return return_value; } +#endif /* defined(HAVE_GETRUSAGE) */ + PyDoc_STRVAR(resource_getrlimit__doc__, "getrlimit($module, resource, /)\n" "--\n" @@ -160,7 +164,11 @@ resource_getpagesize(PyObject *module, PyObject *Py_UNUSED(ignored)) return return_value; } +#ifndef RESOURCE_GETRUSAGE_METHODDEF + #define RESOURCE_GETRUSAGE_METHODDEF +#endif /* !defined(RESOURCE_GETRUSAGE_METHODDEF) */ + #ifndef RESOURCE_PRLIMIT_METHODDEF #define RESOURCE_PRLIMIT_METHODDEF #endif /* !defined(RESOURCE_PRLIMIT_METHODDEF) */ -/*[clinic end generated code: output=ad190fb33d647d1e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9ce1886c129eb2f3 input=a9049054013a1b77]*/ diff --git a/Modules/resource.c b/Modules/resource.c index 0d69c2983b4d18..d8bba2e39847a1 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -78,6 +78,7 @@ get_resource_state(PyObject *module) static struct PyModuleDef resourcemodule; +#ifdef HAVE_GETRUSAGE /*[clinic input] resource.getrusage @@ -134,6 +135,7 @@ resource_getrusage_impl(PyObject *module, int who) return result; } +#endif static int py2rlimit(PyObject *limits, struct rlimit *rl_out) diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 35b8e14e827112..4b7500aabded00 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1282,7 +1282,7 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) #endif /* getrusage(RUSAGE_SELF) */ -#if defined(HAVE_SYS_RESOURCE_H) +#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRUSAGE) struct rusage ru; if (getrusage(RUSAGE_SELF, &ru) == 0) { diff --git a/Tools/wasm/config.site-wasm32-emscripten b/Tools/wasm/config.site-wasm32-emscripten index 413506bbc9abd7..5c3dc4159f8771 100644 --- a/Tools/wasm/config.site-wasm32-emscripten +++ b/Tools/wasm/config.site-wasm32-emscripten @@ -66,6 +66,10 @@ ac_cv_func_pwritev=no ac_cv_func_pipe2=no ac_cv_func_nice=no ac_cv_func_setitimer=no +# unsupported syscall: __syscall_prlimit64 +ac_cv_func_prlimit=no +# unsupported syscall: __syscall_getrusage +ac_cv_func_getrusage=no # Syscalls that resulted in a segfault ac_cv_func_utimensat=no diff --git a/configure b/configure index 9097c0514b57ce..c4dce583599836 100755 --- a/configure +++ b/configure @@ -13751,7 +13751,7 @@ for ac_func in \ gai_strerror getegid getentropy geteuid getgid getgrgid getgrgid_r \ getgrnam_r getgrouplist getgroups getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ - getpwent getpwnam_r getpwuid_r getresgid getresuid getsid getspent \ + getpwent getpwnam_r getpwuid_r getresgid getresuid getrusage getsid getspent \ getspnam getuid getwd if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ diff --git a/configure.ac b/configure.ac index b613c18dbb3c58..fe729bf31d2576 100644 --- a/configure.ac +++ b/configure.ac @@ -4144,7 +4144,7 @@ AC_CHECK_FUNCS([ \ gai_strerror getegid getentropy geteuid getgid getgrgid getgrgid_r \ getgrnam_r getgrouplist getgroups getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ - getpwent getpwnam_r getpwuid_r getresgid getresuid getsid getspent \ + getpwent getpwnam_r getpwuid_r getresgid getresuid getrusage getsid getspent \ getspnam getuid getwd if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ diff --git a/pyconfig.h.in b/pyconfig.h.in index 02569c49543b60..a1bf9502e9268b 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -537,6 +537,9 @@ /* Define to 1 if you have the `getresuid' function. */ #undef HAVE_GETRESUID +/* Define to 1 if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + /* Define to 1 if you have the `getsid' function. */ #undef HAVE_GETSID From d9df26567ebc3f9b194102acbca31001a85d7486 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 31 Jan 2022 16:17:17 +0100 Subject: [PATCH 2/9] bpo-40280: Address more test failures on Emscripten --- Lib/test/test_builtin.py | 2 ++ Lib/test/test_fileio.py | 4 ++-- Lib/test/test_genericalias.py | 17 ++++++++++++----- Lib/test/test_getpass.py | 3 +++ Lib/test/test_inspect.py | 1 + Lib/test/test_interpreters.py | 3 ++- Lib/test/test_os.py | 4 ++++ Lib/test/test_posix.py | 9 +++++---- Lib/test/test_pwd.py | 6 +++++- Lib/test/test_pyexpat.py | 7 +++++-- Lib/test/test_zipfile.py | 8 +++++--- .../2022-01-31-15-15-08.bpo-40280.r1AYNW.rst | 7 +++++++ Modules/clinic/posixmodule.c.h | 6 +++--- Modules/posixmodule.c | 8 ++++---- Tools/wasm/config.site-wasm32-emscripten | 15 ++++++++++++--- 15 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index c6e67cc2910cf0..a601a524d6eb72 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -393,6 +393,7 @@ def test_compile_top_level_await_no_coro(self): msg=f"source={source} mode={mode}") + @unittest.skipIf(support.is_emscripten, "socket.accept is broken") def test_compile_top_level_await(self): """Test whether code some top level await can be compiled. @@ -1213,6 +1214,7 @@ def test_open_default_encoding(self): os.environ.clear() os.environ.update(old_environ) + @support.requires_subprocess() def test_open_non_inheritable(self): fileobj = open(__file__, encoding="utf-8") with fileobj: diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 4269b0e53f56df..e4984d3cd559e3 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -9,7 +9,7 @@ from weakref import proxy from functools import wraps -from test.support import cpython_only, swap_attr, gc_collect +from test.support import cpython_only, swap_attr, gc_collect, is_emscripten from test.support.os_helper import (TESTFN, TESTFN_UNICODE, make_bad_fd) from test.support.warnings_helper import check_warnings from collections import UserList @@ -373,7 +373,7 @@ def testAbles(self): self.assertEqual(f.isatty(), False) f.close() - if sys.platform != "win32": + if sys.platform != "win32" and not is_emscripten: try: f = self.FileIO("/dev/tty", "a") except OSError: diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 706cc5ea1af2f0..d311281c578a25 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -24,14 +24,20 @@ from fileinput import FileInput from itertools import chain from http.cookies import Morsel -from multiprocessing.managers import ValueProxy -from multiprocessing.pool import ApplyResult +try: + from multiprocessing.managers import ValueProxy + from multiprocessing.pool import ApplyResult + from multiprocessing.queues import SimpleQueue as MPSimpleQueue +except ImportError: + # _multiprocessing module is optional + ValueProxy = None + ApplyResult = None + MPSimpleQueue = None try: from multiprocessing.shared_memory import ShareableList except ImportError: # multiprocessing.shared_memory is not available on e.g. Android ShareableList = None -from multiprocessing.queues import SimpleQueue as MPSimpleQueue from os import DirEntry from re import Pattern, Match from types import GenericAlias, MappingProxyType, AsyncGeneratorType @@ -79,13 +85,14 @@ class BaseTest(unittest.TestCase): Queue, SimpleQueue, _AssertRaisesContext, SplitResult, ParseResult, - ValueProxy, ApplyResult, WeakSet, ReferenceType, ref, - ShareableList, MPSimpleQueue, + ShareableList, Future, _WorkItem, Morsel] if ctypes is not None: generic_types.extend((ctypes.Array, ctypes.LibraryLoader)) + if ValueProxy is not None: + generic_types.extend((ValueProxy, ApplyResult, MPSimpleQueue)) def test_subscriptable(self): for t in self.generic_types: diff --git a/Lib/test/test_getpass.py b/Lib/test/test_getpass.py index 3452e46213a76c..98ecec94336e32 100644 --- a/Lib/test/test_getpass.py +++ b/Lib/test/test_getpass.py @@ -28,6 +28,9 @@ def test_username_priorities_of_env_values(self, environ): getpass.getuser() except ImportError: # in case there's no pwd module pass + except KeyError: + # current user has no pwd entry + pass self.assertEqual( environ.get.call_args_list, [mock.call(x) for x in ('LOGNAME', 'USER', 'LNAME', 'USERNAME')]) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index a553431bdccfb2..29589a726768f5 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -788,6 +788,7 @@ def test_nested_class_definition_inside_function(self): self.assertSourceEqual(mod2.cls213, 218, 222) self.assertSourceEqual(mod2.cls213().func219(), 220, 221) + @unittest.skipIf(support.is_emscripten, "socket.accept is broken") def test_nested_class_definition_inside_async_function(self): import asyncio self.addCleanup(asyncio.set_event_loop_policy, None) diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index 6266aa7c33b329..48c7119fff1cfc 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -5,7 +5,8 @@ import unittest import time -import _xxsubinterpreters as _interpreters +from test.support import import_helper +_interpreters = import_helper.import_module('_xxsubinterpreters') from test.support import interpreters diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 84c27f346c3409..4ed19c23f51aaa 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -992,6 +992,7 @@ def _empty_mapping(self): @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") + @support.requires_subprocess() def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") @@ -1002,6 +1003,7 @@ def test_update2(self): @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), 'requires a shell') @unittest.skipUnless(hasattr(os, 'popen'), "needs os.popen()") + @support.requires_subprocess() def test_os_popen_iter(self): with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" % unix_shell) as popen: @@ -1173,6 +1175,8 @@ def test_iter_error_when_changing_os_environ_values(self): def _test_underlying_process_env(self, var, expected): if not (unix_shell and os.path.exists(unix_shell)): return + if not support.has_subprocess_support: + return with os.popen(f"{unix_shell} -c 'echo ${var}'") as popen: value = popen.read().strip() diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 974edd766cc809..5cc04fd46dddb9 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -184,7 +184,7 @@ def test_truncate(self): posix.truncate(os_helper.TESTFN, 0) @unittest.skipUnless(getattr(os, 'execve', None) in os.supports_fd, "test needs execve() to support the fd parameter") - @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") + @support.requires_fork() def test_fexecve(self): fp = os.open(sys.executable, os.O_RDONLY) try: @@ -199,7 +199,7 @@ def test_fexecve(self): @unittest.skipUnless(hasattr(posix, 'waitid'), "test needs posix.waitid()") - @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") + @support.requires_fork() def test_waitid(self): pid = os.fork() if pid == 0: @@ -209,7 +209,7 @@ def test_waitid(self): res = posix.waitid(posix.P_PID, pid, posix.WEXITED) self.assertEqual(pid, res.si_pid) - @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") + @support.requires_fork() def test_register_at_fork(self): with self.assertRaises(TypeError, msg="Positional args not allowed"): os.register_at_fork(lambda: None) @@ -1056,6 +1056,7 @@ def test_getgrouplist(self): @unittest.skipUnless(hasattr(os, 'getegid'), "test needs os.getegid()") @unittest.skipUnless(hasattr(os, 'popen'), "test needs os.popen()") + @support.requires_subprocess() def test_getgroups(self): with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() @@ -1481,7 +1482,7 @@ def test_unlink_dir_fd(self): self.addCleanup(posix.unlink, fullname) raise - @unittest.skipUnless(os.mkfifo in os.supports_dir_fd, "test needs dir_fd support in os.mkfifo()") + @unittest.skipUnless(hasattr(os, 'mkfifo') and os.mkfifo in os.supports_dir_fd, "test needs dir_fd support in os.mkfifo()") def test_mkfifo_dir_fd(self): with self.prepare() as (dir_fd, name, fullname): try: diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py index f8f12571ca90e0..294b2a9090b349 100644 --- a/Lib/test/test_pwd.py +++ b/Lib/test/test_pwd.py @@ -69,7 +69,11 @@ def test_errors(self): allnames = list(bynames.keys()) namei = 0 - fakename = allnames[namei] + if allnames: + fakename = allnames[namei] + else: + # empty user db + fakename = "invaliduser" while fakename in bynames: chars = list(fakename) for i in range(len(chars)): diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index b2b4dea060532d..6e578458a25096 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -12,7 +12,7 @@ from xml.parsers import expat from xml.parsers.expat import errors -from test.support import sortdict +from test.support import sortdict, is_emscripten class SetAttributeTest(unittest.TestCase): @@ -466,7 +466,10 @@ def test_exception(self): "pyexpat.c", "StartElement") self.check_traceback_entry(entries[2], "test_pyexpat.py", "StartElementHandler") - if sysconfig.is_python_build() and not (sys.platform == 'win32' and platform.machine() == 'ARM'): + if (sysconfig.is_python_build() + and not (sys.platform == 'win32' and platform.machine() == 'ARM') + and not is_emscripten + ): self.assertIn('call_with_frame("StartElement"', entries[1][3]) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index e226dd741d7a75..de2dd33f436609 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -22,7 +22,9 @@ from test.support import script_helper from test.support import (findfile, requires_zlib, requires_bz2, requires_lzma, captured_stdout, requires_subprocess) -from test.support.os_helper import TESTFN, unlink, rmtree, temp_dir, temp_cwd +from test.support.os_helper import ( + TESTFN, unlink, rmtree, temp_dir, temp_cwd, fd_count +) TESTFN2 = TESTFN + "2" @@ -2539,14 +2541,14 @@ def test_write_after_read(self): def test_many_opens(self): # Verify that read() and open() promptly close the file descriptor, # and don't rely on the garbage collector to free resources. + startcount = fd_count() self.make_test_archive(TESTFN2) with zipfile.ZipFile(TESTFN2, mode="r") as zipf: for x in range(100): zipf.read('ones') with zipf.open('ones') as zopen1: pass - with open(os.devnull, "rb") as f: - self.assertLess(f.fileno(), 100) + self.assertEqual(startcount, fd_count()) def test_write_while_reading(self): with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: diff --git a/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst new file mode 100644 index 00000000000000..d5e7bb0d68343c --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst @@ -0,0 +1,7 @@ +Fix wasm32-emscripten test failures and platform issues. +- Disable syscalls that are not supported or don't work, e.g. + wait, getrusage, prlimit, mkfifo, mknod, setres[gu]id, setgroups. +- Use fd_count to cound open fds. +- Add more checks for subprocess and fork. +- Add workarounds for missing _multiprocessing and failing socket.accept() +- Enable bzip2 again diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 282a5410f70206..28d79a80bd0d5d 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -5920,7 +5920,7 @@ os_mkfifo(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k #endif /* defined(HAVE_MKFIFO) */ -#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) +#if defined(HAVE_MKNOD) PyDoc_STRVAR(os_mknod__doc__, "mknod($module, /, path, mode=384, device=0, *, dir_fd=None)\n" @@ -6004,7 +6004,7 @@ os_mknod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw return return_value; } -#endif /* (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) */ +#endif /* defined(HAVE_MKNOD) */ #if defined(HAVE_DEVICE_MACROS) @@ -9303,4 +9303,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=d95ba7b0b9c52685 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=817f2f2b53ef93b2 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 7b5c3ef575565c..5234b0dae8ea0b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -881,7 +881,7 @@ _Py_Gid_Converter(PyObject *obj, gid_t *p) #define _PyLong_FromDev PyLong_FromLongLong -#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) +#if defined(HAVE_MKNOD) || defined(HAVE_DEVICE_MACROS) static int _Py_Dev_Converter(PyObject *obj, void *p) { @@ -890,7 +890,7 @@ _Py_Dev_Converter(PyObject *obj, void *p) return 0; return 1; } -#endif /* HAVE_MKNOD && HAVE_MAKEDEV */ +#endif /* HAVE_MKNOD || HAVE_DEVICE_MACROS */ #ifdef AT_FDCWD @@ -10625,7 +10625,7 @@ os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd) #endif /* HAVE_MKFIFO */ -#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) +#if defined(HAVE_MKNOD) /*[clinic input] os.mknod @@ -10674,7 +10674,7 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device, Py_RETURN_NONE; } -#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */ +#endif /* defined(HAVE_MKNOD) */ #ifdef HAVE_DEVICE_MACROS diff --git a/Tools/wasm/config.site-wasm32-emscripten b/Tools/wasm/config.site-wasm32-emscripten index 5c3dc4159f8771..f15cf454f5f17c 100644 --- a/Tools/wasm/config.site-wasm32-emscripten +++ b/Tools/wasm/config.site-wasm32-emscripten @@ -27,9 +27,6 @@ ac_cv_func_prlimit=no # unsupported syscall, https://github.com/emscripten-core/emscripten/issues/13393 ac_cv_func_shutdown=no -# breaks build, see https://github.com/ethanhs/python-wasm/issues/16 -ac_cv_lib_bz2_BZ2_bzCompress=no - # clock_nanosleep() causes time.sleep() to sleep forever. # nanosleep() works correctly ac_cv_func_clock_nanosleep=no @@ -70,6 +67,7 @@ ac_cv_func_setitimer=no ac_cv_func_prlimit=no # unsupported syscall: __syscall_getrusage ac_cv_func_getrusage=no +ac_cv_func_posix_fallocate=no # Syscalls that resulted in a segfault ac_cv_func_utimensat=no @@ -82,6 +80,17 @@ ac_cv_header_sys_ioctl_h=no ac_cv_func_openpty=no ac_cv_func_forkpty=no +# mkfifo and mknod are broken, create regular file +ac_cv_func_mkfifo=no +ac_cv_func_mkfifoat=no +ac_cv_func_mknod=no +ac_cv_func_mknodat=no + +# always fails with permission error +ac_cv_func_setgroups=no +ac_cv_func_setresuid=no +ac_cv_func_setresgid=no + # To use dlopen, you need to use Emscripten's linking support, # see https://github.com/emscripten-core/emscripten/wiki/Linking) ac_cv_func_dlopen=no From fcbc97adc810ed3704dbbfe8077ff744460162eb Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Feb 2022 11:26:57 +0100 Subject: [PATCH 3/9] disable large file support --- .../2022-01-31-15-15-08.bpo-40280.r1AYNW.rst | 1 + configure | 16 ++++++++++++++++ configure.ac | 13 +++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst index d5e7bb0d68343c..bb901568500d5a 100644 --- a/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst +++ b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst @@ -5,3 +5,4 @@ Fix wasm32-emscripten test failures and platform issues. - Add more checks for subprocess and fork. - Add workarounds for missing _multiprocessing and failing socket.accept() - Enable bzip2 again +- Disable large file support diff --git a/configure b/configure index c4dce583599836..d2bf7ba59e0079 100755 --- a/configure +++ b/configure @@ -9844,14 +9844,30 @@ _ACEOF $as_echo_n "checking whether to enable large file support... " >&6; } if test "$ac_cv_sizeof_off_t" -gt "$ac_cv_sizeof_long" -a \ "$ac_cv_sizeof_long_long" -ge "$ac_cv_sizeof_off_t"; then + have_largefile_support="yes" +else + have_largefile_support=no +fi +case $ac_sys_system in #( + Emscripten) : + have_largefile_support="no" + ;; #( + *) : + ;; +esac +if test "x$have_largefile_support" = xyes; then : + $as_echo "#define HAVE_LARGEFILE_SUPPORT 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } + fi # The cast to long int works around a bug in the HP C Compiler diff --git a/configure.ac b/configure.ac index fe729bf31d2576..83dd854248b428 100644 --- a/configure.ac +++ b/configure.ac @@ -2554,15 +2554,24 @@ AC_CHECK_SIZEOF(off_t, [], [ AC_MSG_CHECKING(whether to enable large file support) if test "$ac_cv_sizeof_off_t" -gt "$ac_cv_sizeof_long" -a \ "$ac_cv_sizeof_long_long" -ge "$ac_cv_sizeof_off_t"; then + have_largefile_support="yes" +else + have_largefile_support="no" +fi +dnl LFS does not work with Emscripten 3.1 +AS_CASE([$ac_sys_system], + [Emscripten], [have_largefile_support="no"] +) +AS_VAR_IF([have_largefile_support], [yes], [ AC_DEFINE(HAVE_LARGEFILE_SUPPORT, 1, [Defined to enable large file support when an off_t is bigger than a long and long long is at least as big as an off_t. You may need to add some flags for configuration and compilation to enable this mode. (For Solaris and Linux, the necessary defines are already defined.)]) AC_MSG_RESULT(yes) -else +], [ AC_MSG_RESULT(no) -fi +]) AC_CHECK_SIZEOF(time_t, [], [ #ifdef HAVE_SYS_TYPES_H From 0f8a37ce2d5aacad6525406caac126e7d856e5dc Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Feb 2022 12:02:12 +0100 Subject: [PATCH 4/9] Disable signal.alarm --- Lib/test/test_io.py | 13 +++++++++++++ .../Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst | 1 + Tools/wasm/config.site-wasm32-emscripten | 3 +++ 3 files changed, 17 insertions(+) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index a10611abb13f44..e9abd153a3e8cf 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -76,6 +76,10 @@ def _default_chunk_size(): with open(__file__, "r", encoding="latin-1") as f: return f._CHUNK_SIZE +requires_alarm = unittest.skipUnless( + hasattr(signal, "alarm"), "test requires signal.alarm()" +) + class MockRawIOWithoutRead: """A RawIO implementation without read(), so as to exercise the default @@ -4435,12 +4439,15 @@ def _read(): if e.errno != errno.EBADF: raise + @requires_alarm def test_interrupted_write_unbuffered(self): self.check_interrupted_write(b"xy", b"xy", mode="wb", buffering=0) + @requires_alarm def test_interrupted_write_buffered(self): self.check_interrupted_write(b"xy", b"xy", mode="wb") + @requires_alarm def test_interrupted_write_text(self): self.check_interrupted_write("xy", b"xy", mode="w", encoding="ascii") @@ -4472,9 +4479,11 @@ def on_alarm(*args): wio.close() os.close(r) + @requires_alarm def test_reentrant_write_buffered(self): self.check_reentrant_write(b"xy", mode="wb") + @requires_alarm def test_reentrant_write_text(self): self.check_reentrant_write("xy", mode="w", encoding="ascii") @@ -4502,10 +4511,12 @@ def alarm_handler(sig, frame): os.close(w) os.close(r) + @requires_alarm def test_interrupted_read_retry_buffered(self): self.check_interrupted_read_retry(lambda x: x.decode('latin1'), mode="rb") + @requires_alarm def test_interrupted_read_retry_text(self): self.check_interrupted_read_retry(lambda x: x, mode="r", encoding="latin1") @@ -4578,9 +4589,11 @@ def alarm2(sig, frame): if e.errno != errno.EBADF: raise + @requires_alarm def test_interrupted_write_retry_buffered(self): self.check_interrupted_write_retry(b"x", mode="wb") + @requires_alarm def test_interrupted_write_retry_text(self): self.check_interrupted_write_retry("x", mode="w", encoding="latin1") diff --git a/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst index bb901568500d5a..7617aa058788a6 100644 --- a/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst +++ b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst @@ -6,3 +6,4 @@ Fix wasm32-emscripten test failures and platform issues. - Add workarounds for missing _multiprocessing and failing socket.accept() - Enable bzip2 again - Disable large file support +- Disable signal.alarm for now diff --git a/Tools/wasm/config.site-wasm32-emscripten b/Tools/wasm/config.site-wasm32-emscripten index f15cf454f5f17c..9a620544a9bbf9 100644 --- a/Tools/wasm/config.site-wasm32-emscripten +++ b/Tools/wasm/config.site-wasm32-emscripten @@ -91,6 +91,9 @@ ac_cv_func_setgroups=no ac_cv_func_setresuid=no ac_cv_func_setresgid=no +# alarm signal is not delivered, may need a callback into the event loop? +ac_cv_func_alarm=no + # To use dlopen, you need to use Emscripten's linking support, # see https://github.com/emscripten-core/emscripten/wiki/Linking) ac_cv_func_dlopen=no From ece8f4c1cd5cf25073bfee5e6f91c021f27b7b13 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Feb 2022 12:08:21 +0100 Subject: [PATCH 5/9] Revert feature check for mknod --- Modules/clinic/posixmodule.c.h | 6 +++--- Modules/posixmodule.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 28d79a80bd0d5d..282a5410f70206 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -5920,7 +5920,7 @@ os_mkfifo(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k #endif /* defined(HAVE_MKFIFO) */ -#if defined(HAVE_MKNOD) +#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) PyDoc_STRVAR(os_mknod__doc__, "mknod($module, /, path, mode=384, device=0, *, dir_fd=None)\n" @@ -6004,7 +6004,7 @@ os_mknod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw return return_value; } -#endif /* defined(HAVE_MKNOD) */ +#endif /* (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) */ #if defined(HAVE_DEVICE_MACROS) @@ -9303,4 +9303,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=817f2f2b53ef93b2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d95ba7b0b9c52685 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5234b0dae8ea0b..ea0435d7d52e69 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -881,7 +881,7 @@ _Py_Gid_Converter(PyObject *obj, gid_t *p) #define _PyLong_FromDev PyLong_FromLongLong -#if defined(HAVE_MKNOD) || defined(HAVE_DEVICE_MACROS) +#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS) static int _Py_Dev_Converter(PyObject *obj, void *p) { @@ -890,7 +890,7 @@ _Py_Dev_Converter(PyObject *obj, void *p) return 0; return 1; } -#endif /* HAVE_MKNOD || HAVE_DEVICE_MACROS */ +#endif /* (HAVE_MKNOD && HAVE_MAKEDEV) || HAVE_DEVICE_MACROS */ #ifdef AT_FDCWD @@ -10625,7 +10625,7 @@ os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd) #endif /* HAVE_MKFIFO */ -#if defined(HAVE_MKNOD) +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) /*[clinic input] os.mknod @@ -10674,7 +10674,7 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device, Py_RETURN_NONE; } -#endif /* defined(HAVE_MKNOD) */ +#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */ #ifdef HAVE_DEVICE_MACROS From dd5be39a551164c526388673d30d145f3f25fd1d Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Feb 2022 12:26:37 +0100 Subject: [PATCH 6/9] Add missing quotes --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index d2bf7ba59e0079..49d5abeac3024d 100755 --- a/configure +++ b/configure @@ -9846,7 +9846,7 @@ if test "$ac_cv_sizeof_off_t" -gt "$ac_cv_sizeof_long" -a \ "$ac_cv_sizeof_long_long" -ge "$ac_cv_sizeof_off_t"; then have_largefile_support="yes" else - have_largefile_support=no + have_largefile_support="no" fi case $ac_sys_system in #( Emscripten) : From 8e39ccdf2319173e671935f39803f14c543d1e90 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Feb 2022 17:01:21 +0100 Subject: [PATCH 7/9] List limitation and known issues --- Tools/wasm/README.md | 101 ++++++++++++++++++----- Tools/wasm/config.site-wasm32-emscripten | 2 +- 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index f59b876b11a74d..d973255ad8e5b5 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -1,7 +1,11 @@ # Python WebAssembly (WASM) build +**WARNING: WASM support is highly experimental! Lots of features are not working yet.** + This directory contains configuration and helpers to facilitate cross -compilation of CPython to WebAssembly (WASM). +compilation of CPython to WebAssembly (WASM). For now we support +*wasm32-emscripten* builds for modern browser and for *Node.js*. It's not +possible to build for *wasm32-wasi* out-of-the-box yet. ## wasm32-emscripten build @@ -22,16 +26,14 @@ popd ### Fetch and build additional emscripten ports ```shell -embuilder build zlib +embuilder build zlib bzip2 ``` -### Cross compile to wasm32-emscripten - -For browser: +### Cross compile to wasm32-emscripten for browser ```shell -mkdir -p builddir/emscripten -pushd builddir/emscripten +mkdir -p builddir/emscripten-browser +pushd builddir/emscripten-browser CONFIG_SITE=../../Tools/wasm/config.site-wasm32-emscripten \ emconfigure ../../configure -C \ @@ -41,11 +43,27 @@ CONFIG_SITE=../../Tools/wasm/config.site-wasm32-emscripten \ --with-build-python=$(pwd)/../build/python emmake make -j$(nproc) +popd +``` + +Serve `python.html` with a local webserver and open the file in a browser. + +```shell +emrun builddir/emscripten-browser/python.html +``` + +or + +```shell +python3 -m http.server ``` -For node: +### Cross compile to wasm32-emscripten for node ``` +mkdir -p builddir/emscripten-node +pushd builddir/emscripten-node + CONFIG_SITE=../../Tools/wasm/config.site-wasm32-emscripten \ emconfigure ../../configure -C \ --host=wasm32-unknown-emscripten \ @@ -54,18 +72,63 @@ CONFIG_SITE=../../Tools/wasm/config.site-wasm32-emscripten \ --with-build-python=$(pwd)/../build/python emmake make -j$(nproc) +popd ``` -### Test in browser - -Serve `python.html` with a local webserver and open the file in a browser. - -```shell -emrun python.html ``` - -or - -```shell -python3 -m http.server +node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscripten-node/python.js ``` + +## wasm32-emscripten limitations and issues + +- Most stdlib modules with dependency on external libraries are missing: + ``ctypes``, ``readline``, ``sqlite3``, ``ssl``, and more. +- Shared extension modules are not implemented yet. All extension modules + are statically linked into the main binary. +- Processes are not supported. Calls like fork, popen, and subprocess + fail with ``ENOSYS`` or ``ENOSUP``. +- Blocking sockets are not available. Non-blocking sockets don't work + correctly, e.g. ``socket.accept`` crashes the runtime. ``gethostbyname`` + does not resolve to a real IP address. IPv6 is not available. +- The ``select`` module is limited. ``select.select()`` crashes the runtime + due to lack of exectfd support. +- The ``*at`` variants of functions (e.g. ``openat``) are not available. + The ``dir_fd`` argument of *os* module functions can't be used. +- Signal support is limited. ``signal.alarm``, ``itimer``, ``sigaction`` + are not available or do not work correctly. ``SIGTERM`` exits the runtime. +- Most user, group, and permission related function and modules are not + supported or don't work as expected, e.g.``pwd`` module, ``grp`` module, + ``os.setgroups``, ``os.chown``, and so on. +- Offset and iovec I/O functions (e.g. ``os.pread``, ``os.preadv``) are not + available. +- ``os.mknod`` and ``os.mkfifo`` don't work and are disabled. +- Large file support crashes the runtime and is disabled. +- ``mmap`` module is unstable. flush (``msync``) can crash the runtime. +- Resource-related functions like ``os.nice`` and most functions of the + ``resource`` module are not available. +- Some time and datetime features are broken. ``strftime`` and ``strptime`` + have known bugs. Extended glibc formatting features are not available. +- ``locales`` module is affected by musl libc issues. +- ``uuid`` module is affected by memory leak and crasher in + Emscripten's ``freeaddrinfo``. +- Recursive ``glob`` leaks file descriptors. +- Python's object allocator ``obmalloc`` is disabled by default. +- ``ensurepip`` is not available. + +### wasm32-emscripten in browsers + +- The bundles stdlib is limited. Network-related modules, + distutils, multiprocessing, dbm, tests and similar modules + are not shipped. All other modules are bundled as pre-compiled + ``pyc`` files. +- Threading is not supported. + +### wasm32-emscripten in node + +Node builds use ``NODERAWFS``, ``USE_PTHREADS`` and ``PROXY_TO_PTHREAD`` +linker option. + +- Node RawFS allows direct access to the hosts file system. +- pthread support requires WASM threads and SharedArrayBuffer (bulk memory). + The runtime keeps a pool of web workers around. Each web worker uses + several file descriptors (eventfd, epoll, pipe). diff --git a/Tools/wasm/config.site-wasm32-emscripten b/Tools/wasm/config.site-wasm32-emscripten index 9a620544a9bbf9..98991b462446f5 100644 --- a/Tools/wasm/config.site-wasm32-emscripten +++ b/Tools/wasm/config.site-wasm32-emscripten @@ -95,5 +95,5 @@ ac_cv_func_setresgid=no ac_cv_func_alarm=no # To use dlopen, you need to use Emscripten's linking support, -# see https://github.com/emscripten-core/emscripten/wiki/Linking) +# see https://emscripten.org/docs/compiling/Dynamic-Linking.html ac_cv_func_dlopen=no From d92424742c2e7d7753590c5aa48d1aec5c8b1e3f Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 5 Feb 2022 15:53:28 +0200 Subject: [PATCH 8/9] Apply Brett's suggestions Co-authored-by: Brett Cannon --- Lib/test/support/__init__.py | 2 +- Lib/test/test_os.py | 2 +- Lib/test/test_pwd.py | 6 +----- .../Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst | 8 ++++---- Tools/wasm/README.md | 12 ++++++------ 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 5f9a03de83e131..9d0d3d9c9b6193 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1278,7 +1278,7 @@ def reap_children(): # Need os.waitpid(-1, os.WNOHANG): Windows is not supported if not (hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG')): return - if not has_subprocess_support: + elif not has_subprocess_support: return # Reap all our dead child processes so we don't leave zombies around. diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 4ed19c23f51aaa..660691579c1633 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1175,7 +1175,7 @@ def test_iter_error_when_changing_os_environ_values(self): def _test_underlying_process_env(self, var, expected): if not (unix_shell and os.path.exists(unix_shell)): return - if not support.has_subprocess_support: + elif not support.has_subprocess_support: return with os.popen(f"{unix_shell} -c 'echo ${var}'") as popen: diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py index 294b2a9090b349..c789326425be32 100644 --- a/Lib/test/test_pwd.py +++ b/Lib/test/test_pwd.py @@ -69,11 +69,7 @@ def test_errors(self): allnames = list(bynames.keys()) namei = 0 - if allnames: - fakename = allnames[namei] - else: - # empty user db - fakename = "invaliduser" + fakename = allnames[namei] if allnames else "invaliduser" while fakename in bynames: chars = list(fakename) for i in range(len(chars)): diff --git a/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst index 7617aa058788a6..bb4878c6b0ac2e 100644 --- a/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst +++ b/Misc/NEWS.d/next/Build/2022-01-31-15-15-08.bpo-40280.r1AYNW.rst @@ -3,7 +3,7 @@ Fix wasm32-emscripten test failures and platform issues. wait, getrusage, prlimit, mkfifo, mknod, setres[gu]id, setgroups. - Use fd_count to cound open fds. - Add more checks for subprocess and fork. -- Add workarounds for missing _multiprocessing and failing socket.accept() -- Enable bzip2 again -- Disable large file support -- Disable signal.alarm for now +- Add workarounds for missing _multiprocessing and failing socket.accept(). +- Enable bzip2. +- Disable large file support. +- Disable signal.alarm. diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index d973255ad8e5b5..d445745c7748c1 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -81,13 +81,13 @@ node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscri ## wasm32-emscripten limitations and issues -- Most stdlib modules with dependency on external libraries are missing: +- Most stdlib modules with a dependency on external libraries are missing: ``ctypes``, ``readline``, ``sqlite3``, ``ssl``, and more. - Shared extension modules are not implemented yet. All extension modules are statically linked into the main binary. -- Processes are not supported. Calls like fork, popen, and subprocess +- Processes are not supported. System calls like fork, popen, and subprocess fail with ``ENOSYS`` or ``ENOSUP``. -- Blocking sockets are not available. Non-blocking sockets don't work +- Blocking sockets are not available and non-blocking sockets don't work correctly, e.g. ``socket.accept`` crashes the runtime. ``gethostbyname`` does not resolve to a real IP address. IPv6 is not available. - The ``select`` module is limited. ``select.select()`` crashes the runtime @@ -117,7 +117,7 @@ node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscri ### wasm32-emscripten in browsers -- The bundles stdlib is limited. Network-related modules, +- The bundled stdlib is limited. Network-related modules, distutils, multiprocessing, dbm, tests and similar modules are not shipped. All other modules are bundled as pre-compiled ``pyc`` files. @@ -126,9 +126,9 @@ node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscri ### wasm32-emscripten in node Node builds use ``NODERAWFS``, ``USE_PTHREADS`` and ``PROXY_TO_PTHREAD`` -linker option. +linker options. -- Node RawFS allows direct access to the hosts file system. +- Node RawFS allows direct access to the host file system. - pthread support requires WASM threads and SharedArrayBuffer (bulk memory). The runtime keeps a pool of web workers around. Each web worker uses several file descriptors (eventfd, epoll, pipe). From 5f29e63f6a29cc0d279766f4039fe374c5ff6f69 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 5 Feb 2022 15:13:36 +0100 Subject: [PATCH 9/9] Add links to bugs --- Tools/wasm/README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md index d445745c7748c1..1cdaa4efffb893 100644 --- a/Tools/wasm/README.md +++ b/Tools/wasm/README.md @@ -101,16 +101,23 @@ node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscri ``os.setgroups``, ``os.chown``, and so on. - Offset and iovec I/O functions (e.g. ``os.pread``, ``os.preadv``) are not available. -- ``os.mknod`` and ``os.mkfifo`` don't work and are disabled. +- ``os.mknod`` and ``os.mkfifo`` + [don't work](https://github.com/emscripten-core/emscripten/issues/16158) + and are disabled. - Large file support crashes the runtime and is disabled. - ``mmap`` module is unstable. flush (``msync``) can crash the runtime. - Resource-related functions like ``os.nice`` and most functions of the ``resource`` module are not available. - Some time and datetime features are broken. ``strftime`` and ``strptime`` - have known bugs. Extended glibc formatting features are not available. -- ``locales`` module is affected by musl libc issues. -- ``uuid`` module is affected by memory leak and crasher in - Emscripten's ``freeaddrinfo``. + have known bugs, e.g. + [%% quoting](https://github.com/emscripten-core/emscripten/issues/16155), + [%U off-by-one](https://github.com/emscripten-core/emscripten/issues/16156). + Extended glibc formatting features are not available. +- ``locales`` module is affected by musl libc issues, + [bpo-46390](https://bugs.python.org/issue46390). +- ``uuid`` module is affected by + [memory leak](https://github.com/emscripten-core/emscripten/issues/16081) + and crasher in Emscripten's ``freeaddrinfo``, - Recursive ``glob`` leaks file descriptors. - Python's object allocator ``obmalloc`` is disabled by default. - ``ensurepip`` is not available.