include ../build/Makefile-vars
#
# We build a native version of CPython and a WASM version.
# The native version is ONLY used as part of the cross compilation build process
# and not used for anything else. TODO: Currently, we can't build native Python using
# only zig on our supported architectures, so we use the native toolchain, which is
# really annoying and makes bootstraping a build a little harder.
#
# See https://www.python.org/downloads/
PYTHON_MAJOR = 3
PYTHON_MINOR = 11
PYTHON_PATCH = 2
PYTHON_BETA =
PYTHON_VERSION = ${PYTHON_MAJOR}.${PYTHON_MINOR}.${PYTHON_PATCH}
URL = https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}${PYTHON_BETA}.tar.xz
TARBALL = ${UPSTREAM}/python-${PYTHON_VERSION}${PYTHON_BETA}.tar.xz
ZLIB_NATIVE = ${PACKAGES}/zlib-native/dist/native
LIBEDIT_NATIVE = ${PACKAGES}/libedit-native/dist/native
POSIX_WASM = $(shell cowasm-package-path @cowasm/posix-wasm)
LIBEDIT_WASM = $(shell cowasm-package-path @cowasm/libedit)
LZMA_WASM = $(shell cowasm-package-path @cowasm/lzma)
NCURSES_WASM = $(shell cowasm-package-path @cowasm/ncurses)
TERMCAP_WASM = $(shell cowasm-package-path @cowasm/termcap)
OPENSSL_WASM = $(shell cowasm-package-path @cowasm/openssl)
SQLITE_WASM = $(shell cowasm-package-path @cowasm/sqlite)
BZIP2_WASM = $(shell cowasm-package-path @cowasm/bzip2)
ZLIB_WASM = $(shell cowasm-package-path @cowasm/zlib)
LIBFFI_WASM = $(shell cowasm-package-path @cowasm/libffi)
PYTHON_WASM = ${BIN}/python-wasm
# Either -Oz or -O3 make sense to me...
# -O0 = exceeds a limit so can't even load the result with webassembly!
# The cPython default opt is "-DNDEBUG -g -fwrapv -O3 -Wall", which is
# coded in the generated Makefile. I have noticed some major compiler bugs
# with -O3, so we can revisit this later with newer LLVM/zig releases.
# For example:
# https://github.com/sagemathinc/cowasm/issues/27
# is a bug with -O3 that goes away with -Oz. The diff in performance
# is less than 5% with -O3 over -Oz.
# NDEBUG I think disable assertions (?).
# Use this instead for better debugging (?).
WASM_OPT=-DNDEBUG -fwrapv -Wall -Oz -g
#WASM_OPT=-DNDEBUG -fwrapv -Wall -Oz
# Default target
all: deps native ${BIN}/python-native wasm ${BIN}/python-wasm
${DIST}/.built: native wasm
touch ${DIST}/.built
include ../build/Makefile-rules
## NATIVE
${BUILD_NATIVE}/.build:: ${TARBALL}
cp src/Setup.local-native ${BUILD_NATIVE}/Modules/Setup.local
# This _scproxy is needed macos *only* for pip; on Linux it breaks the build.
if [ `uname -s` == "Darwin" ]; then echo "_scproxy _scproxy.c" >> ${BUILD_NATIVE}/Modules/Setup.local; fi
${DIST_NATIVE}/.built: ${BUILD_NATIVE}/.build
cd ../build && make zig
cd ../lzma-native && make
cd ../zlib-native && make
cd ../termcap-native && make
cd ../libedit-native && make
cd ${BUILD_NATIVE} \
&& AR="zig ar" \
CXX="zig c++ ${ZIG_NATIVE_CFLAGS}" \
CC="zig cc ${ZIG_NATIVE_CFLAGS}" \
CFLAGS="-I${ZLIB_NATIVE}/include -I${LIBEDIT_NATIVE}/include" \
LDFLAGS="-L${ZLIB_NATIVE}/lib -L${LIBEDIT_NATIVE}" \
./configure \
--prefix=${DIST_NATIVE} \
--with-ensurepip \
--disable-shared \
--with-readline=editline
# On **some** macOS systems having either HAVE_MKFIFOAT or HAVE_MKNODAT breaks
# the build due to I guess bugs with zig and __builtin_available. We don't need
# these anywhere, since native cpython is just for bootstrapping our WASM build anyways.
# NOTE: I didn't hit this with 3.11.0.rc2, but did with the actual release.
echo "#undef HAVE_MKFIFOAT" >> ${BUILD_NATIVE}/pyconfig.h
echo "#undef HAVE_MKNODAT" >> ${BUILD_NATIVE}/pyconfig.h
# We **must** explicitly set RUNSHARED on MacOS since it's wrong there (e.g., it misses
# zlib). This is to workaround a security feature of MacOS (see
# https://developer.apple.com/forums/thread/9233). For simplicity we set this on all hosts.
cd ${BUILD_NATIVE} \
&& make -j8 RUNSHARED="DYLD_LIBRARY_PATH=${BUILD_NATIVE}:${DYLD_LIBRARY_PATH} LD_LIBRARY_PATH=${BUILD_NATIVE}:${LD_LIBRARY_PATH}" \
&& make RUNSHARED="DYLD_LIBRARY_PATH=${BUILD_NATIVE}:${DYLD_LIBRARY_PATH} LD_LIBRARY_PATH=${BUILD_NATIVE}:${LD_LIBRARY_PATH}" install
cp ${SRC}/cowasm_bundler.py ${DIST_NATIVE}/lib/python3.11/site-packages
touch ${DIST_NATIVE}/.built
${BIN}/python-native: bin/python-native ${DIST_NATIVE}/.built
rm -f ${BIN}/python-native
ln -s ${CWD}/bin/python-native ${BIN}/python-native
touch ${BIN}/python-native
# Use "make run-native" to run native python at the command line.
.PHONY: run-native
run-native: ${DIST_NATIVE}/.built ${BIN}/python-native
${BIN}/python-native
## WASM
${BUILD_WASM}/.patched: ${BUILD_WASM}/.build
# Create declarations that make everything extension modules need
# accessible from outside our "python.wasm as a shared library".
pnpm dlx dylink ${BUILD_WASM} > ${BUILD_WASM}/Programs/libpython.c
# Scripts to make it easy to change CPython and rebuild iteratively
ln -sf ${SRC}/rebuild ${BUILD_WASM}/rebuild
# Copy the config.site, which answers questions needed for
# cross compiling, without which ./configure won't work.
cp src/config.site ${BUILD_WASM}
# Configure how modules are built
cp src/Setup.local ${BUILD_WASM}/Modules/Setup.local
# Custom code to run at startup
cp src/sitecustomize.py ${BUILD_WASM}/Lib
# Copy over our small cowasm-specific library with functionality for our packages.
cp ${SRC}/cowasm_bundler.py ${BUILD_WASM}/Lib
cp ${SRC}/cowasm_importer.py ${BUILD_WASM}/Lib
# Make empty sys/wait.h so that python's configure will conclude that we have sys/wait.h; we will
# explicitly add anything that is really used from there to posix-wasm.h
mkdir ${BUILD_WASM}/sys
echo '#include "posix-wasm.h"' > ${BUILD_WASM}/sys/wait.h
# Apply our patches:
cd ${BUILD_WASM} && cat ${SRC}/patches/01-main.patch | patch -p1
cd ${BUILD_WASM} && cat ${SRC}/patches/02-pydoc.patch | patch -p1
cd ${BUILD_WASM} && cat ${SRC}/patches/03-wasm-assets.patch | patch -p1
# Apply this patch when we want to take testing to the next level, and fully enable all
# tests that involve subprocess support. Our goal is to support subprocesses fully, but
# it is NOT to support fork, which is just not possible in general with WebAssembly.
# However, I believe subprocess support is possible in a fully solid robust way.
cd ${BUILD_WASM} && cat ${SRC}/patches/04-enable-subprocess-tests.patch | patch -p1
cd ${BUILD_WASM} && cat ${SRC}/patches/05-st_mode.patch | patch -p1
# Implement 'import platform; platform.architecture()' for wasm.
cd ${BUILD_WASM} && cat ${SRC}/patches/06-platform.patch | patch -p1
# Fork and execv on top of posix-node work, but it takes some restrictions to be robust:
cd ${BUILD_WASM} && cat ${SRC}/patches/07-subprocess.patch | patch -p1
cd ${BUILD_WASM} && cat ${SRC}/patches/09-set-inheritable.patch | patch -p1
# Patch 10-setuptools-c++.patch is used, but only *AFTER* ensurepip happens!
# clang15 is more strict:
cd ${BUILD_WASM} && cat ${SRC}/patches/12-timemodule-clang15.patch | patch -p1
# We can't use the modified WASI socket.h headers, since they annoyingly render the
# data structure useless (why!? - to save a few bytes?).
# See packages/python-wasm/src/wasm/posix/socket.zig (which will move soon)
cd ${BUILD_WASM} && cat ${SRC}/patches/13-socket-unmodified-headers.patch | patch -p1
# Warn instead of error on attempt to start a thread.
# cd ${BUILD_WASM} && cat ${SRC}/patches/14-threading-warning.patch | patch -p1
touch ${BUILD_WASM}/.patched
DEBUG_MODE = ""
# DEBUG_MODE = "-g"
BLDSHARED = cowasm-cc --experimental-pic -shared ${DEBUG_MODE}
${DIST_WASM}/.built-cpython: node_modules ${BUILD_WASM}/.patched ${DIST_NATIVE}/.built ${BIN}/python-native
# - Important to set CXX even though it's not in the main build so zig is used for C++ extension modules.
# - with-pymalloc it actually works and seems faster
# - We use "zig cc -target wasm32-wasi" instead of "zig cc -target wasm32-wasi-musl" since the broken
# Python configure.ac script outputs wasm32-wasi instead of wasm32-wasi-musl as the PLATFORM_TRIPLE.
# This might be something to fix and upstream, but for now, just not using a triple works.
# - We use -I (path to source) in the CC/CXX line in addition to CFLAGS so that we can override some
# bad global libraries, e.g., systemwide (zig musl) sys/wait.h with a local version.
# - In Python source they lower the default recursion limit; however, we don't need that since our
# runtime is Chromium, not wasitime and we can handle huge limits (see Include/internal/pycore_ceval.h).
# - enable-ipv6, since I put a lot of work into this for posix-node!
# --with-pkg-config=no, this is CRITICAL; otherwise, if pkg-config is installed, then python's build system
# will use it and pick up system-wide libraries that are not supported by WASM, which is a huge pain
# that leads to subtle bugs. One example of this is "uuid". (TODO: bug report: should be disabled when cross compiling?)
# - DPy_DEFAULT_RECURSION_LIMIT=700:
# - with -Oz, a limit of 1000 causes test_userdict to fail when testing on
# nodejs linux (it works on mac). test_userlist is even worse.
# - with -oZ, test_richcmp.py fails with a limit above 750; a limit of max of 720 works on node on linux.
# Changing to -O3 makes no difference.
# - with-ensurepip: we put that since we need it, but the makefile just runs ensurepip on python-native,
# so it doesn't help. That's fine for our application, since we explicitly run ensurepip later.
# - explicitly specifying BLDSHARED is needed since otherwise linking shared modules doesn't work.
# ALSO, note that wasm-strip for fPIC code must be done at link time and can't be done later, due to
# it simply not being implemented yet. Stripping is automatic with cowasm-cc unless you pass -g.
cd ${BUILD_WASM} && \
OPT="${WASM_OPT}" \
CC="cowasm-cc" \
CXX="cowasm-c++" \
AR="zig ar" \
CFLAGS="${DEBUG_MODE} -I${BUILD_WASM} -I${POSIX_WASM} -DPy_DEFAULT_RECURSION_LIMIT=700" \
BLDSHARED="${BLDSHARED}" \
CONFIG_SITE=./config.site \
READELF=true \
HOSTRUNNER="${BIN}/cowasm" \
./configure \
--config-cache \
--prefix=${DIST_WASM} \
--enable-big-digits=30 \
--enable-optimizations \
--enable-ipv6 \
--disable-shared \
--with-pkg-config=no \
--with-readline=editline \
--with-build-python=${BIN}/python-native \
--with-pymalloc \
--with-ensurepip \
--host=wasm32-unknown-wasi \
--build=`./config.guess`
cat src/pyconfig.h >> ${BUILD_WASM}/pyconfig.h
# NOTE: I have seen "error: unable to build WASI libc CRT file: FileNotFound" when using "make -j8",
# and had it go away when removing parallel build.
cd ${BUILD_WASM} && \
LZMA_WASM="${LZMA_WASM}" LIBEDIT_WASM="${LIBEDIT_WASM}" NCURSES_WASM="${NCURSES_WASM}" TERMCAP_WASM="${TERMCAP_WASM}" OPENSSL_WASM="${OPENSSL_WASM}" SQLITE_WASM="${SQLITE_WASM}" BZIP2_WASM="${BZIP2_WASM}" LIBFFI_WASM="${LIBFFI_WASM}" ZLIB_WASM="${ZLIB_WASM}" make -j8
mkdir -p ${DIST_WASM} && touch ${DIST_WASM}/.built-cpython
${DIST_WASM}/.install-cpython: node_modules ${DIST_WASM}/.built-cpython
# CRITICAL! We have to make changes to build/lib.wasi-wasm32-3.11/_sysconfigdata__wasi_wasm32-wasi.py so it
# matches the configuration we just used. TODO: Find a better way to fix this, e.g., by configuration or
# patching something earlier? This definitely feels like a hack.
# This is what based on testing actually works:
# 'LDSHARED': '' --> 'LDSHARED': '${BLDSHARED}'
# This is idempotent.
cd ${BUILD_WASM}/build/lib.wasi-wasm32-3.11/ \
&& sed -i'.bak' "s/'LDSHARED': ''/'LDSHARED': '${BLDSHARED}'/g" _sysconfigdata__wasi_wasm32-wasi.py
# Install as usual. Output is huge so we do not show it all.
cd ${BUILD_WASM} && \
LZMA_WASM="${LZMA_WASM}" LIBEDIT_WASM="${LIBEDIT_WASM}" NCURSES_WASM="${NCURSES_WASM}" TERMCAP_WASM="${TERMCAP_WASM}" OPENSSL_WASM="${OPENSSL_WASM}" SQLITE_WASM="${SQLITE_WASM}" BZIP2_WASM="${BZIP2_WASM}" LIBFFI_WASM="${LIBFFI_WASM}" ZLIB_WASM="${ZLIB_WASM}" make install | tail -n 10
# For some reason (upstream bug?) test-tomllib isn't copied over, but it's nice to have for testing.
cp -rv ${BUILD_WASM}/Lib/test/test_tomllib ${DIST_WASM}/lib/python3.11/test/
# Used for extension modules to efficiently access what Python exports.
cp ${BUILD_WASM}/Programs/libpython.c ${DIST_WASM}/lib
# Done!
touch ${DIST_WASM}/.install-cpython
.PHONY:
wasm-cpython: ${DIST_WASM}/.install-cpython
# These are the minimal data files needed to start cPython, which we found via "DEBUG=wasi:open pw-d"!
MINIMAL_FILES = encodings/__init__.pyc encodings/aliases.pyc encodings/utf_8.pyc
MINIMAL_READLINE = termcap lib-dynload/readline.cpython-311-wasm32-wasi.so rlcompleter.pyc inspect.pyc ast.pyc contextlib.pyc collections/__init__.pyc keyword.pyc operator.pyc reprlib.pyc functools.pyc types.pyc enum.pyc dis.pyc opcode.pyc collections/abc.pyc importlib/__init__.pyc warnings.pyc linecache.pyc tokenize.pyc re/__init__.pyc re/_compiler.pyc re/_parser.pyc re/_constants.pyc re/_casefix.pyc copyreg.pyc token.pyc
# Below we create two zip files:
#
# ${DIST_WASM}/lib/dist/python-minimal.zip - (size=10K) -- a tiny file with the minimum needed to start python
# ${DIST_WASM}/lib/dist/python-stdlib.zip - (size= ~ 5MB) -- the full standard libary
#
# We WANT to run wasm_assets.py under web assembly rather than use the native cpython!
# This is just because we can and because it's a *good test*. It does not work yet.
# To try this, replace
# && python-native ./Tools/wasm/wasm_assets.py
# below by
# && python-wasm ./Tools/wasm/wasm_assets.py
${DIST_WASM}/.built: node_modules ${DIST_WASM}/.install-cpython ${BIN}/python-native
# Build wasm asset bundle (the pyc files, etc.).
cd ${BUILD_WASM} \
&& mkdir -p usr/local/lib/python3.11/lib-dynload/ \
&& ${BIN}/python-native ./Tools/wasm/wasm_assets.py \
&& rm -rf ${DIST_WASM}/lib/dist \
&& mv usr/local/lib ${DIST_WASM}/lib/dist
# Add termcap for xterm to our zip asset bundle, so readline actually works:
cp ${SRC}/termcap ${DIST_WASM}/lib/dist/termcap && cd ${DIST_WASM}/lib/dist/ && zip -u python311.zip termcap
# **TODO:** It is very silly for these to be in the same zip file, obviously, because that means they have to be downloaded,
# so we should just put them in the main binary. We'll change that when things are working.
mkdir -p ${DIST_WASM}/lib/dist/lib-dynload/
cp -v ${DIST_WASM}/lib/python3.11/lib-dynload/*.so ${DIST_WASM}/lib/dist/lib-dynload/
# This would run wasm-opt on the so extension modules, but it is not
# necessarily "worth it", since it only reduces the size by less than 3%.
#${BIN}/cowasm-opt ${DIST_WASM}/lib/dist/lib-dynload/
# Unfortunately, wasm-strip removes the critical custom section that makes the so file useful, since NotImplementedError
# https://reviews.llvm.org/D73820
#find ${DIST_WASM}/lib/dist/lib-dynload -type f -name "*.so" | xargs -n1 ${CWD}/node_modules/.bin/wasm-strip
# Instead, we use the -s option to wasm-ld above.
# Also mv zip to a more meaningful name.
cd ${DIST_WASM}/lib/dist/ \
&& zip -u python311.zip lib-dynload/* \
&& mv python311.zip python-stdlib.zip
# Next make the MINIMAL python0.zip, which is all that's needed to start python (e.g., for a
# jupyter kernel or to back
# computations, but not a REPL). We also make a version with the readline so, so you
# can start a terminal, before more data needs to be loaded.
cd ${DIST_WASM}/lib/dist/ \
&& mkdir tmp && cd tmp && unzip ../python-stdlib.zip && cp ../termcap .\
&& zip ../python-minimal.zip ${MINIMAL_FILES} \
&& zip ../python-readline.zip ${MINIMAL_FILES} ${MINIMAL_READLINE} \
&& cd .. && rm -rf tmp
# Add some code at the end of the _sysconfigdata so that it is sufficiently relocatable so that
# it's possible to use setuptools and build C extensions. I don't understand why python doesn't
# already do this -- maybe there is some subtle edge case that makes it impossible to do
# in general? Or maybe people don't relocate python installs then build. I don't know.
cat ${SRC}/patches/17-fix-sysconfigdata.py >> ${DIST_WASM}/lib/python3.11/_sysconfigdata__wasi_wasm32-wasi.py
# We also copy the posix-wasm.h that was used for the build, so that building C extensions isn't
# complicated by trying to find this.
cp -v ${POSIX_WASM}/*.h ${DIST_WASM}/include/python3.11/
touch ${DIST_WASM}/.built
###########
# Installing pip and setuptools
# We can only do this after ../packages/python-wasm is built, which depends on dylink
# (which depends on cpython) and many other things. So this target is invoked later.
# NOTE: If you do "make clean-wasm", then "make", be sure to also do "make pip".
###########
${DIST_WASM}/lib/python3.11/site-packages/setuptools/.built: ${DIST_WASM}/.built
rm -rf ${DIST_WASM}/lib/python*/site-packages/*pip* ${DIST_WASM}/lib/python*/site-packages/*setuptools*
${CWD}/bin/python-wasm -m ensurepip
# We apply one patch to support building C++ extensions. Without this, the numpy
# build fails pretty quickly. This is needed due to our hacky approach to -fPIC.
cd ${DIST_WASM} && cat ${SRC}/patches/10-setuptools-c++.patch | patch -p1
# It would be much nicer to implement a better mmap. But for now, let's just patch it out:
cd ${DIST_WASM} && cat ${SRC}/patches/15-pip-no-mmap.patch | patch -p1
# It would be better to have a better fallback for threads that works in some cases so we
# don't need this. But for now, just remove it:
cd ${DIST_WASM} && cat ${SRC}/patches/16-pip-no-auto-refresh-progress.patch | patch -p1
touch ${DIST_WASM}/lib/python3.11/site-packages/setuptools/.built
pip: ${DIST_WASM}/lib/python3.11/site-packages/setuptools/.built
.PHONY: pip
# Run the full official Python test suite on the wasm build. We can't just do
# "make test" in build/wasm, since that only support wasmtime and emscripten.
# Also, we can't use our WASM python to orchestrate this (yet), since the
# test suite runner uses threads (and anyways, we like the speedup from multithreading).
# However, you set line 543 to
# self.ns.use_mp = 0
# of dist/wasm/lib/python3.11/test/libregrtest/main.py then it works, but of course
# with a single process, which is very slow. So we stick with python-native for testing.
RUN_TESTS = _PYTHON_PROJECT_BASE=${BUILD_WASM} \
_PYTHON_HOST_PLATFORM=wasi-wasm32 \
PYTHONPATH=${DIST_WASM}/lib/python3.11 \
_PYTHON_SYSCONFIGDATA_NAME=_sysconfigdata__wasi_wasm32-wasi \
${DIST_NATIVE}/bin/python3 \
./Tools/scripts/run_tests.py \
--python=${PYTHON_WASM}
# We use a restricted PATH for running tests. On Linux I had a lot of trouble
# with some random binaries in random places causing hangs, and this provides
# a cleaner environment.
PNPM = $(shell which pnpm)
NODE = $(shell which node)
TEST_PATH=${BIN}:/bin:/usr/bin:$(shell dirname ${PNPM}):$(shell dirname ${NODE})
test-all: wasm native
cd ${BUILD_WASM} && PATH=${TEST_PATH} ${RUN_TESTS}
.PHONY: test-all
# I got this list using this on the output:
# grep passed out | awk '{print $(NF - 1)}' | tr '\n' ' '
SUPPORTED_TEST_DESC = "the **supported-by-python-wasm** cpython test suites"
SUPPORTED_TESTS = test_atexit test_base64 test_calendar test_cgitb test_compile test_eof test_fstring test_graphlib test_gzip test_hash test_import test_inspect test_module test_ntpath test_popen test_posixpath test_py_compile test_pydoc test_quopri test_script_helper test_source_encoding test_struct test_tabnanny test_tarfile test_trace test_unicodedata test_weakref test___all__ test___future__ test__locale test__opcode test__osx_support test_abc test_abstract_numbers test_aifc test_argparse test_array test_ast test_audioop test_augassign test_baseexception test_bdb test_bigaddrspace test_bigmem test_binascii test_binop test_bisect test_bool test_buffer test_bufio test_bz2 test_call test_cgi test_charmapcodec test_class test_cmath test_cmd test_code_module test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_iso2022 test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codeop test_collections test_colorsys test_compare test_complex test_configparser test_contains test_context test_contextlib test_copy test_copyreg test_crashers test_crypt test_csv test_dataclasses test_datetime test_dbm test_dbm_dumb test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_dict test_dict_version test_dictcomps test_dictviews test_difflib test_dis test_doctest2 test_dynamic test_dynamicclassattribute test_eintr test_email test_ensurepip test_enum test_enumerate test_errno test_except_star test_exception_group test_exception_hierarchy test_exception_variations test_extcall test_file test_filecmp test_fileinput test_fileutils test_finalization test_float test_flufl test_fnmatch test_format test_fractions test_frame test_frozen test_funcattrs test_functools test_future test_future3 test_future4 test_future5 test_generator_stop test_genericalias test_genericclass test_genericpath test_genexps test_getopt test_getpath test_gettext test_global test_grammar test_hashlib test_heapq test_hmac test_html test_htmlparser test_http_cookiejar test_http_cookies test_imghdr test_index test_int test_int_literal test_ipaddress test_isinstance test_iter test_iterlen test_itertools test_keyword test_keywordonlyarg test_lib2to3 test_linecache test_list test_listcomps test_lltrace test_locale test_long test_longexp test_lzma test_mailcap test_math test_memoryio test_memoryview test_metaclass test_mimetypes test_minidom test_modulefinder test_multibytecodec test_named_expressions test_netrc test_numeric_tower test_opcache test_opcodes test_operator test_optparse test_ordered_dict test_osx_env test_patma test_peepholer test_peg_generator test_pep646_syntax test_pickle test_picklebuffer test_pickletools test_pipes test_pkg test_pkgutil test_plistlib test_positional_only_arg test_pow test_pprint test_print test_property test_pulldom test_pyclbr test_pyexpat test_raise test_random test_range test_re test_reprlib test_richcmp test_rlcompleter test_sax test_sched test_scope test_secrets test_set test_setcomps test_shelve test_shlex test_slice test_sndhdr test_sort test_statistics test_strftime test_string test_string_literals test_stringprep test_strptime test_strtod test_structseq test_subclassinit test_sunau test_sundry test_super test_symtable test_syntax test_textwrap test_threadsignals test_timeit test_tokenize test_tomllib test_tuple test_type_annotations test_type_cache test_type_comments test_typechecks test_types test_ucn test_unary test_unicode_file test_unicode_file_functions test_unicode_identifiers test_univnewlines test_unpack test_unpack_ex test_unparse test_urlparse test_userdict test_userlist test_userstring test_utf8source test_uu test_uuid test_wave test_weakset test_with test_xdrlib test_xml_dom_minicompat test_xml_etree_c test_xxtestfuzz test_yield_from test_zipimport test_zlib test_zoneinfo test_pstats test_tempfile test_sys_setprofile
${BIN}/python-wasm: ${DIST_WASM}/.built ${CWD}/bin/python-wasm
ln -sf ${CWD}/bin/python-wasm ${BIN}/python-wasm
.PHONY: python-wasm
python-wasm: ${BIN}/python-wasm
test-pip: pip
echo "Testing PIP"
${CWD}/bin/python-wasm -m pip |grep Usage
echo "Success"
.PHONY: test-pip
# Some tests can be pretty slow, e.g., test_tarfile takes about a minute or more on a FAST computer,
# and doing 4 in parallel on a slow computer.
test: wasm native ${BIN}/python-wasm test-pip
echo "Running ${SUPPORTED_TEST_DESC}"
cd ${BUILD_WASM} && PATH=${TEST_PATH} ${RUN_TESTS} -j4 --timeout=600 ${SUPPORTED_TESTS}
echo "Ran ${SUPPORTED_TEST_DESC}"
.PHONY: test
PASSED_WITHOUT_SUBPROCESS_DESC = "These tests all passed without subprocess or socket support enabled, but fail with it."
PASSED_WITHOUT_SUBPROCESS = test_timeout test_stat test_robotparser test_nntplib test_largefile test_logging test_audit test_bytes test_c_locale_coercion test_cmd_line_script test_compileall test_coroutines test_exceptions test_gc test_json test_marshal test_runpy test_site test_traceback test_unicode test_utf8_mode test_warnings
test-passed-without-subprocess: wasm native
echo "Running ${PASSED_WITHOUT_SUBPROCESS_DESC}"
cd ${BUILD_WASM} && PATH=${TEST_PATH} ${RUN_TESTS} --timeout=180 -j4 ${PASSED_WITHOUT_SUBPROCESS}
echo "Ran ${PASSED_WITHOUT_SUBPROCESS_DESC}"
# One test fails here due to the fact that the PYTHONHOME is different in webworker mode (since the files are in the zip archive).
test-worker: wasm native
echo "Running using webworker -- ${SUPPORTED_TEST_DESC}"
cd ${BUILD_WASM} && PATH=${TEST_PATH} PYTHONHOME=${DIST_WASM} ${RUN_TESTS} -j4 --timeout=180 ${SUPPORTED_TESTS}
echo "Ran using webworker -- ${SUPPORTED_TEST_DESC}"
# NOTES:
# test_pydoc - sometimes randomly hangs, mainly on linux.
# test_sys and test_platform -- I think crashes caused by installing too much and running native subprocesses; eventually we won't run anything native... (these work on most platform but I have an ubuntu docker container where they fail)
#
# test_code test_cppext fail because of libffi not being done.
#
FAILED_TEST_DESC = "All **unsuppported** and known failing cpython test suites: this is less than 10% of the non-skipped cpython test suite. We want to fix all of these."
FAILED_TESTS = test_code test_cppext test_readline test_builtin test_sys test_platform test_getpass test_codecs test_distutils test_fileio test_generators test_glob test_imp test_importlib test_io test_openpty test_os test_pathlib test_posix test_shutil test_signal test_sqlite3 test_support test_sysconfig test_time test_tracemalloc test_typing test_unittest test_xml_etree test_zipapp test_zipfile
test-failed: wasm native
echo "Running ${FAILED_TEST_DESC}"
cd ${BUILD_WASM} && PATH=${TEST_PATH} ${RUN_TESTS} --timeout=180 -j4 ${FAILED_TESTS}
echo "Ran ${FAILED_TEST_DESC}"
TEST=test_long
test-one: wasm native
echo "Use 'make TEST=test_long test-one' to run a specific test, e.g., test_long in this case."
cd ${BUILD_WASM} && PATH=${TEST_PATH} ${RUN_TESTS} --timeout=60 -v ${TEST}
.PHONY: test-one
test-one-worker: wasm native
echo "Use 'make TEST=test_long test-one' to run a specific test, e.g., test_long in this case."
cd ${BUILD_WASM} && PATH=${TEST_PATH} ${RUN_TESTS} -j1 --timeout=60 -v ${TEST}
.PHONY: test-one
SKIPPED_TEST_DESC = "all **skipped** cpython test suites. Extend what cpython wasm can do to include more of these."
SKIPPED_TESTS = test_asdl_parser test__xxsubinterpreters test_asyncgen test_asynchat test_asyncio test_asyncore test_check_c_globals test_clinic test_cmd_line test_concurrent_futures test_contextlib_async test_ctypes test_curses test_dbm_gnu test_dbm_ndbm test_devpoll test_doctest test_docxmlrpc test_dtrace test_embed test_epoll test_faulthandler test_fcntl test_file_eintr test_fork1 test_ftplib test_gdb test_grp test_httplib test_httpservers test_idle test_imaplib test_interpreters test_ioctl test_kqueue test_launcher test_mailbox test_mmap test_msilib test_multiprocessing_fork test_multiprocessing_forkserver test_multiprocessing_main_handling test_multiprocessing_spawn test_nis test_ossaudiodev test_pdb test_poll test_poplib test_pty test_pwd test_queue test_regrtest test_repl test_resource test_select test_selectors test_smtpd test_smtplib test_smtpnet test_socket test_socketserver test_spwd test_ssl test_stable_abi_ctypes test_startfile test_subprocess test_sys_settrace test_syslog test_tcl test_telnetlib test_thread test_threadedtempfile test_threading test_threading_local test_tix test_tk test_tools test_ttk_guionly test_ttk_textonly test_turtle test_urllib test_urllib2 test_urllib2_localnet test_urllib2net test_urllib_response test_urllibnet test_venv test_wait3 test_wait4 test_webbrowser test_winconsoleio test_winreg test_winsound test_wsgiref test_xmlrpc test_xmlrpc_net test_xxlimited test_zipfile64 test_zipimport_support test_cprofile test_profile
test-skipped: wasm native
echo "Running ${SKIPPED_TEST_DESC}"
cd ${BUILD_WASM} && PATH=${TEST_PATH} ${RUN_TESTS} --timeout=60 ${SKIPPED_TESTS}
echo "Ran ${SKIPPED_TEST_DESC}"
# This is sort of like the 'assets' thing that is part of cpython, but it's
# much more useful (but less small).
${DIST_WASM}/.publishable: wasm
# Do not need the tests in the published version -- they're huge
rm -rf ${DIST_WASM}/lib/python3.11/test
# Graphics -- Remove idle which we are nowhere near supporting (and is big):
rm -rf ${DIST_WASM}/lib/python3.11/idlelib
rm -rf ${DIST_WASM}/lib/python3.11/*tkinter
rm -rf ${DIST_WASM}/lib/python3.11/turtle*
# Temporarily make it into a package so that we can bundle it
touch ${DIST_WASM}/lib/python3.11/__init__.py
# Bundle it -- using python-native since it's more compressed:
cd ${DIST_WASM}/lib/ && ${CWD}/bin/python-wasm -m cowasm_bundler python3.11
# Move it out of the way
mv ${DIST_WASM}/lib/python3.11 ${DIST_WASM}/lib/python3.11.orig
# Extract bundle
cd ${DIST_WASM}/lib/ && tar xf python3.11.tar.xz
# Remove __init__.pyc
rm -f ${DIST_WASM}/lib/python3.11/__init__.pyc
# Also site-packages
mkdir ${DIST_WASM}/lib/python3.11/site-packages
# Move compiled so files over
mv ${DIST_WASM}/lib/python3.11.orig/lib-dynload ${DIST_WASM}/lib/python3.11/
# Compress libpython3.11 to save 20MB decompressed. Note that this obviously
# complicates the python-wasm package which has to explicitly copy this out and decompress it.
xz ${DIST_WASM}/lib/libpython3.11.a
# Remove stuff
rm -rf ${DIST_WASM}/lib/python3.11.tar.xz ${DIST_WASM}/lib/python3.11.orig
touch ${DIST_WASM}/.publishable
.PHONY: publishable
publishable: ${DIST_WASM}/.publishable