Files
Thomas Buchberger cb9bdc4754 python27: fix support for virtualenv on macOS 12 (#13480)
On macOS 12 (Monterey), dladdr, NSLibraryNameForModule and similar
functions return the real path of the dynamic library instead of the
path to the symlink in a virtualenv. This breaks support for virtualenv.

By applying the same patch as Apple does for its Python version, which
avoids using these functions whenever possible, support for virtualenv
is restored.

See also:
https://github.com/apple-oss-distributions/python/blob/python-136.120.2/2.7/fix/getpath.c.ed

Co-authored-by: Joshua Root <jmr@macports.org>
2022-01-08 05:34:47 +11:00

83 lines
3.2 KiB
Diff

As of macOS 12, dladdr, NSLibraryNameForModule, and similar functions
will resolve symlinks, which breaks virtualenv. This works around that
issue by not using those functions if at all possible.
Adapted from https://github.com/apple-oss-distributions/python/blob/python-136.120.2/2.7/fix/getpath.c.ed
--- Modules/getpath.c.orig 2022-01-08 05:08:48.000000000 +1100
+++ Modules/getpath.c 2022-01-08 05:13:34.000000000 +1100
@@ -7,7 +7,7 @@
#include <string.h>
#ifdef __APPLE__
-#include <mach-o/dyld.h>
+#include <dlfcn.h>
#include <AvailabilityMacros.h>
#endif
@@ -380,9 +380,10 @@ calculate_path(void)
size_t prefixsz;
char *defpath = pythonpath;
#ifdef WITH_NEXT_FRAMEWORK
- NSModule pythonModule;
+ Dl_info addrinfo;
+ int first_pass = 1;
#endif
-#ifdef __APPLE__
+#if 0
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
uint32_t nsexeclength = MAXPATHLEN;
#else
@@ -397,7 +398,7 @@ calculate_path(void)
*/
if (strchr(prog, SEP))
strncpy(progpath, prog, MAXPATHLEN);
-#ifdef __APPLE__
+#if 0
/* On Mac OS X, if a script uses an interpreter of the form
* "#!/opt/python2.3/bin/python", the kernel only passes "python"
* as argv[0], which falls through to the $PATH search below.
@@ -410,7 +411,7 @@ calculate_path(void)
*/
else if(0 == _NSGetExecutablePath(progpath, &nsexeclength) && progpath[0] == SEP)
;
-#endif /* __APPLE__ */
+#endif /* notdef */
else if (path) {
while (1) {
char *delim = strchr(path, DELIM);
@@ -449,9 +450,18 @@ calculate_path(void)
** which is in the framework, not relative to the executable, which ma
** be outside of the framework. Except when we're in the build directory...
*/
- pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize"));
- /* Use dylib functions to find out where the framework was loaded from */
- buf = (char *)NSLibraryNameForModule(pythonModule);
+ /* dladdr() now returns the real path of the dylib, instead of the
+ ** path of the symlink. This breaks virtualenv. To fix this, we
+ ** skip using dladdr() during a first pass, and if that fails, then
+ ** we go back and do the dladdr(). It turns out that since we moved
+ ** Python.app to inside the Python.framework bundle, the call to
+ ** search_for_prefix() will now succeed without needing dladdr().
+ ** However, virtualenv copies the wrong binary when the prefix is
+ ** /usr, so if progpath begins with /usr/bin/, we skip the first pass.
+ */
+ if (strncmp(progpath, "/usr/bin/", 9) == 0) first_pass = 0;
+return_here_for_second_pass:
+ buf = first_pass ? NULL : (dladdr("_Py_Initialize", &addrinfo) ? (char *)addrinfo.dli_fname : NULL);
if (buf != NULL) {
/* We're in a framework. */
/* See if we might be in the build directory. The framework in the
@@ -504,6 +514,12 @@ calculate_path(void)
*/
if (!(pfound = search_for_prefix(argv0_path, home))) {
+#ifdef WITH_NEXT_FRAMEWORK
+ if (first_pass) {
+ first_pass = 0;
+ goto return_here_for_second_pass;
+ }
+#endif
if (!Py_FrozenFlag)
fprintf(stderr,
"Could not find platform independent libraries <prefix>\n");