diff options
31 files changed, 424 insertions, 84 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b26dfec..d30f9b0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -33,6 +33,7 @@ jobs: - spec: cp38-manylinux_x86_64 - spec: cp39-manylinux_x86_64 - spec: cp310-manylinux_x86_64 + - spec: cp311-manylinux_x86_64 - spec: cp27-manylinux_i686 cibw_version: cibuildwheel<2.0 # py2.7 is not supported on CIBW 2.0+ manylinux_img: manylinux1 # build really old Pythons on manylinux1 @@ -42,12 +43,20 @@ jobs: - spec: cp38-manylinux_i686 - spec: cp39-manylinux_i686 - spec: cp310-manylinux_i686 + - spec: cp311-manylinux_i686 + - spec: cp39-musllinux_x86_64 + - spec: cp310-musllinux_x86_64 + - spec: cp311-musllinux_x86_64 + - spec: cp39-musllinux_i686 + - spec: cp310-musllinux_i686 + - spec: cp311-musllinux_i686 steps: - name: clone repo uses: actions/checkout@v2 - name: build/test wheels env: + CFLAGS: -Dffi_call=cffistatic_ffi_call # override name for ffi_call to break hard if we linked against someone else's libffi CIBW_ARCHS_LINUX: auto CIBW_BUILD: ${{ matrix.spec }} CIBW_BEFORE_BUILD: | @@ -55,19 +64,20 @@ jobs: curl -L -O https://github.com/libffi/libffi/archive/v3.4.2.tar.gz && \ tar zxf v3.4.2.tar.gz && cd libffi-3.4.2 && \ ./autogen.sh && \ - ./configure --without-gcc-arch --disable-docs && \ + ./configure --without-gcc-arch --disable-docs --with-pic --enable-shared=no && \ make && \ make install && \ - ldconfig - # TODO: update default to '' once CIBW 2.1.3 ships: https://github.com/pypa/cibuildwheel/pull/829 - CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_img || 'manylinux2010' }} - CIBW_MANYLINUX_I686_IMAGE: ${{ matrix.manylinux_img || 'manylinux2010' }} + ldconfig || true + CIBW_ENVIRONMENT_PASS_LINUX: CFLAGS # ensure that the build container can see our overridden build config + CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_img || '' }} + CIBW_MANYLINUX_I686_IMAGE: ${{ matrix.manylinux_img || '' }} + CIBW_PRERELEASE_PYTHONS: 'True' CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: python -m pytest {project}/c {project}/testing + CIBW_TEST_COMMAND: PYTHONUNBUFFERED=1 python -m pytest {project} run: | python -m pip install --upgrade "${{ matrix.cibw_version || 'cibuildwheel' }}" - # actually build libyaml + wheel (using env tweaks above) + # actually build libffi + wheel (using env tweaks above) python -m cibuildwheel --output-dir dist . @@ -94,6 +104,7 @@ jobs: - spec: cp38-macosx_x86_64 - spec: cp39-macosx_x86_64 - spec: cp310-macosx_x86_64 + - spec: cp311-macosx_x86_64 # build for arm64 under a hacked macOS 12 self-hosted x86_64-on-arm64 runner until arm64 is fully supported # FIXME: ? cp38-macosx_arm64 requires special handling and fails some test_zdist tests under cibw 2.1.2, skip it (so Apple's XCode python3 won't have a wheel) - spec: cp39-macosx_arm64 @@ -107,6 +118,14 @@ jobs: runs_on: [self-hosted, macOS] run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0} sdkroot: macosx11.3 + + - spec: cp311-macosx_arm64 + deployment_target: '11.0' + runs_on: [self-hosted, macOS] + run_wrapper: arch -arm64 bash --noprofile --norc -eo pipefail {0} + sdkroot: macosx11.3 + + steps: - name: clone repo uses: actions/checkout@v2 @@ -119,8 +138,9 @@ jobs: - name: build/test wheels env: CIBW_BUILD: ${{ matrix.spec }} + CIBW_PRERELEASE_PYTHONS: 'True' CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pip install pip --upgrade; cd {project}; pytest + CIBW_TEST_COMMAND: pip install pip --upgrade; cd {project}; PYTHONUNBUFFERED=1 pytest run: | if [[ -n "${{ matrix.deployment_target || '' }}" ]] then @@ -141,7 +161,7 @@ jobs: if-no-files-found: error windows: - runs-on: windows-2016 + runs-on: windows-2019 strategy: matrix: include: @@ -152,6 +172,7 @@ jobs: - spec: cp38-win_amd64 - spec: cp39-win_amd64 - spec: cp310-win_amd64 + - spec: cp311-win_amd64 - spec: cp27-win32 cibw_version: cibuildwheel==1.10 # last release with proper py2.7 Windows support - spec: cp36-win32 @@ -159,6 +180,7 @@ jobs: - spec: cp38-win32 - spec: cp39-win32 - spec: cp310-win32 + - spec: cp311-win32 steps: - name: clone repo uses: actions/checkout@v2 @@ -180,6 +202,7 @@ jobs: - name: build/test wheels env: CIBW_BUILD: ${{ matrix.spec }} + CIBW_PRERELEASE_PYTHONS: 'True' run: | python -m pip install --upgrade pip pip install "${{ matrix.cibw_version || 'cibuildwheel'}}" diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index ffecbf9..b9618bd 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2,7 +2,7 @@ #include <Python.h> #include "structmember.h" -#define CFFI_VERSION "1.15.0" +#define CFFI_VERSION "1.15.1" #ifdef MS_WIN32 #include <windows.h> @@ -2197,7 +2197,7 @@ static PyObject *_frombuf_repr(CDataObject *cd, const char *cd_type_name) } } -static PyObject *cdataowning_repr(CDataObject *cd) +static Py_ssize_t cdataowning_size_bytes(CDataObject *cd) { Py_ssize_t size = _cdata_var_byte_size(cd); if (size < 0) { @@ -2208,6 +2208,12 @@ static PyObject *cdataowning_repr(CDataObject *cd) else size = cd->c_type->ct_size; } + return size; +} + +static PyObject *cdataowning_repr(CDataObject *cd) +{ + Py_ssize_t size = cdataowning_size_bytes(cd); return PyText_FromFormat("<cdata '%s' owning %zd bytes>", cd->c_type->ct_name, size); } @@ -4603,7 +4609,7 @@ static PyObject *get_unique_type(CTypeDescrObject *x, array [ctype, length] funcptr [ctresult, ellipsis+abi, num_args, ctargs...] */ - PyObject *key, *y; + PyObject *key, *y, *res; void *pkey; key = PyBytes_FromStringAndSize(NULL, keylength * sizeof(void *)); @@ -4635,8 +4641,9 @@ static PyObject *get_unique_type(CTypeDescrObject *x, /* the 'value' in unique_cache doesn't count as 1, but don't use Py_DECREF(x) here because it will confuse debug builds into thinking there was an extra DECREF in total. */ - ((PyObject *)x)->ob_refcnt--; - return (PyObject *)x; + res = (PyObject *)x; + Py_SET_REFCNT(res, Py_REFCNT(res) - 1); + return res; error: Py_DECREF(x); @@ -5665,7 +5672,8 @@ static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct, } } -#define ALIGN_ARG(n) ((n) + 7) & ~7 +#define ALIGN_TO(n, a) ((n) + ((a)-1)) & ~((a)-1) +#define ALIGN_ARG(n) ALIGN_TO(n, 8) static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, CTypeDescrObject *fresult) @@ -5690,10 +5698,12 @@ static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, /* exchange data size */ /* first, enough room for an array of 'nargs' pointers */ exchange_offset = nargs * sizeof(void*); + /* then enough room for the result --- which means at least + sizeof(ffi_arg), according to the ffi docs, but we also + align according to the result type, for issue #531 */ + exchange_offset = ALIGN_TO(exchange_offset, fb->rtype->alignment); exchange_offset = ALIGN_ARG(exchange_offset); cif_descr->exchange_offset_arg[0] = exchange_offset; - /* then enough room for the result --- which means at least - sizeof(ffi_arg), according to the ffi docs */ i = fb->rtype->size; if (i < (Py_ssize_t)sizeof(ffi_arg)) i = sizeof(ffi_arg); @@ -5721,6 +5731,7 @@ static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, if (fb->atypes != NULL) { fb->atypes[i] = atype; /* exchange data size */ + exchange_offset = ALIGN_TO(exchange_offset, atype->alignment); exchange_offset = ALIGN_ARG(exchange_offset); cif_descr->exchange_offset_arg[1 + i] = exchange_offset; exchange_offset += atype->size; @@ -5737,6 +5748,7 @@ static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, } #undef ALIGN_ARG +#undef ALIGN_TO static void fb_cat_name(struct funcbuilder_s *fb, const char *piece, int piecelen) @@ -7010,12 +7022,14 @@ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* this is the constructor of the type implemented in minibuffer.h */ CDataObject *cd; Py_ssize_t size = -1; + int explicit_size; static char *keywords[] = {"cdata", "size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords, &CData_Type, &cd, &size)) return NULL; + explicit_size = size >= 0; if (size < 0) size = _cdata_var_byte_size(cd); @@ -7039,6 +7053,20 @@ b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) cd->c_type->ct_name); return NULL; } + + if (explicit_size && CDataOwn_Check(cd)) { + Py_ssize_t size_max = cdataowning_size_bytes(cd); + if (size > size_max) { + char msg[256]; + sprintf(msg, "ffi.buffer(cdata, bytes): creating a buffer of %llu " + "bytes over a cdata that owns only %llu bytes. This " + "will crash if you access the extra memory", + (unsigned PY_LONG_LONG)size, + (unsigned PY_LONG_LONG)size_max); + if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) + return NULL; + } + } /*WRITE(cd->c_data, size)*/ return minibuffer_new(cd->c_data, size, (PyObject *)cd); } diff --git a/c/libffi_arm64/README b/c/libffi_arm64/README index 3b8f133..f531e5f 100644 --- a/c/libffi_arm64/README +++ b/c/libffi_arm64/README @@ -1,5 +1,5 @@ -Libffi package for ARM64 is copied from cpython binary dependencies +Please run build_libffi.bat to generate the libffi static library and header files -https://github.com/python/cpython-bin-deps/archive/libffi.zip +Environment variable LIBFFI_SOURCE needs to be set to libffi source before invoking the script. -The library file has been renamed from libffi-7.lib to ffi.lib to avoid special casing
\ No newline at end of file +Libffi source can be downloaded from https://github.com/libffi/libffi
\ No newline at end of file diff --git a/c/libffi_arm64/build_libffi.bat b/c/libffi_arm64/build_libffi.bat new file mode 100644 index 0000000..be4e23b --- /dev/null +++ b/c/libffi_arm64/build_libffi.bat @@ -0,0 +1,71 @@ +@echo off
+goto :Run
+
+:Usage
+echo.
+echo Before running prepare_libffi.bat
+echo .
+echo LIBFFI_SOURCE environment variable must be set to the location of libffi source
+echo Source can be checked out from https://github.com/libffi/libffi
+echo.
+echo Cygwin needs to be installed (Invoke with --install-cygwin to install)
+echo.
+echo. Visual Studio 2017 or newer with ARM64 toolchain needs to be installed
+:Run
+set INSTALL_CYGWIN=
+
+:CheckOpts
+if "%1"=="" goto :CheckOptsDone
+if /I "%1"=="-?" goto :Usage
+if /I "%1"=="--install-cygwin" (set INSTALL_CYGWIN=1) & shift & goto :CheckOpts
+goto :Usage
+
+:CheckOptsDone
+
+if "%INSTALL_CYGWIN%"=="1" call :InstallCygwin
+
+REM Set build variables
+
+set BUILD=i686-pc-cygwin
+set HOST=aarch64-w64-cygwin
+if NOT DEFINED SH if exist c:\cygwin\bin\sh.exe set SH=c:\cygwin\bin\sh.exe
+
+REM Initialise ARM64 build environment
+
+if NOT DEFINED VCVARSALL (
+ for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64') DO @(set VCVARSALL="%%i\VC\Auxiliary\Build\vcvarsall.bat")
+)
+if ^%VCVARSALL:~0,1% NEQ ^" SET VCVARSALL="%VCVARSALL%"
+call %VCVARSALL% x86_arm64
+pushd %LIBFFI_SOURCE%
+%SH% --login -lc "cygcheck -dc cygwin"
+set GET_MSVCC=%SH% -lc "cd $LIBFFI_SOURCE; export MSVCC=`/usr/bin/find $PWD -name msvcc.sh`; echo ${MSVCC};"
+FOR /F "usebackq delims==" %%i IN (`%GET_MSVCC%`) do @set MSVCC=%%i
+set MSVCC=%MSVCC% -marm64
+
+echo Configuring and building libffi for ARM64
+
+%SH% -lc "(cd $LIBFFI_SOURCE; ./autogen.sh)"
+%SH% -lc "(cd $LIBFFI_SOURCE; ./configure CC='%MSVCC%' CXX='%MSVCC%' LD='link' CPP='cl -nologo -EP' CXXCPP='cl -nologo -EP' CPPFLAGS='-DFFI_BUILDING_DLL' NM='dumpbin -symbols' STRIP=':' --build=$BUILD --host=$HOST --enable-static --disable-shared)"
+%SH% -lc "(cd $LIBFFI_SOURCE; cp src/aarch64/ffitarget.h include)"
+%SH% -lc "(cd $LIBFFI_SOURCE; make)"
+
+set LIBFFI_OUT=%~dp0
+
+echo copying files to %LIBFFI_OUT%
+if not exist %LIBFFI_OUT%\include (md %LIBFFI_OUT%\include)
+copy %LIBFFI_SOURCE%\%HOST%\.libs\libffi.lib %LIBFFI_OUT%\ffi.lib || exit /B 1
+copy %LIBFFI_SOURCE%\%HOST%\fficonfig.h %LIBFFI_OUT%\include || exit /B 1
+copy %LIBFFI_SOURCE%\%HOST%\include\*.h %LIBFFI_OUT%\include || exit /B 1
+popd
+exit /B
+
+:InstallCygwin
+setlocal
+set CYG_ROOT=C:/cygwin
+set CYG_CACHE=C:/cygwin/var/cache/setup
+set CYG_MIRROR=http://mirrors.kernel.org/sourceware/cygwin/
+powershell -c "Invoke-WebRequest https://cygwin.com/setup-x86.exe -OutFile setup.exe"
+setup.exe -qgnNdO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P dejagnu -P autoconf -P automake -P libtool -P make
+endlocal
+exit /B
diff --git a/c/libffi_arm64/ffi.lib b/c/libffi_arm64/ffi.lib Binary files differindex 4a8b84b..b3f79e1 100644 --- a/c/libffi_arm64/ffi.lib +++ b/c/libffi_arm64/ffi.lib diff --git a/c/libffi_arm64/include/ffi.h b/c/libffi_arm64/include/ffi.h index d91c3e1..87eb14b 100644 --- a/c/libffi_arm64/include/ffi.h +++ b/c/libffi_arm64/include/ffi.h @@ -1,6 +1,7 @@ /* -----------------------------------------------------------------*-C-*- - libffi 3.3-rc0 - Copyright (c) 2011, 2014 Anthony Green - - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. + libffi 3.4.2 + - Copyright (c) 2011, 2014, 2019, 2021 Anthony Green + - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -217,7 +218,8 @@ FFI_EXTERN ffi_type ffi_type_complex_longdouble; typedef enum { FFI_OK = 0, FFI_BAD_TYPEDEF, - FFI_BAD_ABI + FFI_BAD_ABI, + FFI_BAD_ARGTYPE } ffi_status; typedef struct { @@ -269,7 +271,7 @@ typedef ffi_raw ffi_java_raw; #endif -FFI_API +FFI_API void ffi_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, @@ -288,15 +290,15 @@ FFI_API void ffi_java_raw_call (ffi_cif *cif, void (*fn)(void), void *rvalue, - ffi_java_raw *avalue); + ffi_java_raw *avalue) __attribute__((deprecated)); #endif FFI_API -void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw); +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw) __attribute__((deprecated)); FFI_API -void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args); +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args) __attribute__((deprecated)); FFI_API -size_t ffi_java_raw_size (ffi_cif *cif); +size_t ffi_java_raw_size (ffi_cif *cif) __attribute__((deprecated)); /* ---- Definitions for closures ----------------------------------------- */ @@ -310,7 +312,10 @@ typedef struct { void *trampoline_table; void *trampoline_table_entry; #else - char tramp[FFI_TRAMPOLINE_SIZE]; + union { + char tramp[FFI_TRAMPOLINE_SIZE]; + void *ftramp; + }; #endif ffi_cif *cif; void (*fun)(ffi_cif*,void*,void**,void*); @@ -330,6 +335,14 @@ typedef struct { FFI_API void *ffi_closure_alloc (size_t size, void **code); FFI_API void ffi_closure_free (void *); +#if defined(PA_LINUX) || defined(PA_HPUX) +#define FFI_CLOSURE_PTR(X) ((void *)((unsigned int)(X) | 2)) +#define FFI_RESTORE_PTR(X) ((void *)((unsigned int)(X) & ~3)) +#else +#define FFI_CLOSURE_PTR(X) (X) +#define FFI_RESTORE_PTR(X) (X) +#endif + FFI_API ffi_status ffi_prep_closure (ffi_closure*, ffi_cif *, @@ -363,8 +376,8 @@ typedef struct { #if !FFI_NATIVE_RAW_API - /* If this is enabled, then a raw closure has the same layout - as a regular closure. We use this to install an intermediate + /* If this is enabled, then a raw closure has the same layout + as a regular closure. We use this to install an intermediate handler to do the transaltion, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); @@ -389,8 +402,8 @@ typedef struct { #if !FFI_NATIVE_RAW_API - /* If this is enabled, then a raw closure has the same layout - as a regular closure. We use this to install an intermediate + /* If this is enabled, then a raw closure has the same layout + as a regular closure. We use this to install an intermediate handler to do the translation, void** -> ffi_raw*. */ void (*translate_args)(ffi_cif*,void*,void**,void*); @@ -421,14 +434,14 @@ FFI_API ffi_status ffi_prep_java_raw_closure (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), - void *user_data); + void *user_data) __attribute__((deprecated)); FFI_API ffi_status ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, ffi_cif *cif, void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), void *user_data, - void *codeloc); + void *codeloc) __attribute__((deprecated)); #endif #endif /* FFI_CLOSURES */ @@ -451,7 +464,7 @@ FFI_API void ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue, /* ---- Public interface definition -------------------------------------- */ -FFI_API +FFI_API ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs, @@ -484,18 +497,18 @@ ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, #endif /* If these change, update src/mips/ffitarget.h. */ -#define FFI_TYPE_VOID 0 +#define FFI_TYPE_VOID 0 #define FFI_TYPE_INT 1 -#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_FLOAT 2 #define FFI_TYPE_DOUBLE 3 #if 0 #define FFI_TYPE_LONGDOUBLE 4 #else #define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE #endif -#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_UINT8 5 #define FFI_TYPE_SINT8 6 -#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_UINT16 7 #define FFI_TYPE_SINT16 8 #define FFI_TYPE_UINT32 9 #define FFI_TYPE_SINT32 10 diff --git a/c/libffi_arm64/include/fficonfig.h b/c/libffi_arm64/include/fficonfig.h index 5768c29..e888ff1 100644 --- a/c/libffi_arm64/include/fficonfig.h +++ b/c/libffi_arm64/include/fficonfig.h @@ -18,6 +18,9 @@ /* Define this if you want extra debugging. */ /* #undef FFI_DEBUG */ +/* Define this if you want statically defined trampolines */ +/* #undef FFI_EXEC_STATIC_TRAMP */ + /* Cannot use PROT_EXEC on this target, so, we revert to alternative means */ /* #undef FFI_EXEC_TRAMPOLINE_TABLE */ @@ -77,12 +80,18 @@ /* Define to 1 if you have the `memcpy' function. */ /* #undef HAVE_MEMCPY */ +/* Define to 1 if you have the `memfd_create' function. */ +/* #undef HAVE_MEMFD_CREATE */ + /* Define to 1 if you have the <memory.h> header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the `mkostemp' function. */ /* #undef HAVE_MKOSTEMP */ +/* Define to 1 if you have the `mkstemp' function. */ +/* #undef HAVE_MKSTEMP */ + /* Define to 1 if you have the `mmap' function. */ /* #undef HAVE_MMAP */ @@ -95,6 +104,9 @@ /* Define if read-only mmap of a plain file works. */ /* #undef HAVE_MMAP_FILE */ +/* Define if your compiler supports pointer authentication. */ +/* #undef HAVE_PTRAUTH */ + /* Define if .eh_frame sections should be read-only. */ /* #undef HAVE_RO_EH_FRAME */ @@ -110,6 +122,9 @@ /* Define to 1 if you have the <string.h> header file. */ #define HAVE_STRING_H 1 +/* Define to 1 if you have the <sys/memfd.h> header file. */ +/* #undef HAVE_SYS_MEMFD_H */ + /* Define to 1 if you have the <sys/mman.h> header file. */ /* #undef HAVE_SYS_MMAN_H */ @@ -138,7 +153,7 @@ #define PACKAGE_NAME "libffi" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libffi 3.3-rc0" +#define PACKAGE_STRING "libffi 3.4.2" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "libffi" @@ -147,7 +162,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.3-rc0" +#define PACKAGE_VERSION "3.4.2" /* The size of `double', as computed by sizeof. */ #define SIZEOF_DOUBLE 8 @@ -177,7 +192,7 @@ /* #undef USING_PURIFY */ /* Version number of package */ -#define VERSION "3.3-rc0" +#define VERSION "3.4.2" /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ diff --git a/c/libffi_arm64/include/ffitarget.h b/c/libffi_arm64/include/ffitarget.h index ecb6d2d..d5622e1 100644 --- a/c/libffi_arm64/include/ffitarget.h +++ b/c/libffi_arm64/include/ffitarget.h @@ -32,7 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define FFI_SIZEOF_JAVA_RAW 4 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; -#elif defined(_M_ARM64) +#elif defined(_WIN32) #define FFI_SIZEOF_ARG 8 typedef unsigned long long ffi_arg; typedef signed long long ffi_sarg; @@ -45,8 +45,13 @@ typedef enum ffi_abi { FFI_FIRST_ABI = 0, FFI_SYSV, + FFI_WIN64, FFI_LAST_ABI, +#if defined(_WIN32) + FFI_DEFAULT_ABI = FFI_WIN64 +#else FFI_DEFAULT_ABI = FFI_SYSV +#endif } ffi_abi; #endif @@ -69,22 +74,22 @@ typedef enum ffi_abi #define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE #endif -#ifdef _M_ARM64 +#ifdef _WIN32 #define FFI_EXTRA_CIF_FIELDS unsigned is_variadic #endif +#define FFI_TARGET_SPECIFIC_VARIADIC /* ---- Internal ---- */ #if defined (__APPLE__) -#define FFI_TARGET_SPECIFIC_VARIADIC #define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs -#elif !defined(_M_ARM64) +#elif !defined(_WIN32) /* iOS and Windows reserve x18 for the system. Disable Go closures until a new static chain is chosen. */ #define FFI_GO_CLOSURES 1 #endif -#ifndef _M_ARM64 +#ifndef _WIN32 /* No complex type on Windows */ #define FFI_TARGET_HAS_COMPLEX_TYPE #endif diff --git a/c/test_c.py b/c/test_c.py index 654584d..cde83b8 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -1,5 +1,15 @@ import py import pytest +import sys + +is_musl = False +if sys.platform == 'linux': + try: + from packaging.tags import platform_tags + is_musl = any(t.startswith('musllinux') for t in platform_tags()) + del platform_tags + except ImportError: + pass def _setup_path(): import os, sys @@ -17,7 +27,7 @@ from _cffi_backend import __version__ # ____________________________________________________________ import sys -assert __version__ == "1.15.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.15.1", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): @@ -93,7 +103,8 @@ def test_all_rtld_symbols(): if sys.platform.startswith("linux"): RTLD_NODELETE RTLD_NOLOAD - RTLD_DEEPBIND + if not is_musl: + RTLD_DEEPBIND def test_new_primitive_type(): py.test.raises(KeyError, new_primitive_type, "foo") @@ -1296,7 +1307,7 @@ def test_read_variable_as_unknown_length_array(): def test_write_variable(): ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard ## https://bugs.pypy.org/issue1643 - if not sys.platform.startswith("linux"): + if not sys.platform.startswith("linux") or is_musl: py.test.skip("untested") BVoidP = new_pointer_type(new_void_type()) ll = find_and_load_library('c') @@ -1331,9 +1342,11 @@ def test_callback_exception(): except ImportError: import io as cStringIO # Python 3 import linecache - def matches(istr, ipattern, ipattern38): + def matches(istr, ipattern, ipattern38, ipattern311): if sys.version_info >= (3, 8): ipattern = ipattern38 + if sys.version_info >= (3, 11): + ipattern = ipattern311 str, pattern = istr, ipattern while '$' in pattern: i = pattern.index('$') @@ -1387,6 +1400,16 @@ Traceback (most recent call last): File "$", line $, in check_value $ ValueError: 42 +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>: +Traceback (most recent call last): + File "$", line $, in Zcb1 + $ + $ + File "$", line $, in check_value + $ + $ +ValueError: 42 """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 @@ -1401,6 +1424,13 @@ Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C: +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ + $ +OverflowError: integer 60000 does not fit 'short' """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 @@ -1449,6 +1479,19 @@ Traceback (most recent call last): File "$", line $, in test_callback_exception $ TypeError: $integer$ +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C: +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ + $ +OverflowError: integer 60000 does not fit 'short' +Exception ignored during handling of the above exception by 'onerror': +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ + $ +TypeError: $integer$ """) # sys.stderr = cStringIO.StringIO() @@ -1478,6 +1521,19 @@ Traceback (most recent call last): File "$", line $, in oops $ AttributeError: 'str' object has no attribute 'append$ +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C: +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ + $ +OverflowError: integer 60000 does not fit 'short' +Exception ignored during handling of the above exception by 'onerror': +Traceback (most recent call last): + File "$", line $, in oops + $ + $ +AttributeError: 'str' object has no attribute 'append$ """) finally: sys.stderr = orig_stderr @@ -3453,6 +3509,18 @@ def test_bitfield_as_ppc_gcc(): _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN) +def buffer_warning(cdata): + import warnings + buf = buffer(cdata) + bytes = len(buf) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + buffer(cdata, bytes) + assert len(w) == 0 + buffer(cdata, bytes + 1) + assert len(w) <= 1 + return len(w) == 1 + def test_struct_array_no_length(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) @@ -3567,6 +3635,7 @@ def test_struct_array_no_length(): assert p.a[1] == 20 assert p.a[2] == 30 assert p.a[3] == 0 + assert buffer_warning(p) # # struct of struct of varsized array BStruct2 = new_struct_type("bar") @@ -3575,6 +3644,20 @@ def test_struct_array_no_length(): for i in range(2): # try to detect heap overwrites p = newp(new_pointer_type(BStruct2), [100, [200, list(range(50))]]) assert p.tail.y[49] == 49 + assert buffer_warning(p) + assert not buffer_warning(cast(new_pointer_type(BStruct2), p)) + assert not buffer_warning(cast(BIntP, p)) + +def test_more_buffer_warning(): + BChar = new_primitive_type("unsigned char") + BCharP = new_pointer_type(BChar) + BArray = new_array_type(BCharP, 10) # char[10] + p = newp(BArray) + assert buffer_warning(p) + assert not buffer_warning(cast(BCharP, p)) + p = newp(BCharP) + assert buffer_warning(p) + assert not buffer_warning(cast(BCharP, p)) def test_struct_array_no_length_explicit_position(): diff --git a/cffi/__init__.py b/cffi/__init__.py index 82a9618..90e2e65 100644 --- a/cffi/__init__.py +++ b/cffi/__init__.py @@ -5,8 +5,8 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.15.0" -__version_info__ = (1, 15, 0) +__version__ = "1.15.1" +__version_info__ = (1, 15, 1) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/cffi/_embedding.h b/cffi/_embedding.h index e863d85..8e8df88 100644 --- a/cffi/_embedding.h +++ b/cffi/_embedding.h @@ -22,7 +22,8 @@ extern "C" { * _cffi_call_python_org, which on CPython is actually part of the _cffi_exports[] array, is the function pointer copied from - _cffi_backend. + _cffi_backend. If _cffi_start_python() fails, then this is set + to NULL; otherwise, it should never be NULL. After initialization is complete, both are equal. However, the first one remains equal to &_cffi_start_and_call_python until the @@ -224,7 +225,7 @@ static int _cffi_initialize_python(void) if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.15.0" + "\ncompiled with cffi version: 1.15.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/cffi/recompiler.py b/cffi/recompiler.py index 86b37d7..5d9d32d 100644 --- a/cffi/recompiler.py +++ b/cffi/recompiler.py @@ -407,7 +407,7 @@ class Recompiler: prnt(' NULL, /* no includes */') prnt(' %d, /* num_types */' % (len(self.cffi_types),)) flags = 0 - if self._num_externpy: + if self._num_externpy > 0 or self.ffi._embedding is not None: flags |= 1 # set to mean that we use extern "Python" prnt(' %d, /* flags */' % flags) prnt('};') @@ -422,7 +422,7 @@ class Recompiler: prnt('PyMODINIT_FUNC') prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) prnt('{') - if self._num_externpy: + if flags & 1: prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') prnt(' _cffi_call_python_org = ' '(void(*)(struct _cffi_externpy_s *, char *))p[1];') diff --git a/doc/source/conf.py b/doc/source/conf.py index 33e8c11..fb6421e 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,7 +47,7 @@ copyright = u'2012-2018, Armin Rigo, Maciej Fijalkowski' # The short X.Y version. version = '1.15' # The full version, including alpha/beta/rc tags. -release = '1.15.0' +release = '1.15.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 6d55eb5..4859c83 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -52,13 +52,13 @@ Download and Installation: * https://pypi.python.org/pypi/cffi -* Checksums of the "source" package version 1.15.0: +* Checksums of the "source" package version 1.15.1: - - MD5: f3a3f26cd3335fc597479c9475da0a0b + - MD5: ... - - SHA1: 9c51c29e35510adf7f94542e1f8e05611930b07b + - SHA1: ... - - SHA256: 920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954 + - SHA256: ... * Or grab the most current version from the `Heptapod page`_: ``hg clone https://foss.heptapod.net/pypy/cffi`` diff --git a/doc/source/overview.rst b/doc/source/overview.rst index dbc3540..3de8a3f 100644 --- a/doc/source/overview.rst +++ b/doc/source/overview.rst @@ -15,7 +15,12 @@ two sections delve deeper in the CFFI library. Make sure you have `cffi installed`__. +You can find these and some other complete demos in the demo__ directory +of the repository__. + .. __: installation.html +.. __: https://foss.heptapod.net/pypy/cffi/-/tree/branch/default/demo +.. __: https://foss.heptapod.net/pypy/cffi .. _out-of-line-api-level: .. _real-example: diff --git a/doc/source/ref.rst b/doc/source/ref.rst index 05c0f7c..946e48c 100644 --- a/doc/source/ref.rst +++ b/doc/source/ref.rst @@ -846,20 +846,35 @@ allowed. argument is identical to a ``item[]`` argument (and ``ffi.cdef()`` doesn't record the difference). So when you call such a function, you can pass an argument that is accepted by either C type, like - for example passing a Python string to a ``char *`` argument + for example passing a Python byte string to a ``char *`` argument (because it works for ``char[]`` arguments) or a list of integers to a ``int *`` argument (it works for ``int[]`` arguments). Note that even if you want to pass a single ``item``, you need to specify it in a list of length 1; for example, a ``struct point_s *`` argument might be passed as ``[[x, y]]`` or ``[{'x': 5, 'y': - 10}]``. + 10}]``. In all these cases (including passing a byte string to + a ``char *`` argument), the required C data structure is created + just before the call is done, and freed afterwards. As an optimization, CFFI assumes that a - function with a ``char *`` argument to which you pass a Python + function with a ``char *`` argument to which you pass a Python byte string will not actually modify the array of characters passed in, - and so passes directly a pointer inside the Python string object. + and so it attempts to pass directly a pointer inside the Python + byte string object. This still doesn't mean that the ``char *`` + argument can be stored by the C function and inspected later. + The ``char *`` is only valid for the duration of the call, even if + the Python object is kept alive for longer. (On PyPy, this optimization is only available since PyPy 5.4 - with CFFI 1.8.) + with CFFI 1.8. It may fail in rare cases and fall back to making + a copy anyway, but only for short strings so it shouldn't be + noticeable.) + + If you need to pass a ``char *`` that must be valid for longer than + just the call, you need to build it explicitly, either with ``p = + ffi.new("char[]", mystring)`` (which makes a copy) or by not using a + byte string in the first place but something else like a buffer object, + or a bytearray and ``ffi.from_buffer()``; or just use + ``ffi.new("char[]", length)`` directly if possible. `[2]` C function calls are done with the GIL released. diff --git a/doc/source/using.rst b/doc/source/using.rst index 38c96ba..ccaa4db 100644 --- a/doc/source/using.rst +++ b/doc/source/using.rst @@ -383,6 +383,18 @@ argument and may mutate it!): assert lib.strlen("hello") == 5 +(Note that there is no guarantee that the ``char *`` passed to the +function remains valid after the call is done. Similarly, if you write +``lib.f(x); lib.f(x)`` where ``x`` is a variable containing a byte string, +the two calls to ``f()`` could sometimes receive different ``char *`` +pointers, with each of them only valid during the corresponding call. This is +important notably for PyPy which uses many optimizations tweaking the data +underlying a byte string object. CFFI will not make and free a copy of +the whole string at *every* call---it usually won't---but you *cannot* +write code that relies on it: there are cases were that would break. +If you need a pointer to remain valid, you need to make one explicitly, +for example with ``ptr = ffi.new("char[]", x)``.) + You can also pass unicode strings as ``wchar_t *`` or ``char16_t *`` or ``char32_t *`` arguments. Note that the C language makes no difference between argument declarations that diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst index aa7f2fe..ff2b7ba 100644 --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -2,6 +2,15 @@ What's New ====================== +v1.15.1 +======= + +* If you call `ffi.embedding_api()` but don't write any `extern "Python"` + function there, then the resulting C code would fail an assert. Fixed. + +* Updated Windows/arm64 embedded libffi static lib to v3.4.2, and scripted + to ease future updates (thanks Niyas Sait!) + v1.15.0 ======= @@ -11,7 +11,7 @@ sources = ['c/_cffi_backend.c'] libraries = ['ffi'] include_dirs = ['/usr/include/ffi', '/usr/include/libffi'] # may be changed by pkg-config -define_macros = [] +define_macros = [('FFI_BUILDING', '1')] # for linking with libffi static library library_dirs = [] extra_compile_args = [] extra_link_args = [] @@ -156,6 +156,11 @@ if 'freebsd' in sys.platform: include_dirs.append('/usr/local/include') library_dirs.append('/usr/local/lib') +forced_extra_objs = os.environ.get('CFFI_FORCE_STATIC', []) +if forced_extra_objs: + forced_extra_objs = forced_extra_objs.split(';') + + if __name__ == '__main__': from setuptools import setup, Distribution, Extension @@ -186,7 +191,7 @@ Contact `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_ """, - version='1.15.0', + version='1.15.1', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h', '_cffi_errors.h']} @@ -209,6 +214,7 @@ Contact library_dirs=library_dirs, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, + extra_objects=forced_extra_objs, )] if cpython else [], install_requires=[ diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index b4bb23d..84d8db6 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -5,7 +5,7 @@ import math, os, sys import ctypes.util from cffi.backend_ctypes import CTypesBackend from testing.udir import udir -from testing.support import FdWriteCapture, StdErrCapture +from testing.support import FdWriteCapture, StdErrCapture, is_musl from .backend_tests import needs_dlopen_none try: @@ -13,6 +13,12 @@ try: except ImportError: from io import StringIO +try: + from packaging.tags import platform_tags + _platform_tags_cached = set(platform_tags()) + _is_musl = any(t.startswith('musllinux') for t in _platform_tags_cached) +except ImportError: + _is_musl = False lib_m = 'm' if sys.platform == 'win32': @@ -20,6 +26,8 @@ if sys.platform == 'win32': import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' +elif is_musl: + lib_m = 'c' class TestFunction(object): Backend = CTypesBackend @@ -165,11 +173,15 @@ class TestFunction(object): ffi.cast("long long", 168)) ffi.C.fprintf(ffi.C.stderr, b"hello %p\n", ffi.NULL) res = fd.getvalue() + if is_musl: + nil_repr = b'0' + else: + nil_repr = b'(nil)' assert res == (b"hello with no arguments\n" b"hello, world!\n" b"hello, world2!\n" b"hello int 42 long 84 long long 168\n" - b"hello (nil)\n") + b"hello " + nil_repr + b"\n") def test_must_specify_type_of_vararg(self): ffi = FFI(backend=self.Backend()) @@ -265,7 +277,7 @@ class TestFunction(object): assert res == 5 def test_write_variable(self): - if not sys.platform.startswith('linux'): + if not sys.platform.startswith('linux') or _is_musl: py.test.skip("probably no symbol 'stdout' in the lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index ffad879..bbdab8c 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -2,7 +2,7 @@ import py, sys, os import subprocess, weakref from cffi import FFI from cffi.backend_ctypes import CTypesBackend -from testing.support import u +from testing.support import u, is_musl SOURCE = """\ @@ -388,7 +388,7 @@ class TestOwnLib(object): def test_dlopen_handle(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") - if sys.platform == 'win32': + if sys.platform == 'win32' or is_musl: py.test.skip("uses 'dl' explicitly") if self.__class__.Backend is CTypesBackend: py.test.skip("not for the ctypes backend") diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py index a5e4587..5d93a8d 100644 --- a/testing/cffi0/test_parsing.py +++ b/testing/cffi0/test_parsing.py @@ -1,6 +1,7 @@ import py, sys, re from cffi import FFI, FFIError, CDefError, VerificationError from .backend_tests import needs_dlopen_none +from testing.support import is_musl class FakeBackend(object): @@ -80,6 +81,9 @@ if sys.platform == 'win32': import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' +elif is_musl: + lib_m = 'c' + def test_simple(): ffi = FFI(backend=FakeBackend()) diff --git a/testing/cffi0/test_unicode_literals.py b/testing/cffi0/test_unicode_literals.py index 7b0a5cc..8838de5 100644 --- a/testing/cffi0/test_unicode_literals.py +++ b/testing/cffi0/test_unicode_literals.py @@ -9,6 +9,8 @@ from __future__ import unicode_literals # import sys, math from cffi import FFI +from testing.support import is_musl + lib_m = "m" if sys.platform == 'win32': @@ -16,6 +18,8 @@ if sys.platform == 'win32': import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' +elif is_musl: + lib_m = 'c' def test_cast(): diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py index 3a1c0b9..de1608d 100644 --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -3,7 +3,7 @@ import pytest import sys, os, math, weakref from cffi import FFI, VerificationError, VerificationMissing, model, FFIError from testing.support import * -from testing.support import extra_compile_args +from testing.support import extra_compile_args, is_musl lib_m = ['m'] @@ -1609,7 +1609,7 @@ def test_keepalive_ffi(): assert func() == 42 def test_FILE_stored_in_stdout(): - if not sys.platform.startswith('linux'): + if not sys.platform.startswith('linux') or is_musl: py.test.skip("likely, we cannot assign to stdout") ffi = FFI() ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);") diff --git a/testing/cffi1/test_cffi_binary.py b/testing/cffi1/test_cffi_binary.py index 7cfbace..45421ed 100644 --- a/testing/cffi1/test_cffi_binary.py +++ b/testing/cffi1/test_cffi_binary.py @@ -1,10 +1,11 @@ import py, sys, os import _cffi_backend +from testing.support import is_musl def test_no_unknown_exported_symbols(): if not hasattr(_cffi_backend, '__file__'): py.test.skip("_cffi_backend module is built-in") - if not sys.platform.startswith('linux'): + if not sys.platform.startswith('linux') or is_musl: py.test.skip("linux-only") g = os.popen("objdump -T '%s'" % _cffi_backend.__file__, 'r') for line in g: @@ -17,6 +18,9 @@ def test_no_unknown_exported_symbols(): name = line.split()[-1] if name.startswith('_') or name.startswith('.'): continue - if name not in ('init_cffi_backend', 'PyInit__cffi_backend'): + # a statically-linked libffi will always appear here without header hackage, ignore it if it's internal + if name.startswith('ffi_') and 'Base' in line: + continue + if name not in ('init_cffi_backend', 'PyInit__cffi_backend', 'cffistatic_ffi_call'): raise Exception("Unexpected exported name %r" % (name,)) g.close() diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py index 2ae0dd1..45dd70c 100644 --- a/testing/cffi1/test_re_python.py +++ b/testing/cffi1/test_re_python.py @@ -3,7 +3,7 @@ import py from cffi import FFI from cffi import recompiler, ffiplatform, VerificationMissing from testing.udir import udir -from testing.support import u +from testing.support import u, is_musl def setup_module(mod): @@ -269,7 +269,7 @@ def test_selfref(): def test_dlopen_handle(): import _cffi_backend from re_python_pysrc import ffi - if sys.platform == 'win32': + if sys.platform == 'win32' or is_musl: py.test.skip("uses 'dl' explicitly") ffi1 = FFI() ffi1.cdef("""void *dlopen(const char *filename, int flags); diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py index 33244cc..45df2b3 100644 --- a/testing/cffi1/test_verify1.py +++ b/testing/cffi1/test_verify1.py @@ -4,7 +4,7 @@ from cffi import FFI, FFIError, VerificationError, VerificationMissing, model from cffi import CDefError from cffi import recompiler from testing.support import * -from testing.support import _verify, extra_compile_args +from testing.support import _verify, extra_compile_args, is_musl import _cffi_backend lib_m = ['m'] @@ -1571,7 +1571,7 @@ def test_keepalive_ffi(): assert func() == 42 def test_FILE_stored_in_stdout(): - if not sys.platform.startswith('linux'): + if not sys.platform.startswith('linux') or is_musl: py.test.skip("likely, we cannot assign to stdout") ffi = FFI() ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);") diff --git a/testing/embedding/empty-test.c b/testing/embedding/empty-test.c new file mode 100644 index 0000000..b00dd50 --- /dev/null +++ b/testing/embedding/empty-test.c @@ -0,0 +1,11 @@ +#include <stdio.h> + +void initialize_my_empty_cffi(void); + +int main(void) +{ + initialize_my_empty_cffi(); + printf("OK\n"); + return 0; +} + diff --git a/testing/embedding/empty.py b/testing/embedding/empty.py index aa8d830..1093505 100644 --- a/testing/embedding/empty.py +++ b/testing/embedding/empty.py @@ -4,7 +4,14 @@ ffi = cffi.FFI() ffi.embedding_api("") -ffi.set_source("_empty_cffi", "") +ffi.set_source("_empty_cffi", """ +void initialize_my_empty_cffi(void) { + if (cffi_start_python() != 0) { + printf("oops, cffi_start_python() returned non-0\\n"); + abort(); + } +} +""") fn = ffi.compile(verbose=True) print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py index 8d2e776..b29afd2 100644 --- a/testing/embedding/test_basic.py +++ b/testing/embedding/test_basic.py @@ -180,6 +180,9 @@ if sys.platform == 'win32': class TestBasic(EmbeddingTests): def test_empty(self): empty_cffi = self.prepare_module('empty') + self.compile('empty-test', [empty_cffi]) + output = self.execute('empty-test') + assert output == 'OK\n' def test_basic(self): add1_cffi = self.prepare_module('add1') diff --git a/testing/support.py b/testing/support.py index 6339a94..a65375e 100644 --- a/testing/support.py +++ b/testing/support.py @@ -117,3 +117,12 @@ else: extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', '-Wno-unused-parameter', '-Wno-unreachable-code'] + +is_musl = False +if sys.platform == 'linux': + try: + from packaging.tags import platform_tags + is_musl = any(t.startswith('musllinux') for t in platform_tags()) + del platform_tags + except ImportError: + pass |