290 lines
11 KiB
Diff
290 lines
11 KiB
Diff
![]() |
diff -ru a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
|
||
|
--- a/Doc/library/ctypes.rst 2020-03-10 07:11:12.000000000 +0100
|
||
|
+++ b/Doc/library/ctypes.rst 2020-07-14 08:10:10.000000000 +0200
|
||
|
@@ -1551,6 +1551,13 @@
|
||
|
value usable as argument (integer, string, ctypes instance). This allows
|
||
|
defining adapters that can adapt custom objects as function parameters.
|
||
|
|
||
|
+ .. attribute:: variadic
|
||
|
+
|
||
|
+ Assign a boolean to specify that the function takes a variable number of
|
||
|
+ arguments. This does not matter on most platforms, but for Apple arm64
|
||
|
+ platforms variadic functions have a different calling convention than
|
||
|
+ normal functions.
|
||
|
+
|
||
|
.. attribute:: errcheck
|
||
|
|
||
|
Assign a Python function or another callable to this attribute. The
|
||
|
diff -ru a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
|
||
|
--- a/Modules/_ctypes/_ctypes.c 2020-03-10 07:11:12.000000000 +0100
|
||
|
+++ b/Modules/_ctypes/_ctypes.c 2020-07-14 08:14:41.000000000 +0200
|
||
|
@@ -3175,6 +3175,35 @@
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
+PyCFuncPtr_set_variadic(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
|
||
|
+{
|
||
|
+ StgDictObject *dict = PyObject_stgdict((PyObject *)self);
|
||
|
+ assert(dict);
|
||
|
+ int r = PyObject_IsTrue(ob);
|
||
|
+ if (r == 1) {
|
||
|
+ dict->flags |= FUNCFLAG_VARIADIC;
|
||
|
+ return 0;
|
||
|
+ } else if (r == 0) {
|
||
|
+ dict->flags &= ~FUNCFLAG_VARIADIC;
|
||
|
+ return 0;
|
||
|
+ } else {
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static PyObject *
|
||
|
+PyCFuncPtr_get_variadic(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
|
||
|
+{
|
||
|
+ StgDictObject *dict = PyObject_stgdict((PyObject *)self);
|
||
|
+ assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
|
||
|
+ if (dict->flags & FUNCFLAG_VARIADIC)
|
||
|
+ Py_RETURN_TRUE;
|
||
|
+ else
|
||
|
+ Py_RETURN_FALSE;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static int
|
||
|
PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
|
||
|
{
|
||
|
PyObject *converters;
|
||
|
@@ -5632,6 +5661,7 @@
|
||
|
PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
|
||
|
PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
|
||
|
PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
|
||
|
+ PyModule_AddObject(m, "FUNCFLAG_VARIADIC", PyLong_FromLong(FUNCFLAG_VARIADIC));
|
||
|
PyModule_AddStringConstant(m, "__version__", "1.1.0");
|
||
|
|
||
|
PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove));
|
||
|
diff -ru a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
|
||
|
--- a/Modules/_ctypes/callproc.c 2020-03-10 07:11:12.000000000 +0100
|
||
|
+++ b/Modules/_ctypes/callproc.c 2020-07-14 08:18:33.000000000 +0200
|
||
|
@@ -767,7 +767,8 @@
|
||
|
ffi_type **atypes,
|
||
|
ffi_type *restype,
|
||
|
void *resmem,
|
||
|
- int argcount)
|
||
|
+ int argcount,
|
||
|
+ int argtypecount)
|
||
|
{
|
||
|
PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
|
||
|
PyObject *error_object = NULL;
|
||
|
@@ -793,15 +794,38 @@
|
||
|
if ((flags & FUNCFLAG_CDECL) == 0)
|
||
|
cc = FFI_STDCALL;
|
||
|
#endif
|
||
|
- if (FFI_OK != ffi_prep_cif(&cif,
|
||
|
- cc,
|
||
|
- argcount,
|
||
|
- restype,
|
||
|
- atypes)) {
|
||
|
- PyErr_SetString(PyExc_RuntimeError,
|
||
|
- "ffi_prep_cif failed");
|
||
|
- return -1;
|
||
|
- }
|
||
|
+#if HAVE_FFI_PREP_CIF_VAR
|
||
|
+ /* Everyone SHOULD set f.variadic=True on variadic function pointers, but
|
||
|
+ * lots of existing code will not. If there's at least one arg and more
|
||
|
+ * args are passed than are defined in the prototype, then it must be a
|
||
|
+ * variadic function. */
|
||
|
+ if ((flags & FUNCFLAG_VARIADIC) ||
|
||
|
+ (argtypecount != 0 && argcount > argtypecount))
|
||
|
+ {
|
||
|
+ if (FFI_OK != ffi_prep_cif_var(&cif,
|
||
|
+ cc,
|
||
|
+ argtypecount,
|
||
|
+ argcount,
|
||
|
+ restype,
|
||
|
+ atypes)) {
|
||
|
+ PyErr_SetString(PyExc_RuntimeError,
|
||
|
+ "ffi_prep_cif_var failed");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+#endif
|
||
|
+ if (FFI_OK != ffi_prep_cif(&cif,
|
||
|
+ cc,
|
||
|
+ argcount,
|
||
|
+ restype,
|
||
|
+ atypes)) {
|
||
|
+ PyErr_SetString(PyExc_RuntimeError,
|
||
|
+ "ffi_prep_cif failed");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+#if HAVE_FFI_PREP_CIF_VAR
|
||
|
+ }
|
||
|
+#endif
|
||
|
|
||
|
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
|
||
|
error_object = _ctypes_get_errobj(&space);
|
||
|
@@ -1185,9 +1209,8 @@
|
||
|
|
||
|
if (-1 == _call_function_pointer(flags, pProc, avalues, atypes,
|
||
|
rtype, resbuf,
|
||
|
- Py_SAFE_DOWNCAST(argcount,
|
||
|
- Py_ssize_t,
|
||
|
- int)))
|
||
|
+ Py_SAFE_DOWNCAST(argcount, Py_ssize_t, int),
|
||
|
+ Py_SAFE_DOWNCAST(argtype_count, Py_ssize_t, int)))
|
||
|
goto cleanup;
|
||
|
|
||
|
#ifdef WORDS_BIGENDIAN
|
||
|
diff -ru a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
|
||
|
--- a/Modules/_ctypes/ctypes.h 2020-03-10 07:11:12.000000000 +0100
|
||
|
+++ b/Modules/_ctypes/ctypes.h 2020-07-14 08:30:53.000000000 +0200
|
||
|
@@ -285,6 +285,7 @@
|
||
|
#define FUNCFLAG_PYTHONAPI 0x4
|
||
|
#define FUNCFLAG_USE_ERRNO 0x8
|
||
|
#define FUNCFLAG_USE_LASTERROR 0x10
|
||
|
+#define FUNCFLAG_VARIADIC 0x20
|
||
|
|
||
|
#define TYPEFLAG_ISPOINTER 0x100
|
||
|
#define TYPEFLAG_HASPOINTER 0x200
|
||
|
diff -ru a/configure b/configure
|
||
|
--- a/configure 2020-03-10 07:11:12.000000000 +0100
|
||
|
+++ b/configure 2020-07-14 08:03:27.000000000 +0200
|
||
|
@@ -3374,7 +3374,7 @@
|
||
|
# has no effect, don't bother defining them
|
||
|
Darwin/[6789].*)
|
||
|
define_xopen_source=no;;
|
||
|
- Darwin/1[0-9].*)
|
||
|
+ Darwin/[12][0-9].*)
|
||
|
define_xopen_source=no;;
|
||
|
# On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but
|
||
|
# used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined
|
||
|
@@ -9251,6 +9251,9 @@
|
||
|
ppc)
|
||
|
MACOSX_DEFAULT_ARCH="ppc64"
|
||
|
;;
|
||
|
+ arm64)
|
||
|
+ MACOSX_DEFAULT_ARCH="arm64"
|
||
|
+ ;;
|
||
|
*)
|
||
|
as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5
|
||
|
;;
|
||
|
diff -ru a/configure.ac b/configure.ac
|
||
|
--- a/configure.ac 2020-03-10 07:11:12.000000000 +0100
|
||
|
+++ b/configure.ac 2020-07-14 08:03:27.000000000 +0200
|
||
|
@@ -2456,6 +2456,9 @@
|
||
|
ppc)
|
||
|
MACOSX_DEFAULT_ARCH="ppc64"
|
||
|
;;
|
||
|
+ arm64)
|
||
|
+ MACOSX_DEFAULT_ARCH="arm64"
|
||
|
+ ;;
|
||
|
*)
|
||
|
AC_MSG_ERROR([Unexpected output of 'arch' on OSX])
|
||
|
;;
|
||
|
diff -ru a/setup.py b/setup.py
|
||
|
--- a/setup.py 2020-03-10 07:11:12.000000000 +0100
|
||
|
+++ b/setup.py 2020-07-14 08:28:12.000000000 +0200
|
||
|
@@ -141,6 +141,13 @@
|
||
|
os.unlink(tmpfile)
|
||
|
|
||
|
return MACOS_SDK_ROOT
|
||
|
+
|
||
|
+def is_macosx_at_least(vers):
|
||
|
+ if host_platform == 'darwin':
|
||
|
+ dep_target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
|
||
|
+ if dep_target:
|
||
|
+ return tuple(map(int, dep_target.split('.'))) >= vers
|
||
|
+ return False
|
||
|
|
||
|
def is_macosx_sdk_path(path):
|
||
|
"""
|
||
|
@@ -150,6 +157,13 @@
|
||
|
or path.startswith('/System/')
|
||
|
or path.startswith('/Library/') )
|
||
|
|
||
|
+def grep_headers_for(function, headers):
|
||
|
+ for header in headers:
|
||
|
+ with open(header, 'r') as f:
|
||
|
+ if function in f.read():
|
||
|
+ return True
|
||
|
+ return False
|
||
|
+
|
||
|
def find_file(filename, std_dirs, paths):
|
||
|
"""Searches for the directory where a given file is located,
|
||
|
and returns a possibly-empty list of additional directories, or None
|
||
|
@@ -1972,7 +1986,11 @@
|
||
|
return True
|
||
|
|
||
|
def detect_ctypes(self, inc_dirs, lib_dirs):
|
||
|
- self.use_system_libffi = False
|
||
|
+ if not sysconfig.get_config_var("LIBFFI_INCLUDEDIR") and is_macosx_at_least((10,15)):
|
||
|
+ self.use_system_libffi = True
|
||
|
+ else:
|
||
|
+ self.use_system_libffi = '--with-system-ffi' in sysconfig.get_config_var("CONFIG_ARGS")
|
||
|
+
|
||
|
include_dirs = []
|
||
|
extra_compile_args = []
|
||
|
extra_link_args = []
|
||
|
@@ -2016,32 +2034,48 @@
|
||
|
ext_test = Extension('_ctypes_test',
|
||
|
sources=['_ctypes/_ctypes_test.c'],
|
||
|
libraries=['m'])
|
||
|
+ ffi_inc = sysconfig.get_config_var("LIBFFI_INCLUDEDIR")
|
||
|
+ ffi_lib = None
|
||
|
+
|
||
|
self.extensions.extend([ext, ext_test])
|
||
|
|
||
|
if host_platform == 'darwin':
|
||
|
- if '--with-system-ffi' not in sysconfig.get_config_var("CONFIG_ARGS"):
|
||
|
+ if not self.use_system_libffi:
|
||
|
return
|
||
|
- # OS X 10.5 comes with libffi.dylib; the include files are
|
||
|
- # in /usr/include/ffi
|
||
|
- inc_dirs.append('/usr/include/ffi')
|
||
|
-
|
||
|
- ffi_inc = [sysconfig.get_config_var("LIBFFI_INCLUDEDIR")]
|
||
|
- if not ffi_inc or ffi_inc[0] == '':
|
||
|
- ffi_inc = find_file('ffi.h', [], inc_dirs)
|
||
|
- if ffi_inc is not None:
|
||
|
- ffi_h = ffi_inc[0] + '/ffi.h'
|
||
|
+ ffi_in_sdk = os.path.join(macosx_sdk_root(), "usr/include/ffi")
|
||
|
+ if os.path.exists(ffi_in_sdk):
|
||
|
+ ffi_inc = ffi_in_sdk
|
||
|
+ ffi_lib = 'ffi'
|
||
|
+ else:
|
||
|
+ # OS X 10.5 comes with libffi.dylib; the include files are
|
||
|
+ # in /usr/include/ffi
|
||
|
+ ffi_inc_dirs.append('/usr/include/ffi')
|
||
|
+
|
||
|
+ if not ffi_inc:
|
||
|
+ found = find_file('ffi.h', [], ffi_inc_dirs)
|
||
|
+ if found:
|
||
|
+ ffi_inc = found[0]
|
||
|
+ if ffi_inc:
|
||
|
+ ffi_h = ffi_inc + '/ffi.h'
|
||
|
if not os.path.exists(ffi_h):
|
||
|
ffi_inc = None
|
||
|
print('Header file {} does not exist'.format(ffi_h))
|
||
|
- ffi_lib = None
|
||
|
- if ffi_inc is not None:
|
||
|
+ if ffi_lib is None and ffi_inc:
|
||
|
for lib_name in ('ffi', 'ffi_pic'):
|
||
|
if (self.compiler.find_library_file(lib_dirs, lib_name)):
|
||
|
ffi_lib = lib_name
|
||
|
break
|
||
|
|
||
|
if ffi_inc and ffi_lib:
|
||
|
- ext.include_dirs.extend(ffi_inc)
|
||
|
+ ffi_headers = glob(os.path.join(ffi_inc, '*.h'))
|
||
|
+ if grep_headers_for('ffi_closure_alloc', ffi_headers):
|
||
|
+ try:
|
||
|
+ sources.remove('_ctypes/malloc_closure.c')
|
||
|
+ except ValueError:
|
||
|
+ pass
|
||
|
+ if grep_headers_for('ffi_prep_cif_var', ffi_headers):
|
||
|
+ ext.extra_compile_args.append("-DHAVE_FFI_PREP_CIF_VAR=1")
|
||
|
+ ext.include_dirs.append(ffi_inc)
|
||
|
ext.libraries.append(ffi_lib)
|
||
|
self.use_system_libffi = True
|
||
|
|