[3.9] bpo-41100: Support macOS 11 and Apple Silicon (GH-22855) (GH-23295)
* [3.9] bpo-41100: Support macOS 11 and Apple Silicon (GH-22855)
Co-authored-by: Lawrence D’Anna <lawrence_danna@apple.com>
* Add support for macOS 11 and Apple Silicon (aka arm64)
As a side effect of this work use the system copy of libffi on macOS, and remove the vendored copy
* Support building on recent versions of macOS while deploying to older versions
This allows building installers on macOS 11 while still supporting macOS 10.9..
(cherry picked from commit 41761933c1c30bb6003b65eef1ba23a83db4eae4)
Co-authored-by: Ronald Oussoren <ronaldoussoren@mac.com>
* Back port of changes to _decimal to support arm64
* temp_dir is in test.support in 3.9
diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
index 2abfa67..39cace5 100644
--- a/Modules/_ctypes/callbacks.c
+++ b/Modules/_ctypes/callbacks.c
@@ -1,6 +1,8 @@
#include "Python.h"
#include "frameobject.h"
+#include <stdbool.h>
+
#include <ffi.h>
#ifdef MS_WIN32
#include <windows.h>
@@ -18,7 +20,7 @@
Py_XDECREF(self->callable);
Py_XDECREF(self->restype);
if (self->pcl_write)
- ffi_closure_free(self->pcl_write);
+ Py_ffi_closure_free(self->pcl_write);
PyObject_GC_Del(self);
}
@@ -361,8 +363,7 @@
assert(CThunk_CheckExact((PyObject *)p));
- p->pcl_write = ffi_closure_alloc(sizeof(ffi_closure),
- &p->pcl_exec);
+ p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
if (p->pcl_write == NULL) {
PyErr_NoMemory();
goto error;
@@ -408,13 +409,35 @@
"ffi_prep_cif failed with %d", result);
goto error;
}
-#if defined(X86_DARWIN) || defined(POWERPC_DARWIN)
- result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
-#else
- result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
- p,
- p->pcl_exec);
+#if HAVE_FFI_PREP_CLOSURE_LOC
+# if USING_APPLE_OS_LIBFFI
+# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# else
+# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME 1
+# endif
+ if (HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME) {
+ result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
+ p,
+ p->pcl_exec);
+ } else
#endif
+ {
+#if USING_APPLE_OS_LIBFFI && defined(__arm64__)
+ PyErr_Format(PyExc_NotImplementedError, "ffi_prep_closure_loc() is missing");
+ goto error;
+#else
+#ifdef MACOSX
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
+
+#ifdef MACOSX
+ #pragma clang diagnostic pop
+#endif
+
+#endif
+ }
if (result != FFI_OK) {
PyErr_Format(PyExc_RuntimeError,
"ffi_prep_closure failed with %d", result);
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index 6030cc3..b0a36a3 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -57,6 +57,8 @@
#include "Python.h"
#include "structmember.h" // PyMemberDef
+#include <stdbool.h>
+
#ifdef MS_WIN32
#include <windows.h>
#include <tchar.h>
@@ -64,6 +66,10 @@
#include "ctypes_dlfcn.h"
#endif
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+
#ifdef MS_WIN32
#include <malloc.h>
#endif
@@ -812,7 +818,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;
@@ -835,14 +842,70 @@
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 USING_APPLE_OS_LIBFFI
+# define HAVE_FFI_PREP_CIF_VAR_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# elif HAVE_FFI_PREP_CIF_VAR
+# define HAVE_FFI_PREP_CIF_VAR_RUNTIME true
+# else
+# define HAVE_FFI_PREP_CIF_VAR_RUNTIME false
+# endif
+
+ /* Even on Apple-arm64 the calling convention for variadic functions conincides
+ * with the standard calling convention in the case that the function called
+ * only with its fixed arguments. Thus, we do not need a special flag to be
+ * set on variadic functions. We treat a function as variadic if it is called
+ * with a nonzero number of variadic arguments */
+ bool is_variadic = (argtypecount != 0 && argcount > argtypecount);
+ (void) is_variadic;
+
+#if defined(__APPLE__) && defined(__arm64__)
+ if (is_variadic) {
+ if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
+ } else {
+ PyErr_SetString(PyExc_NotImplementedError, "ffi_prep_cif_var() is missing");
+ return -1;
+ }
+ }
+#endif
+
+#if HAVE_FFI_PREP_CIF_VAR
+ if (is_variadic) {
+ if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
+ 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 {
+ if (FFI_OK != ffi_prep_cif(&cif,
+ cc,
+ argcount,
+ restype,
+ atypes)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "ffi_prep_cif 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 (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
@@ -1212,9 +1275,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
@@ -1398,6 +1460,42 @@
}
#else
+#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
+static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args)
+{
+ PyObject *name, *name2;
+ char *name_str;
+
+ if (__builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)) {
+ int r;
+
+ if (!PyArg_ParseTuple(args, "O", &name))
+ return NULL;
+
+ if (name == Py_None)
+ Py_RETURN_FALSE;
+
+ if (PyUnicode_FSConverter(name, &name2) == 0)
+ return NULL;
+ name_str = PyBytes_AS_STRING(name2);
+
+ r = _dyld_shared_cache_contains_path(name_str);
+ Py_DECREF(name2);
+
+ if (r) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+
+ } else {
+ PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing");
+ return NULL;
+ }
+
+ }
+#endif
+
static PyObject *py_dl_open(PyObject *self, PyObject *args)
{
PyObject *name, *name2;
@@ -1887,6 +1985,8 @@
return Py_BuildValue("siN", dict->format, dict->ndim, shape);
}
+
+
PyMethodDef _ctypes_module_methods[] = {
{"get_errno", get_errno, METH_NOARGS},
{"set_errno", set_errno, METH_VARARGS},
@@ -1909,6 +2009,9 @@
{"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"},
{"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"},
#endif
+#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
+ {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"},
+#endif
{"alignment", align_func, METH_O, alignment_doc},
{"sizeof", sizeof_func, METH_O, sizeof_doc},
{"byref", byref, METH_VARARGS, byref_doc},
diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
index 1effccf..3f20031 100644
--- a/Modules/_ctypes/ctypes.h
+++ b/Modules/_ctypes/ctypes.h
@@ -366,6 +366,14 @@
extern PyObject *ComError;
#endif
+#if USING_MALLOC_CLOSURE_DOT_C
+void Py_ffi_closure_free(void *p);
+void *Py_ffi_closure_alloc(size_t size, void** codeloc);
+#else
+#define Py_ffi_closure_free ffi_closure_free
+#define Py_ffi_closure_alloc ffi_closure_alloc
+#endif
+
/*
Local Variables:
compile-command: "python setup.py -q build install --home ~"
diff --git a/Modules/_ctypes/malloc_closure.c b/Modules/_ctypes/malloc_closure.c
index f9cdb33..4f220e4 100644
--- a/Modules/_ctypes/malloc_closure.c
+++ b/Modules/_ctypes/malloc_closure.c
@@ -89,16 +89,27 @@
/******************************************************************/
/* put the item back into the free list */
-void ffi_closure_free(void *p)
+void Py_ffi_closure_free(void *p)
{
+#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
+ if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
+ ffi_closure_free(p);
+ return;
+ }
+#endif
ITEM *item = (ITEM *)p;
item->next = free_list;
free_list = item;
}
/* return one item from the free list, allocating more if needed */
-void *ffi_closure_alloc(size_t ignored, void** codeloc)
+void *Py_ffi_closure_alloc(size_t size, void** codeloc)
{
+#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
+ if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
+ return ffi_closure_alloc(size, codeloc);
+ }
+#endif
ITEM *item;
if (!free_list)
more_core();
diff --git a/Modules/_decimal/libmpdec/mpdecimal.h b/Modules/_decimal/libmpdec/mpdecimal.h
index 2815a8c..5a24396 100644
--- a/Modules/_decimal/libmpdec/mpdecimal.h
+++ b/Modules/_decimal/libmpdec/mpdecimal.h
@@ -121,6 +121,9 @@
#elif defined(__x86_64__)
#define CONFIG_64
#define ASM
+ #elif defined(__arm64__)
+ #define CONFIG_64
+ #define ANSI
#else
#error "unknown architecture for universal build."
#endif
diff --git a/Modules/getpath.c b/Modules/getpath.c
index a84c858..4035819 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -923,11 +923,7 @@
calculate_program_macos(wchar_t **abs_path_p)
{
char execpath[MAXPATHLEN + 1];
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
-#else
- unsigned long nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
-#endif
/* On Mac OS X, if a script uses an interpreter of the form
"#!/opt/python2.3/bin/python", the kernel only passes "python"
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 8e14ffc..12f72f5 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -7,18 +7,6 @@
of the compiler used. Different compilers define their own feature
test macro, e.g. '_MSC_VER'. */
-#ifdef __APPLE__
- /*
- * Step 1 of support for weak-linking a number of symbols existing on
- * OSX 10.4 and later, see the comment in the #ifdef __APPLE__ block
- * at the end of this file for more information.
- */
-# pragma weak lchown
-# pragma weak statvfs
-# pragma weak fstatvfs
-
-#endif /* __APPLE__ */
-
#define PY_SSIZE_T_CLEAN
#include "Python.h"
@@ -50,6 +38,127 @@
#include <stdio.h> /* needed for ctermid() */
+/*
+ * A number of APIs are available on macOS from a certain macOS version.
+ * To support building with a new SDK while deploying to older versions
+ * the availability test is split into two:
+ * - HAVE_<FUNCTION>: The configure check for compile time availability
+ * - HAVE_<FUNCTION>_RUNTIME: Runtime check for availability
+ *
+ * The latter is always true when not on macOS, or when using a compiler
+ * that does not support __has_builtin (older versions of Xcode).
+ *
+ * Due to compiler restrictions there is one valid use of HAVE_<FUNCTION>_RUNTIME:
+ * if (HAVE_<FUNCTION>_RUNTIME) { ... }
+ *
+ * In mixing the test with other tests or using negations will result in compile
+ * errors.
+ */
+#if defined(__APPLE__)
+
+#if defined(__has_builtin) && __has_builtin(__builtin_available)
+# define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_FCHOWNAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_LINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_FDOPENDIR_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_MKDIRAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_RENAMEAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_UNLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_OPENAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_READLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_SYMLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
+# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
+# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)
+
+# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *)
+
+#else /* Xcode 8 or earlier */
+
+ /* __builtin_available is not present in these compilers, but
+ * some of the symbols might be weak linked (10.10 SDK or later
+ * deploying on 10.9.
+ *
+ * Fall back to the older style of availability checking for
+ * symbols introduced in macOS 10.10.
+ */
+
+# ifdef HAVE_FSTATAT
+# define HAVE_FSTATAT_RUNTIME (fstatat != NULL)
+# endif
+
+# ifdef HAVE_FACCESSAT
+# define HAVE_FACCESSAT_RUNTIME (faccessat != NULL)
+# endif
+
+# ifdef HAVE_FCHMODAT
+# define HAVE_FCHMODAT_RUNTIME (fchmodat != NULL)
+# endif
+
+# ifdef HAVE_FCHOWNAT
+# define HAVE_FCHOWNAT_RUNTIME (fchownat != NULL)
+# endif
+
+# ifdef HAVE_LINKAT
+# define HAVE_LINKAT_RUNTIME (linkat != NULL)
+# endif
+
+# ifdef HAVE_FDOPENDIR
+# define HAVE_FDOPENDIR_RUNTIME (fdopendir != NULL)
+# endif
+
+# ifdef HAVE_MKDIRAT
+# define HAVE_MKDIRAT_RUNTIME (mkdirat != NULL)
+# endif
+
+# ifdef HAVE_RENAMEAT
+# define HAVE_RENAMEAT_RUNTIME (renameat != NULL)
+# endif
+
+# ifdef HAVE_UNLINKAT
+# define HAVE_UNLINKAT_RUNTIME (unlinkat != NULL)
+# endif
+
+# ifdef HAVE_OPENAT
+# define HAVE_OPENAT_RUNTIME (openat != NULL)
+# endif
+
+# ifdef HAVE_READLINKAT
+# define HAVE_READLINKAT_RUNTIME (readlinkat != NULL)
+# endif
+
+# ifdef HAVE_SYMLINKAT
+# define HAVE_SYMLINKAT_RUNTIME (symlinkat != NULL)
+# endif
+
+#endif
+
+#ifdef HAVE_FUTIMESAT
+/* Some of the logic for weak linking depends on this assertion */
+# error "HAVE_FUTIMESAT unexpectedly defined"
+#endif
+
+#else
+# define HAVE_FSTATAT_RUNTIME 1
+# define HAVE_FACCESSAT_RUNTIME 1
+# define HAVE_FCHMODAT_RUNTIME 1
+# define HAVE_FCHOWNAT_RUNTIME 1
+# define HAVE_LINKAT_RUNTIME 1
+# define HAVE_FDOPENDIR_RUNTIME 1
+# define HAVE_MKDIRAT_RUNTIME 1
+# define HAVE_RENAMEAT_RUNTIME 1
+# define HAVE_UNLINKAT_RUNTIME 1
+# define HAVE_OPENAT_RUNTIME 1
+# define HAVE_READLINKAT_RUNTIME 1
+# define HAVE_SYMLINKAT_RUNTIME 1
+# define HAVE_FUTIMENS_RUNTIME 1
+# define HAVE_UTIMENSAT_RUNTIME 1
+# define HAVE_PWRITEV_RUNTIME 1
+#endif
+
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -2308,6 +2417,10 @@
STRUCT_STAT st;
int result;
+#ifdef HAVE_FSTATAT
+ int fstatat_unavailable = 0;
+#endif
+
#if !defined(MS_WINDOWS) && !defined(HAVE_FSTATAT) && !defined(HAVE_LSTAT)
if (follow_symlinks_specified(function_name, follow_symlinks))
return NULL;
@@ -2334,15 +2447,27 @@
else
#endif /* HAVE_LSTAT */
#ifdef HAVE_FSTATAT
- if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks)
- result = fstatat(dir_fd, path->narrow, &st,
+ if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
+ if (HAVE_FSTATAT_RUNTIME) {
+ result = fstatat(dir_fd, path->narrow, &st,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
- else
+
+ } else {
+ fstatat_unavailable = 1;
+ }
+ } else
#endif /* HAVE_FSTATAT */
result = STAT(path->narrow, &st);
#endif /* MS_WINDOWS */
Py_END_ALLOW_THREADS
+#ifdef HAVE_FSTATAT
+ if (fstatat_unavailable) {
+ argument_unavailable_error("stat", "dir_fd");
+ return NULL;
+ }
+#endif
+
if (result != 0) {
return path_error(path);
}
@@ -2760,6 +2885,10 @@
int result;
#endif
+#ifdef HAVE_FACCESSAT
+ int faccessat_unavailable = 0;
+#endif
+
#ifndef HAVE_FACCESSAT
if (follow_symlinks_specified("access", follow_symlinks))
return -1;
@@ -2794,17 +2923,40 @@
if ((dir_fd != DEFAULT_DIR_FD) ||
effective_ids ||
!follow_symlinks) {
- int flags = 0;
- if (!follow_symlinks)
- flags |= AT_SYMLINK_NOFOLLOW;
- if (effective_ids)
- flags |= AT_EACCESS;
- result = faccessat(dir_fd, path->narrow, mode, flags);
+
+ if (HAVE_FACCESSAT_RUNTIME) {
+ int flags = 0;
+ if (!follow_symlinks)
+ flags |= AT_SYMLINK_NOFOLLOW;
+ if (effective_ids)
+ flags |= AT_EACCESS;
+ result = faccessat(dir_fd, path->narrow, mode, flags);
+ } else {
+ faccessat_unavailable = 1;
+ }
}
else
#endif
result = access(path->narrow, mode);
Py_END_ALLOW_THREADS
+
+#ifdef HAVE_FACCESSAT
+ if (faccessat_unavailable) {
+ if (dir_fd != DEFAULT_DIR_FD) {
+ argument_unavailable_error("access", "dir_fd");
+ return -1;
+ }
+ if (follow_symlinks_specified("access", follow_symlinks))
+ return -1;
+
+ if (effective_ids) {
+ argument_unavailable_error("access", "effective_ids");
+ return -1;
+ }
+ /* should be unreachable */
+ return -1;
+ }
+#endif
return_value = !result;
#endif
@@ -3002,6 +3154,7 @@
#ifdef HAVE_FCHMODAT
int fchmodat_nofollow_unsupported = 0;
+ int fchmodat_unsupported = 0;
#endif
#if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD))
@@ -3037,42 +3190,56 @@
if (path->fd != -1)
result = fchmod(path->fd, mode);
else
-#endif
+#endif /* HAVE_CHMOD */
#ifdef HAVE_LCHMOD
if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
result = lchmod(path->narrow, mode);
else
-#endif
+#endif /* HAVE_LCHMOD */
#ifdef HAVE_FCHMODAT
if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
- /*
- * fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW!
- * The documentation specifically shows how to use it,
- * and then says it isn't implemented yet.
- * (true on linux with glibc 2.15, and openindiana 3.x)
- *
- * Once it is supported, os.chmod will automatically
- * support dir_fd and follow_symlinks=False. (Hopefully.)
- * Until then, we need to be careful what exception we raise.
- */
- result = fchmodat(dir_fd, path->narrow, mode,
- follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
- /*
- * But wait! We can't throw the exception without allowing threads,
- * and we can't do that in this nested scope. (Macro trickery, sigh.)
- */
- fchmodat_nofollow_unsupported =
- result &&
- ((errno == ENOTSUP) || (errno == EOPNOTSUPP)) &&
- !follow_symlinks;
+ if (HAVE_FCHMODAT_RUNTIME) {
+ /*
+ * fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW!
+ * The documentation specifically shows how to use it,
+ * and then says it isn't implemented yet.
+ * (true on linux with glibc 2.15, and openindiana 3.x)
+ *
+ * Once it is supported, os.chmod will automatically
+ * support dir_fd and follow_symlinks=False. (Hopefully.)
+ * Until then, we need to be careful what exception we raise.
+ */
+ result = fchmodat(dir_fd, path->narrow, mode,
+ follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
+ /*
+ * But wait! We can't throw the exception without allowing threads,
+ * and we can't do that in this nested scope. (Macro trickery, sigh.)
+ */
+ fchmodat_nofollow_unsupported =
+ result &&
+ ((errno == ENOTSUP) || (errno == EOPNOTSUPP)) &&
+ !follow_symlinks;
+ } else {
+ fchmodat_unsupported = 1;
+ fchmodat_nofollow_unsupported = 1;
+
+ result = -1;
+ }
}
else
-#endif
+#endif /* HAVE_FHCMODAT */
result = chmod(path->narrow, mode);
Py_END_ALLOW_THREADS
if (result) {
#ifdef HAVE_FCHMODAT
+ if (fchmodat_unsupported) {
+ if (dir_fd != DEFAULT_DIR_FD) {
+ argument_unavailable_error("chmod", "dir_fd");
+ return NULL;
+ }
+ }
+
if (fchmodat_nofollow_unsupported) {
if (dir_fd != DEFAULT_DIR_FD)
dir_fd_and_follow_symlinks_invalid("chmod",
@@ -3082,10 +3249,10 @@
return NULL;
}
else
-#endif
+#endif /* HAVE_FCHMODAT */
return path_error(path);
}
-#endif
+#endif /* MS_WINDOWS */
Py_RETURN_NONE;
}
@@ -3373,6 +3540,10 @@
{
int result;
+#if defined(HAVE_FCHOWNAT)
+ int fchownat_unsupported = 0;
+#endif
+
#if !(defined(HAVE_LCHOWN) || defined(HAVE_FCHOWNAT))
if (follow_symlinks_specified("chown", follow_symlinks))
return NULL;
@@ -3381,19 +3552,6 @@
fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks))
return NULL;
-#ifdef __APPLE__
- /*
- * This is for Mac OS X 10.3, which doesn't have lchown.
- * (But we still have an lchown symbol because of weak-linking.)
- * It doesn't have fchownat either. So there's no possibility
- * of a graceful failover.
- */
- if ((!follow_symlinks) && (lchown == NULL)) {
- follow_symlinks_specified("chown", follow_symlinks);
- return NULL;
- }
-#endif
-
if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid,
dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
return NULL;
@@ -3411,14 +3569,28 @@
else
#endif
#ifdef HAVE_FCHOWNAT
- if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks))
+ if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) {
+ if (HAVE_FCHOWNAT_RUNTIME) {
result = fchownat(dir_fd, path->narrow, uid, gid,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
- else
+ } else {
+ fchownat_unsupported = 1;
+ }
+ } else
#endif
result = chown(path->narrow, uid, gid);
Py_END_ALLOW_THREADS
+#ifdef HAVE_FCHOWNAT
+ if (fchownat_unsupported) {
+ /* This would be incorrect if the current platform
+ * doesn't support lchown.
+ */
+ argument_unavailable_error(NULL, "dir_fd");
+ return NULL;
+ }
+#endif
+
if (result)
return path_error(path);
@@ -3664,6 +3836,9 @@
#else
int result;
#endif
+#if defined(HAVE_LINKAT)
+ int linkat_unavailable = 0;
+#endif
#ifndef HAVE_LINKAT
if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) {
@@ -3698,15 +3873,43 @@
#ifdef HAVE_LINKAT
if ((src_dir_fd != DEFAULT_DIR_FD) ||
(dst_dir_fd != DEFAULT_DIR_FD) ||
- (!follow_symlinks))
- result = linkat(src_dir_fd, src->narrow,
- dst_dir_fd, dst->narrow,
- follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
+ (!follow_symlinks)) {
+
+ if (HAVE_LINKAT_RUNTIME) {
+
+ result = linkat(src_dir_fd, src->narrow,
+ dst_dir_fd, dst->narrow,
+ follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
+
+ }
+#ifdef __APPLE__
+ else {
+ if (src_dir_fd == DEFAULT_DIR_FD && dst_dir_fd == DEFAULT_DIR_FD) {
+ /* See issue 41355: This matches the behaviour of !HAVE_LINKAT */
+ result = link(src->narrow, dst->narrow);
+ } else {
+ linkat_unavailable = 1;
+ }
+ }
+#endif
+ }
else
#endif /* HAVE_LINKAT */
result = link(src->narrow, dst->narrow);
Py_END_ALLOW_THREADS
+#ifdef HAVE_LINKAT
+ if (linkat_unavailable) {
+ /* Either or both dir_fd arguments were specified */
+ if (src_dir_fd != DEFAULT_DIR_FD) {
+ argument_unavailable_error("link", "src_dir_fd");
+ } else {
+ argument_unavailable_error("link", "dst_dir_fd");
+ }
+ return NULL;
+ }
+#endif
+
if (result)
return path_error2(src, dst);
#endif /* MS_WINDOWS */
@@ -3829,6 +4032,7 @@
errno = 0;
#ifdef HAVE_FDOPENDIR
if (path->fd != -1) {
+ if (HAVE_FDOPENDIR_RUNTIME) {
/* closedir() closes the FD, so we duplicate it */
fd = _Py_dup(path->fd);
if (fd == -1)
@@ -3839,6 +4043,11 @@
Py_BEGIN_ALLOW_THREADS
dirp = fdopendir(fd);
Py_END_ALLOW_THREADS
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "listdir: path should be string, bytes, os.PathLike or None, not int");
+ return NULL;
+ }
}
else
#endif
@@ -4152,6 +4361,9 @@
/*[clinic end generated code: output=a70446903abe821f input=e965f68377e9b1ce]*/
{
int result;
+#ifdef HAVE_MKDIRAT
+ int mkdirat_unavailable = 0;
+#endif
if (PySys_Audit("os.mkdir", "Oii", path->object, mode,
dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
@@ -4168,9 +4380,14 @@
#else
Py_BEGIN_ALLOW_THREADS
#if HAVE_MKDIRAT
- if (dir_fd != DEFAULT_DIR_FD)
+ if (dir_fd != DEFAULT_DIR_FD) {
+ if (HAVE_MKDIRAT_RUNTIME) {
result = mkdirat(dir_fd, path->narrow, mode);
- else
+
+ } else {
+ mkdirat_unavailable = 1;
+ }
+ } else
#endif
#if defined(__WATCOMC__) && !defined(__QNX__)
result = mkdir(path->narrow);
@@ -4178,6 +4395,14 @@
result = mkdir(path->narrow, mode);
#endif
Py_END_ALLOW_THREADS
+
+#if HAVE_MKDIRAT
+ if (mkdirat_unavailable) {
+ argument_unavailable_error(NULL, "dir_fd");
+ return NULL;
+ }
+#endif
+
if (result < 0)
return path_error(path);
#endif /* MS_WINDOWS */
@@ -4287,6 +4512,10 @@
const char *function_name = is_replace ? "replace" : "rename";
int dir_fd_specified;
+#ifdef HAVE_RENAMEAT
+ int renameat_unavailable = 0;
+#endif
+
#ifdef MS_WINDOWS
BOOL result;
int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0;
@@ -4326,13 +4555,25 @@
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_RENAMEAT
- if (dir_fd_specified)
- result = renameat(src_dir_fd, src->narrow, dst_dir_fd, dst->narrow);
- else
+ if (dir_fd_specified) {
+ if (HAVE_RENAMEAT_RUNTIME) {
+ result = renameat(src_dir_fd, src->narrow, dst_dir_fd, dst->narrow);
+ } else {
+ renameat_unavailable = 1;
+ }
+ } else
#endif
result = rename(src->narrow, dst->narrow);
Py_END_ALLOW_THREADS
+
+#ifdef HAVE_RENAMEAT
+ if (renameat_unavailable) {
+ argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd");
+ return NULL;
+ }
+#endif
+
if (result)
return path_error2(src, dst);
#endif
@@ -4408,6 +4649,9 @@
/*[clinic end generated code: output=080eb54f506e8301 input=38c8b375ca34a7e2]*/
{
int result;
+#ifdef HAVE_UNLINKAT
+ int unlinkat_unavailable = 0;
+#endif
if (PySys_Audit("os.rmdir", "Oi", path->object,
dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
@@ -4420,14 +4664,26 @@
result = !RemoveDirectoryW(path->wide);
#else
#ifdef HAVE_UNLINKAT
- if (dir_fd != DEFAULT_DIR_FD)
+ if (dir_fd != DEFAULT_DIR_FD) {
+ if (HAVE_UNLINKAT_RUNTIME) {
result = unlinkat(dir_fd, path->narrow, AT_REMOVEDIR);
- else
+ } else {
+ unlinkat_unavailable = 1;
+ result = -1;
+ }
+ } else
#endif
result = rmdir(path->narrow);
#endif
Py_END_ALLOW_THREADS
+#ifdef HAVE_UNLINKAT
+ if (unlinkat_unavailable) {
+ argument_unavailable_error("rmdir", "dir_fd");
+ return NULL;
+ }
+#endif
+
if (result)
return path_error(path);
@@ -4571,6 +4827,9 @@
/*[clinic end generated code: output=621797807b9963b1 input=d7bcde2b1b2a2552]*/
{
int result;
+#ifdef HAVE_UNLINKAT
+ int unlinkat_unavailable = 0;
+#endif
if (PySys_Audit("os.remove", "Oi", path->object,
dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
@@ -4584,15 +4843,27 @@
result = !Py_DeleteFileW(path->wide);
#else
#ifdef HAVE_UNLINKAT
- if (dir_fd != DEFAULT_DIR_FD)
+ if (dir_fd != DEFAULT_DIR_FD) {
+ if (HAVE_UNLINKAT_RUNTIME) {
+
result = unlinkat(dir_fd, path->narrow, 0);
- else
+ } else {
+ unlinkat_unavailable = 1;
+ }
+ } else
#endif /* HAVE_UNLINKAT */
result = unlink(path->narrow);
#endif
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
+#ifdef HAVE_UNLINKAT
+ if (unlinkat_unavailable) {
+ argument_unavailable_error(NULL, "dir_fd");
+ return NULL;
+ }
+#endif
+
if (result)
return path_error(path);
@@ -4763,7 +5034,16 @@
static int
utime_dir_fd(utime_t *ut, int dir_fd, const char *path, int follow_symlinks)
{
-#ifdef HAVE_UTIMENSAT
+#if defined(__APPLE__) && defined(HAVE_UTIMENSAT)
+ if (HAVE_UTIMENSAT_RUNTIME) {
+ int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW;
+ UTIME_TO_TIMESPEC;
+ return utimensat(dir_fd, path, time, flags);
+ } else {
+ errno = ENOSYS;
+ return -1;
+ }
+#elif defined(HAVE_UTIMENSAT)
int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW;
UTIME_TO_TIMESPEC;
return utimensat(dir_fd, path, time, flags);
@@ -4790,11 +5070,30 @@
utime_fd(utime_t *ut, int fd)
{
#ifdef HAVE_FUTIMENS
+
+ if (HAVE_FUTIMENS_RUNTIME) {
+
UTIME_TO_TIMESPEC;
return futimens(fd, time);
-#else
+
+ } else
+#ifndef HAVE_FUTIMES
+ {
+ /* Not sure if this can happen */
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "neither futimens nor futimes are supported"
+ " on this system");
+ return -1;
+ }
+#endif
+
+#endif
+#ifdef HAVE_FUTIMES
+ {
UTIME_TO_TIMEVAL;
return futimes(fd, time);
+ }
#endif
}
@@ -4813,11 +5112,27 @@
utime_nofollow_symlinks(utime_t *ut, const char *path)
{
#ifdef HAVE_UTIMENSAT
- UTIME_TO_TIMESPEC;
- return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW);
-#else
+ if (HAVE_UTIMENSAT_RUNTIME) {
+ UTIME_TO_TIMESPEC;
+ return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW);
+ } else
+#ifndef HAVE_LUTIMES
+ {
+ /* Not sure if this can happen */
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "neither utimensat nor lutimes are supported"
+ " on this system");
+ return -1;
+ }
+#endif
+#endif
+
+#ifdef HAVE_LUTIMES
+ {
UTIME_TO_TIMEVAL;
return lutimes(path, time);
+ }
#endif
}
@@ -4828,7 +5143,15 @@
static int
utime_default(utime_t *ut, const char *path)
{
-#ifdef HAVE_UTIMENSAT
+#if defined(__APPLE__) && defined(HAVE_UTIMENSAT)
+ if (HAVE_UTIMENSAT_RUNTIME) {
+ UTIME_TO_TIMESPEC;
+ return utimensat(DEFAULT_DIR_FD, path, time, 0);
+ } else {
+ UTIME_TO_TIMEVAL;
+ return utimes(path, time);
+ }
+#elif defined(HAVE_UTIMENSAT)
UTIME_TO_TIMESPEC;
return utimensat(DEFAULT_DIR_FD, path, time, 0);
#elif defined(HAVE_UTIMES)
@@ -5037,9 +5360,10 @@
#endif
#if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT)
- if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks))
+ if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) {
result = utime_dir_fd(&utime, dir_fd, path->narrow, follow_symlinks);
- else
+
+ } else
#endif
#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS)
@@ -5052,6 +5376,14 @@
Py_END_ALLOW_THREADS
+#if defined(__APPLE__) && defined(HAVE_UTIMENSAT)
+ /* See utime_dir_fd implementation */
+ if (result == -1 && errno == ENOSYS) {
+ argument_unavailable_error(NULL, "dir_fd");
+ return NULL;
+ }
+#endif
+
if (result < 0) {
/* see previous comment about not putting filename in error here */
posix_error();
@@ -5450,6 +5782,9 @@
}
if (setsid) {
+#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME
+ if (HAVE_POSIX_SPAWN_SETSID_RUNTIME) {
+#endif
#ifdef POSIX_SPAWN_SETSID
all_flags |= POSIX_SPAWN_SETSID;
#elif defined(POSIX_SPAWN_SETSID_NP)
@@ -5458,6 +5793,14 @@
argument_unavailable_error(func_name, "setsid");
return -1;
#endif
+
+#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME
+ } else {
+ argument_unavailable_error(func_name, "setsid");
+ return -1;
+ }
+#endif /* HAVE_POSIX_SPAWN_SETSID_RUNTIME */
+
}
if (setsigmask) {
@@ -8068,16 +8411,30 @@
#if defined(HAVE_READLINK)
char buffer[MAXPATHLEN+1];
ssize_t length;
+#ifdef HAVE_READLINKAT
+ int readlinkat_unavailable = 0;
+#endif
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_READLINKAT
- if (dir_fd != DEFAULT_DIR_FD)
- length = readlinkat(dir_fd, path->narrow, buffer, MAXPATHLEN);
- else
+ if (dir_fd != DEFAULT_DIR_FD) {
+ if (HAVE_READLINKAT_RUNTIME) {
+ length = readlinkat(dir_fd, path->narrow, buffer, MAXPATHLEN);
+ } else {
+ readlinkat_unavailable = 1;
+ }
+ } else
#endif
length = readlink(path->narrow, buffer, MAXPATHLEN);
Py_END_ALLOW_THREADS
+#ifdef HAVE_READLINKAT
+ if (readlinkat_unavailable) {
+ argument_unavailable_error(NULL, "dir_fd");
+ return NULL;
+ }
+#endif
+
if (length < 0) {
return path_error(path);
}
@@ -8273,6 +8630,9 @@
static int windows_has_symlink_unprivileged_flag = TRUE;
#else
int result;
+#ifdef HAVE_SYMLINKAT
+ int symlinkat_unavailable = 0;
+#endif
#endif
if (PySys_Audit("os.symlink", "OOi", src->object, dst->object,
@@ -8335,14 +8695,25 @@
}
Py_BEGIN_ALLOW_THREADS
-#if HAVE_SYMLINKAT
- if (dir_fd != DEFAULT_DIR_FD)
- result = symlinkat(src->narrow, dir_fd, dst->narrow);
- else
+#ifdef HAVE_SYMLINKAT
+ if (dir_fd != DEFAULT_DIR_FD) {
+ if (HAVE_SYMLINKAT_RUNTIME) {
+ result = symlinkat(src->narrow, dir_fd, dst->narrow);
+ } else {
+ symlinkat_unavailable = 1;
+ }
+ } else
#endif
result = symlink(src->narrow, dst->narrow);
Py_END_ALLOW_THREADS
+#ifdef HAVE_SYMLINKAT
+ if (symlinkat_unavailable) {
+ argument_unavailable_error(NULL, "dir_fd");
+ return NULL;
+ }
+#endif
+
if (result)
return path_error2(src, dst);
#endif
@@ -8613,6 +8984,9 @@
{
int fd;
int async_err = 0;
+#ifdef HAVE_OPENAT
+ int openat_unavailable = 0;
+#endif
#ifdef O_CLOEXEC
int *atomic_flag_works = &_Py_open_cloexec_works;
@@ -8637,9 +9011,15 @@
fd = _wopen(path->wide, flags, mode);
#else
#ifdef HAVE_OPENAT
- if (dir_fd != DEFAULT_DIR_FD)
- fd = openat(dir_fd, path->narrow, flags, mode);
- else
+ if (dir_fd != DEFAULT_DIR_FD) {
+ if (HAVE_OPENAT_RUNTIME) {
+ fd = openat(dir_fd, path->narrow, flags, mode);
+
+ } else {
+ openat_unavailable = 1;
+ fd = -1;
+ }
+ } else
#endif /* HAVE_OPENAT */
fd = open(path->narrow, flags, mode);
#endif /* !MS_WINDOWS */
@@ -8647,6 +9027,13 @@
} while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
_Py_END_SUPPRESS_IPH
+#ifdef HAVE_OPENAT
+ if (openat_unavailable) {
+ argument_unavailable_error(NULL, "dir_fd");
+ return -1;
+ }
+#endif
+
if (fd < 0) {
if (!async_err)
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
@@ -9229,12 +9616,25 @@
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
#else
do {
+#ifdef __APPLE__
+/* This entire function will be removed from the module dict when the API
+ * is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+#endif
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
n = preadv(fd, iov, cnt, offset);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
#endif
iov_cleanup(iov, buf, cnt);
@@ -9856,6 +10256,15 @@
Py_END_ALLOW_THREADS
} while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
#else
+
+#ifdef __APPLE__
+/* This entire function will be removed from the module dict when the API
+ * is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+#endif
do {
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
@@ -9863,6 +10272,11 @@
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
} while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
#endif
iov_cleanup(iov, buf, cnt);
@@ -10752,13 +11166,6 @@
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_FSTATVFS
if (path->fd != -1) {
-#ifdef __APPLE__
- /* handle weak-linking on Mac OS X 10.3 */
- if (fstatvfs == NULL) {
- fd_specified("statvfs", path->fd);
- return NULL;
- }
-#endif
result = fstatvfs(path->fd, &st);
}
else
@@ -12832,12 +13239,17 @@
const char *path = PyBytes_AS_STRING(ub);
if (self->dir_fd != DEFAULT_DIR_FD) {
#ifdef HAVE_FSTATAT
+ if (HAVE_FSTATAT_RUNTIME) {
result = fstatat(self->dir_fd, path, &st,
follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
-#else
+ } else
+
+#endif /* HAVE_FSTATAT */
+ {
+ Py_DECREF(ub);
PyErr_SetString(PyExc_NotImplementedError, "can't fetch stat");
return NULL;
-#endif /* HAVE_FSTATAT */
+ }
}
else
#endif
@@ -13623,7 +14035,8 @@
#else /* POSIX */
errno = 0;
#ifdef HAVE_FDOPENDIR
- if (path->fd != -1) {
+ if (iterator->path.fd != -1) {
+ if (HAVE_FDOPENDIR_RUNTIME) {
/* closedir() closes the FD, so we duplicate it */
fd = _Py_dup(path->fd);
if (fd == -1)
@@ -13632,6 +14045,11 @@
Py_BEGIN_ALLOW_THREADS
iterator->dirp = fdopendir(fd);
Py_END_ALLOW_THREADS
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "scandir: path should be string, bytes, os.PathLike or None, not int");
+ return NULL;
+ }
}
else
#endif
@@ -14702,137 +15120,210 @@
}
-static const char * const have_functions[] = {
+
+#define PROBE(name, test) \
+ static int name(void) \
+ { \
+ if (test) { \
+ return 1; \
+ } else { \
+ return 0; \
+ } \
+ }
+
+#ifdef HAVE_FSTATAT
+PROBE(probe_fstatat, HAVE_FSTATAT_RUNTIME)
+#endif
#ifdef HAVE_FACCESSAT
- "HAVE_FACCESSAT",
-#endif
-
-#ifdef HAVE_FCHDIR
- "HAVE_FCHDIR",
-#endif
-
-#ifdef HAVE_FCHMOD
- "HAVE_FCHMOD",
+PROBE(probe_faccessat, HAVE_FACCESSAT_RUNTIME)
#endif
#ifdef HAVE_FCHMODAT
- "HAVE_FCHMODAT",
-#endif
-
-#ifdef HAVE_FCHOWN
- "HAVE_FCHOWN",
+PROBE(probe_fchmodat, HAVE_FCHMODAT_RUNTIME)
#endif
#ifdef HAVE_FCHOWNAT
- "HAVE_FCHOWNAT",
-#endif
-
-#ifdef HAVE_FEXECVE
- "HAVE_FEXECVE",
-#endif
-
-#ifdef HAVE_FDOPENDIR
- "HAVE_FDOPENDIR",
-#endif
-
-#ifdef HAVE_FPATHCONF
- "HAVE_FPATHCONF",
-#endif
-
-#ifdef HAVE_FSTATAT
- "HAVE_FSTATAT",
-#endif
-
-#ifdef HAVE_FSTATVFS
- "HAVE_FSTATVFS",
-#endif
-
-#if defined HAVE_FTRUNCATE || defined MS_WINDOWS
- "HAVE_FTRUNCATE",
-#endif
-
-#ifdef HAVE_FUTIMENS
- "HAVE_FUTIMENS",
-#endif
-
-#ifdef HAVE_FUTIMES
- "HAVE_FUTIMES",
-#endif
-
-#ifdef HAVE_FUTIMESAT
- "HAVE_FUTIMESAT",
+PROBE(probe_fchownat, HAVE_FCHOWNAT_RUNTIME)
#endif
#ifdef HAVE_LINKAT
- "HAVE_LINKAT",
+PROBE(probe_linkat, HAVE_LINKAT_RUNTIME)
#endif
-#ifdef HAVE_LCHFLAGS
- "HAVE_LCHFLAGS",
-#endif
-
-#ifdef HAVE_LCHMOD
- "HAVE_LCHMOD",
-#endif
-
-#ifdef HAVE_LCHOWN
- "HAVE_LCHOWN",
-#endif
-
-#ifdef HAVE_LSTAT
- "HAVE_LSTAT",
-#endif
-
-#ifdef HAVE_LUTIMES
- "HAVE_LUTIMES",
-#endif
-
-#ifdef HAVE_MEMFD_CREATE
- "HAVE_MEMFD_CREATE",
+#ifdef HAVE_FDOPENDIR
+PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME)
#endif
#ifdef HAVE_MKDIRAT
- "HAVE_MKDIRAT",
-#endif
-
-#ifdef HAVE_MKFIFOAT
- "HAVE_MKFIFOAT",
-#endif
-
-#ifdef HAVE_MKNODAT
- "HAVE_MKNODAT",
-#endif
-
-#ifdef HAVE_OPENAT
- "HAVE_OPENAT",
-#endif
-
-#ifdef HAVE_READLINKAT
- "HAVE_READLINKAT",
+PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME)
#endif
#ifdef HAVE_RENAMEAT
- "HAVE_RENAMEAT",
-#endif
-
-#ifdef HAVE_SYMLINKAT
- "HAVE_SYMLINKAT",
+PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME)
#endif
#ifdef HAVE_UNLINKAT
- "HAVE_UNLINKAT",
+PROBE(probe_unlinkat, HAVE_UNLINKAT_RUNTIME)
+#endif
+
+#ifdef HAVE_OPENAT
+PROBE(probe_openat, HAVE_OPENAT_RUNTIME)
+#endif
+
+#ifdef HAVE_READLINKAT
+PROBE(probe_readlinkat, HAVE_READLINKAT_RUNTIME)
+#endif
+
+#ifdef HAVE_SYMLINKAT
+PROBE(probe_symlinkat, HAVE_SYMLINKAT_RUNTIME)
+#endif
+
+#ifdef HAVE_FUTIMENS
+PROBE(probe_futimens, HAVE_FUTIMENS_RUNTIME)
#endif
#ifdef HAVE_UTIMENSAT
- "HAVE_UTIMENSAT",
+PROBE(probe_utimensat, HAVE_UTIMENSAT_RUNTIME)
+#endif
+
+
+
+
+static const struct have_function {
+ const char * const label;
+ int (*probe)(void);
+} have_functions[] = {
+
+#ifdef HAVE_FACCESSAT
+ { "HAVE_FACCESSAT", probe_faccessat },
+#endif
+
+#ifdef HAVE_FCHDIR
+ { "HAVE_FCHDIR", NULL },
+#endif
+
+#ifdef HAVE_FCHMOD
+ { "HAVE_FCHMOD", NULL },
+#endif
+
+#ifdef HAVE_FCHMODAT
+ { "HAVE_FCHMODAT", probe_fchmodat },
+#endif
+
+#ifdef HAVE_FCHOWN
+ { "HAVE_FCHOWN", NULL },
+#endif
+
+#ifdef HAVE_FCHOWNAT
+ { "HAVE_FCHOWNAT", probe_fchownat },
+#endif
+
+#ifdef HAVE_FEXECVE
+ { "HAVE_FEXECVE", NULL },
+#endif
+
+#ifdef HAVE_FDOPENDIR
+ { "HAVE_FDOPENDIR", probe_fdopendir },
+#endif
+
+#ifdef HAVE_FPATHCONF
+ { "HAVE_FPATHCONF", NULL },
+#endif
+
+#ifdef HAVE_FSTATAT
+ { "HAVE_FSTATAT", probe_fstatat },
+#endif
+
+#ifdef HAVE_FSTATVFS
+ { "HAVE_FSTATVFS", NULL },
+#endif
+
+#if defined HAVE_FTRUNCATE || defined MS_WINDOWS
+ { "HAVE_FTRUNCATE", NULL },
+#endif
+
+#ifdef HAVE_FUTIMENS
+ { "HAVE_FUTIMENS", probe_futimens },
+#endif
+
+#ifdef HAVE_FUTIMES
+ { "HAVE_FUTIMES", NULL },
+#endif
+
+#ifdef HAVE_FUTIMESAT
+ { "HAVE_FUTIMESAT", NULL },
+#endif
+
+#ifdef HAVE_LINKAT
+ { "HAVE_LINKAT", probe_linkat },
+#endif
+
+#ifdef HAVE_LCHFLAGS
+ { "HAVE_LCHFLAGS", NULL },
+#endif
+
+#ifdef HAVE_LCHMOD
+ { "HAVE_LCHMOD", NULL },
+#endif
+
+#ifdef HAVE_LCHOWN
+ { "HAVE_LCHOWN", NULL },
+#endif
+
+#ifdef HAVE_LSTAT
+ { "HAVE_LSTAT", NULL },
+#endif
+
+#ifdef HAVE_LUTIMES
+ { "HAVE_LUTIMES", NULL },
+#endif
+
+#ifdef HAVE_MEMFD_CREATE
+ { "HAVE_MEMFD_CREATE", NULL },
+#endif
+
+#ifdef HAVE_MKDIRAT
+ { "HAVE_MKDIRAT", probe_mkdirat },
+#endif
+
+#ifdef HAVE_MKFIFOAT
+ { "HAVE_MKFIFOAT", NULL },
+#endif
+
+#ifdef HAVE_MKNODAT
+ { "HAVE_MKNODAT", NULL },
+#endif
+
+#ifdef HAVE_OPENAT
+ { "HAVE_OPENAT", probe_openat },
+#endif
+
+#ifdef HAVE_READLINKAT
+ { "HAVE_READLINKAT", probe_readlinkat },
+#endif
+
+#ifdef HAVE_RENAMEAT
+ { "HAVE_RENAMEAT", probe_renameat },
+#endif
+
+#ifdef HAVE_SYMLINKAT
+ { "HAVE_SYMLINKAT", probe_symlinkat },
+#endif
+
+#ifdef HAVE_UNLINKAT
+ { "HAVE_UNLINKAT", probe_unlinkat },
+#endif
+
+#ifdef HAVE_UTIMENSAT
+ { "HAVE_UTIMENSAT", probe_utimensat },
#endif
#ifdef MS_WINDOWS
- "MS_WINDOWS",
+ { "MS_WINDOWS", NULL },
#endif
- NULL
+ { NULL, NULL }
};
@@ -14841,6 +15332,23 @@
{
_posixstate *state = get_posix_state(m);
+#if defined(HAVE_PWRITEV)
+ if (HAVE_PWRITEV_RUNTIME) {} else {
+ PyObject* dct = PyModule_GetDict(m);
+
+ if (dct == NULL) {
+ return -1;
+ }
+
+ if (PyDict_DelItemString(dct, "pwritev") == -1) {
+ PyErr_Clear();
+ }
+ if (PyDict_DelItemString(dct, "preadv") == -1) {
+ PyErr_Clear();
+ }
+ }
+#endif
+
/* Initialize environ dictionary */
PyObject *v = convertenviron();
Py_XINCREF(v);
@@ -14953,44 +15461,6 @@
PyModule_AddObject(m, "uname_result", (PyObject *)UnameResultType);
state->UnameResultType = (PyObject *)UnameResultType;
-#ifdef __APPLE__
- /*
- * Step 2 of weak-linking support on Mac OS X.
- *
- * The code below removes functions that are not available on the
- * currently active platform.
- *
- * This block allow one to use a python binary that was build on
- * OSX 10.4 on OSX 10.3, without losing access to new APIs on
- * OSX 10.4.
- */
-#ifdef HAVE_FSTATVFS
- if (fstatvfs == NULL) {
- if (PyObject_DelAttrString(m, "fstatvfs") == -1) {
- return -1;
- }
- }
-#endif /* HAVE_FSTATVFS */
-
-#ifdef HAVE_STATVFS
- if (statvfs == NULL) {
- if (PyObject_DelAttrString(m, "statvfs") == -1) {
- return -1;
- }
- }
-#endif /* HAVE_STATVFS */
-
-# ifdef HAVE_LCHOWN
- if (lchown == NULL) {
- if (PyObject_DelAttrString(m, "lchown") == -1) {
- return -1;
- }
- }
-#endif /* HAVE_LCHOWN */
-
-
-#endif /* __APPLE__ */
-
if ((state->billion = PyLong_FromLong(1000000000)) == NULL)
return -1;
#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
@@ -15020,14 +15490,17 @@
if (!list) {
return -1;
}
- for (const char * const *trace = have_functions; *trace; trace++) {
- PyObject *unicode = PyUnicode_DecodeASCII(*trace, strlen(*trace), NULL);
+ for (const struct have_function *trace = have_functions; trace->label; trace++) {
+ PyObject *unicode;
+ if (trace->probe && !trace->probe()) continue;
+ unicode = PyUnicode_DecodeASCII(trace->label, strlen(trace->label), NULL);
if (!unicode)
return -1;
if (PyList_Append(list, unicode))
return -1;
Py_DECREF(unicode);
}
+
PyModule_AddObject(m, "_have_functions", list);
return 0;
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index eb192c5..80eab30 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -51,6 +51,15 @@
#define _Py_tzname tzname
#endif
+#if defined(__APPLE__ ) && defined(__has_builtin)
+# if __has_builtin(__builtin_available)
+# define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
+# endif
+#endif
+#ifndef HAVE_CLOCK_GETTIME_RUNTIME
+# define HAVE_CLOCK_GETTIME_RUNTIME 1
+#endif
+
#define SEC_TO_NS (1000 * 1000 * 1000)
/* Forward declarations */
@@ -149,6 +158,16 @@
}
#ifdef HAVE_CLOCK_GETTIME
+
+#ifdef __APPLE__
+/*
+ * The clock_* functions will be removed from the module
+ * dict entirely when the C API is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#endif
+
static PyObject *
time_clock_gettime(PyObject *self, PyObject *args)
{
@@ -297,6 +316,11 @@
"clock_getres(clk_id) -> floating point number\n\
\n\
Return the resolution (precision) of the specified clock clk_id.");
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
#endif /* HAVE_CLOCK_GETRES */
#ifdef HAVE_PTHREAD_GETCPUCLOCKID
@@ -1162,31 +1186,35 @@
#if defined(HAVE_CLOCK_GETTIME) \
&& (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
struct timespec ts;
+
+ if (HAVE_CLOCK_GETTIME_RUNTIME) {
+
#ifdef CLOCK_PROF
- const clockid_t clk_id = CLOCK_PROF;
- const char *function = "clock_gettime(CLOCK_PROF)";
+ const clockid_t clk_id = CLOCK_PROF;
+ const char *function = "clock_gettime(CLOCK_PROF)";
#else
- const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
- const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
+ const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
+ const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
#endif
- if (clock_gettime(clk_id, &ts) == 0) {
- if (info) {
- struct timespec res;
- info->implementation = function;
- info->monotonic = 1;
- info->adjustable = 0;
- if (clock_getres(clk_id, &res)) {
- PyErr_SetFromErrno(PyExc_OSError);
+ if (clock_gettime(clk_id, &ts) == 0) {
+ if (info) {
+ struct timespec res;
+ info->implementation = function;
+ info->monotonic = 1;
+ info->adjustable = 0;
+ if (clock_getres(clk_id, &res)) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ }
+
+ if (_PyTime_FromTimespec(tp, &ts) < 0) {
return -1;
}
- info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ return 0;
}
-
- if (_PyTime_FromTimespec(tp, &ts) < 0) {
- return -1;
- }
- return 0;
}
#endif
@@ -1390,6 +1418,16 @@
#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
#define HAVE_THREAD_TIME
+
+#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
+static int
+_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
+ __attribute__((availability(macos, introduced=10.12)))
+ __attribute__((availability(ios, introduced=10.0)))
+ __attribute__((availability(tvos, introduced=10.0)))
+ __attribute__((availability(watchos, introduced=3.0)));
+#endif
+
static int
_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
{
@@ -1421,6 +1459,15 @@
#endif
#ifdef HAVE_THREAD_TIME
+#ifdef __APPLE__
+/*
+ * The clock_* functions will be removed from the module
+ * dict entirely when the C API is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#endif
+
static PyObject *
time_thread_time(PyObject *self, PyObject *unused)
{
@@ -1451,6 +1498,11 @@
\n\
Thread time for profiling as nanoseconds:\n\
sum of the kernel and user-space CPU time.");
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
#endif
@@ -1500,9 +1552,19 @@
}
#ifdef HAVE_THREAD_TIME
else if (strcmp(name, "thread_time") == 0) {
- if (_PyTime_GetThreadTimeWithInfo(&t, &info) < 0) {
+
+#ifdef __APPLE__
+ if (HAVE_CLOCK_GETTIME_RUNTIME) {
+#endif
+ if (_PyTime_GetThreadTimeWithInfo(&t, &info) < 0) {
+ return NULL;
+ }
+#ifdef __APPLE__
+ } else {
+ PyErr_SetString(PyExc_ValueError, "unknown clock");
return NULL;
}
+#endif
}
#endif
else {
@@ -1783,68 +1845,116 @@
static int
time_exec(PyObject *module)
{
+#if defined(__APPLE__) && defined(HAVE_CLOCK_GETTIME)
+ if (HAVE_CLOCK_GETTIME_RUNTIME) {
+ /* pass: ^^^ cannot use '!' here */
+ } else {
+ PyObject* dct = PyModule_GetDict(module);
+ if (dct == NULL) {
+ return -1;
+ }
+
+ if (PyDict_DelItemString(dct, "clock_gettime") == -1) {
+ PyErr_Clear();
+ }
+ if (PyDict_DelItemString(dct, "clock_gettime_ns") == -1) {
+ PyErr_Clear();
+ }
+ if (PyDict_DelItemString(dct, "clock_settime") == -1) {
+ PyErr_Clear();
+ }
+ if (PyDict_DelItemString(dct, "clock_settime_ns") == -1) {
+ PyErr_Clear();
+ }
+ if (PyDict_DelItemString(dct, "clock_getres") == -1) {
+ PyErr_Clear();
+ }
+ }
+#endif
+#if defined(__APPLE__) && defined(HAVE_THREAD_TIME)
+ if (HAVE_CLOCK_GETTIME_RUNTIME) {
+ /* pass: ^^^ cannot use '!' here */
+ } else {
+ PyObject* dct = PyModule_GetDict(module);
+
+ if (PyDict_DelItemString(dct, "thread_time") == -1) {
+ PyErr_Clear();
+ }
+ if (PyDict_DelItemString(dct, "thread_time_ns") == -1) {
+ PyErr_Clear();
+ }
+ }
+#endif
/* Set, or reset, module variables like time.timezone */
if (init_timezone(module) < 0) {
return -1;
}
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES)
+ if (HAVE_CLOCK_GETTIME_RUNTIME) {
#ifdef CLOCK_REALTIME
- if (PyModule_AddIntMacro(module, CLOCK_REALTIME) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_REALTIME) < 0) {
+ return -1;
+ }
#endif
+
#ifdef CLOCK_MONOTONIC
- if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC) < 0) {
- return -1;
- }
+
+ if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC) < 0) {
+ return -1;
+ }
+
#endif
#ifdef CLOCK_MONOTONIC_RAW
- if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC_RAW) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC_RAW) < 0) {
+ return -1;
+ }
#endif
+
#ifdef CLOCK_HIGHRES
- if (PyModule_AddIntMacro(module, CLOCK_HIGHRES) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_HIGHRES) < 0) {
+ return -1;
+ }
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
- if (PyModule_AddIntMacro(module, CLOCK_PROCESS_CPUTIME_ID) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_PROCESS_CPUTIME_ID) < 0) {
+ return -1;
+ }
#endif
+
#ifdef CLOCK_THREAD_CPUTIME_ID
- if (PyModule_AddIntMacro(module, CLOCK_THREAD_CPUTIME_ID) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_THREAD_CPUTIME_ID) < 0) {
+ return -1;
+ }
#endif
#ifdef CLOCK_PROF
- if (PyModule_AddIntMacro(module, CLOCK_PROF) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_PROF) < 0) {
+ return -1;
+ }
#endif
#ifdef CLOCK_BOOTTIME
- if (PyModule_AddIntMacro(module, CLOCK_BOOTTIME) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_BOOTTIME) < 0) {
+ return -1;
+ }
#endif
#ifdef CLOCK_TAI
- if (PyModule_AddIntMacro(module, CLOCK_TAI) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_TAI) < 0) {
+ return -1;
+ }
#endif
#ifdef CLOCK_UPTIME
- if (PyModule_AddIntMacro(module, CLOCK_UPTIME) < 0) {
- return -1;
- }
+ if (PyModule_AddIntMacro(module, CLOCK_UPTIME) < 0) {
+ return -1;
+ }
#endif
#ifdef CLOCK_UPTIME_RAW
- if (PyModule_AddIntMacro(module, CLOCK_UPTIME_RAW) < 0) {
- return -1;
- }
+
+ if (PyModule_AddIntMacro(module, CLOCK_UPTIME_RAW) < 0) {
+ return -1;
+ }
#endif
+ }
#endif /* defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES) */