1 | /* Return the initial module search path. */ |
2 | |
3 | #include "Python.h" |
4 | #include "pycore_fileutils.h" |
5 | #include "pycore_initconfig.h" |
6 | #include "pycore_pathconfig.h" |
7 | #include "osdefs.h" // DELIM |
8 | |
9 | #include <sys/types.h> |
10 | #include <string.h> |
11 | |
12 | #ifdef __APPLE__ |
13 | # include <mach-o/dyld.h> |
14 | #endif |
15 | |
16 | /* Search in some common locations for the associated Python libraries. |
17 | * |
18 | * Two directories must be found, the platform independent directory |
19 | * (prefix), containing the common .py and .pyc files, and the platform |
20 | * dependent directory (exec_prefix), containing the shared library |
21 | * modules. Note that prefix and exec_prefix can be the same directory, |
22 | * but for some installations, they are different. |
23 | * |
24 | * Py_GetPath() carries out separate searches for prefix and exec_prefix. |
25 | * Each search tries a number of different locations until a ``landmark'' |
26 | * file or directory is found. If no prefix or exec_prefix is found, a |
27 | * warning message is issued and the preprocessor defined PREFIX and |
28 | * EXEC_PREFIX are used (even though they will not work); python carries on |
29 | * as best as is possible, but most imports will fail. |
30 | * |
31 | * Before any searches are done, the location of the executable is |
32 | * determined. If argv[0] has one or more slashes in it, it is used |
33 | * unchanged. Otherwise, it must have been invoked from the shell's path, |
34 | * so we search $PATH for the named executable and use that. If the |
35 | * executable was not found on $PATH (or there was no $PATH environment |
36 | * variable), the original argv[0] string is used. |
37 | * |
38 | * Next, the executable location is examined to see if it is a symbolic |
39 | * link. If so, the link is chased (correctly interpreting a relative |
40 | * pathname if one is found) and the directory of the link target is used. |
41 | * |
42 | * Finally, argv0_path is set to the directory containing the executable |
43 | * (i.e. the last component is stripped). |
44 | * |
45 | * With argv0_path in hand, we perform a number of steps. The same steps |
46 | * are performed for prefix and for exec_prefix, but with a different |
47 | * landmark. |
48 | * |
49 | * Step 1. Are we running python out of the build directory? This is |
50 | * checked by looking for a different kind of landmark relative to |
51 | * argv0_path. For prefix, the landmark's path is derived from the VPATH |
52 | * preprocessor variable (taking into account that its value is almost, but |
53 | * not quite, what we need). For exec_prefix, the landmark is |
54 | * pybuilddir.txt. If the landmark is found, we're done. |
55 | * |
56 | * For the remaining steps, the prefix landmark will always be |
57 | * lib/python$VERSION/os.py and the exec_prefix will always be |
58 | * lib/python$VERSION/lib-dynload, where $VERSION is Python's version |
59 | * number as supplied by the Makefile. Note that this means that no more |
60 | * build directory checking is performed; if the first step did not find |
61 | * the landmarks, the assumption is that python is running from an |
62 | * installed setup. |
63 | * |
64 | * Step 2. See if the $PYTHONHOME environment variable points to the |
65 | * installed location of the Python libraries. If $PYTHONHOME is set, then |
66 | * it points to prefix and exec_prefix. $PYTHONHOME can be a single |
67 | * directory, which is used for both, or the prefix and exec_prefix |
68 | * directories separated by a colon. |
69 | * |
70 | * Step 3. Try to find prefix and exec_prefix relative to argv0_path, |
71 | * backtracking up the path until it is exhausted. This is the most common |
72 | * step to succeed. Note that if prefix and exec_prefix are different, |
73 | * exec_prefix is more likely to be found; however if exec_prefix is a |
74 | * subdirectory of prefix, both will be found. |
75 | * |
76 | * Step 4. Search the directories pointed to by the preprocessor variables |
77 | * PREFIX and EXEC_PREFIX. These are supplied by the Makefile but can be |
78 | * passed in as options to the configure script. |
79 | * |
80 | * That's it! |
81 | * |
82 | * Well, almost. Once we have determined prefix and exec_prefix, the |
83 | * preprocessor variable PYTHONPATH is used to construct a path. Each |
84 | * relative path on PYTHONPATH is prefixed with prefix. Then the directory |
85 | * containing the shared library modules is appended. The environment |
86 | * variable $PYTHONPATH is inserted in front of it all. Finally, the |
87 | * prefix and exec_prefix globals are tweaked so they reflect the values |
88 | * expected by other code, by stripping the "lib/python$VERSION/..." stuff |
89 | * off. If either points to the build directory, the globals are reset to |
90 | * the corresponding preprocessor variables (so sys.prefix will reflect the |
91 | * installation location, even though sys.path points into the build |
92 | * directory). This seems to make more sense given that currently the only |
93 | * known use of sys.prefix and sys.exec_prefix is for the ILU installation |
94 | * process to find the installed Python tree. |
95 | * |
96 | * An embedding application can use Py_SetPath() to override all of |
97 | * these automatic path computations. |
98 | * |
99 | * NOTE: Windows MSVC builds use PC/getpathp.c instead! |
100 | */ |
101 | |
102 | #ifdef __cplusplus |
103 | extern "C" { |
104 | #endif |
105 | |
106 | |
107 | #if (!defined(PREFIX) || !defined(EXEC_PREFIX) \ |
108 | || !defined(VERSION) || !defined(VPATH)) |
109 | #error "PREFIX, EXEC_PREFIX, VERSION and VPATH macros must be defined" |
110 | #endif |
111 | |
112 | #ifndef LANDMARK |
113 | #define LANDMARK L"os.py" |
114 | #endif |
115 | |
116 | #define BUILD_LANDMARK L"Modules/Setup.local" |
117 | |
118 | #define DECODE_LOCALE_ERR(NAME, LEN) \ |
119 | ((LEN) == (size_t)-2) \ |
120 | ? _PyStatus_ERR("cannot decode " NAME) \ |
121 | : _PyStatus_NO_MEMORY() |
122 | |
123 | #define PATHLEN_ERR() _PyStatus_ERR("path configuration: path too long") |
124 | |
125 | typedef struct { |
126 | wchar_t *path_env; /* PATH environment variable */ |
127 | |
128 | wchar_t *pythonpath_macro; /* PYTHONPATH macro */ |
129 | wchar_t *prefix_macro; /* PREFIX macro */ |
130 | wchar_t *exec_prefix_macro; /* EXEC_PREFIX macro */ |
131 | wchar_t *vpath_macro; /* VPATH macro */ |
132 | |
133 | wchar_t *lib_python; /* <platlibdir> / "pythonX.Y" */ |
134 | |
135 | int prefix_found; /* found platform independent libraries? */ |
136 | int exec_prefix_found; /* found the platform dependent libraries? */ |
137 | |
138 | int warnings; |
139 | const wchar_t *pythonpath_env; |
140 | const wchar_t *platlibdir; |
141 | |
142 | wchar_t *argv0_path; |
143 | wchar_t *zip_path; |
144 | wchar_t *prefix; |
145 | wchar_t *exec_prefix; |
146 | } PyCalculatePath; |
147 | |
148 | static const wchar_t delimiter[2] = {DELIM, '\0'}; |
149 | static const wchar_t separator[2] = {SEP, '\0'}; |
150 | |
151 | |
152 | /* Get file status. Encode the path to the locale encoding. */ |
153 | static int |
154 | _Py_wstat(const wchar_t* path, struct stat *buf) |
155 | { |
156 | int err; |
157 | char *fname; |
158 | fname = _Py_EncodeLocaleRaw(path, NULL); |
159 | if (fname == NULL) { |
160 | errno = EINVAL; |
161 | return -1; |
162 | } |
163 | err = stat(fname, buf); |
164 | PyMem_RawFree(fname); |
165 | return err; |
166 | } |
167 | |
168 | |
169 | static void |
170 | reduce(wchar_t *dir) |
171 | { |
172 | size_t i = wcslen(dir); |
173 | while (i > 0 && dir[i] != SEP) { |
174 | --i; |
175 | } |
176 | dir[i] = '\0'; |
177 | } |
178 | |
179 | |
180 | /* Is file, not directory */ |
181 | static int |
182 | isfile(const wchar_t *filename) |
183 | { |
184 | struct stat buf; |
185 | if (_Py_wstat(filename, &buf) != 0) { |
186 | return 0; |
187 | } |
188 | if (!S_ISREG(buf.st_mode)) { |
189 | return 0; |
190 | } |
191 | return 1; |
192 | } |
193 | |
194 | |
195 | /* Is executable file */ |
196 | static int |
197 | isxfile(const wchar_t *filename) |
198 | { |
199 | struct stat buf; |
200 | if (_Py_wstat(filename, &buf) != 0) { |
201 | return 0; |
202 | } |
203 | if (!S_ISREG(buf.st_mode)) { |
204 | return 0; |
205 | } |
206 | if ((buf.st_mode & 0111) == 0) { |
207 | return 0; |
208 | } |
209 | return 1; |
210 | } |
211 | |
212 | |
213 | /* Is directory */ |
214 | static int |
215 | isdir(const wchar_t *filename) |
216 | { |
217 | struct stat buf; |
218 | if (_Py_wstat(filename, &buf) != 0) { |
219 | return 0; |
220 | } |
221 | if (!S_ISDIR(buf.st_mode)) { |
222 | return 0; |
223 | } |
224 | return 1; |
225 | } |
226 | |
227 | |
228 | /* Add a path component, by appending stuff to buffer. |
229 | buflen: 'buffer' length in characters including trailing NUL. |
230 | |
231 | If path2 is empty: |
232 | |
233 | - if path doesn't end with SEP and is not empty, add SEP to path |
234 | - otherwise, do nothing. */ |
235 | static PyStatus |
236 | joinpath(wchar_t *path, const wchar_t *path2, size_t path_len) |
237 | { |
238 | size_t n; |
239 | if (!_Py_isabs(path2)) { |
240 | n = wcslen(path); |
241 | if (n >= path_len) { |
242 | return PATHLEN_ERR(); |
243 | } |
244 | |
245 | if (n > 0 && path[n-1] != SEP) { |
246 | path[n++] = SEP; |
247 | } |
248 | } |
249 | else { |
250 | n = 0; |
251 | } |
252 | |
253 | size_t k = wcslen(path2); |
254 | if (n + k >= path_len) { |
255 | return PATHLEN_ERR(); |
256 | } |
257 | wcsncpy(path + n, path2, k); |
258 | path[n + k] = '\0'; |
259 | |
260 | return _PyStatus_OK(); |
261 | } |
262 | |
263 | |
264 | static wchar_t* |
265 | substring(const wchar_t *str, size_t len) |
266 | { |
267 | wchar_t *substr = PyMem_RawMalloc((len + 1) * sizeof(wchar_t)); |
268 | if (substr == NULL) { |
269 | return NULL; |
270 | } |
271 | |
272 | if (len) { |
273 | memcpy(substr, str, len * sizeof(wchar_t)); |
274 | } |
275 | substr[len] = L'\0'; |
276 | return substr; |
277 | } |
278 | |
279 | |
280 | static wchar_t* |
281 | joinpath2(const wchar_t *path, const wchar_t *path2) |
282 | { |
283 | if (_Py_isabs(path2)) { |
284 | return _PyMem_RawWcsdup(path2); |
285 | } |
286 | |
287 | size_t len = wcslen(path); |
288 | int add_sep = (len > 0 && path[len - 1] != SEP); |
289 | len += add_sep; |
290 | len += wcslen(path2); |
291 | |
292 | wchar_t *new_path = PyMem_RawMalloc((len + 1) * sizeof(wchar_t)); |
293 | if (new_path == NULL) { |
294 | return NULL; |
295 | } |
296 | |
297 | wcscpy(new_path, path); |
298 | if (add_sep) { |
299 | wcscat(new_path, separator); |
300 | } |
301 | wcscat(new_path, path2); |
302 | return new_path; |
303 | } |
304 | |
305 | |
306 | static inline int |
307 | safe_wcscpy(wchar_t *dst, const wchar_t *src, size_t n) |
308 | { |
309 | size_t srclen = wcslen(src); |
310 | if (n <= srclen) { |
311 | dst[0] = L'\0'; |
312 | return -1; |
313 | } |
314 | memcpy(dst, src, (srclen + 1) * sizeof(wchar_t)); |
315 | return 0; |
316 | } |
317 | |
318 | |
319 | /* copy_absolute requires that path be allocated at least |
320 | 'abs_path_len' characters (including trailing NUL). */ |
321 | static PyStatus |
322 | copy_absolute(wchar_t *abs_path, const wchar_t *path, size_t abs_path_len) |
323 | { |
324 | if (_Py_isabs(path)) { |
325 | if (safe_wcscpy(abs_path, path, abs_path_len) < 0) { |
326 | return PATHLEN_ERR(); |
327 | } |
328 | } |
329 | else { |
330 | if (!_Py_wgetcwd(abs_path, abs_path_len)) { |
331 | /* unable to get the current directory */ |
332 | if (safe_wcscpy(abs_path, path, abs_path_len) < 0) { |
333 | return PATHLEN_ERR(); |
334 | } |
335 | return _PyStatus_OK(); |
336 | } |
337 | if (path[0] == '.' && path[1] == SEP) { |
338 | path += 2; |
339 | } |
340 | PyStatus status = joinpath(abs_path, path, abs_path_len); |
341 | if (_PyStatus_EXCEPTION(status)) { |
342 | return status; |
343 | } |
344 | } |
345 | return _PyStatus_OK(); |
346 | } |
347 | |
348 | |
349 | /* path_len: path length in characters including trailing NUL */ |
350 | static PyStatus |
351 | absolutize(wchar_t **path_p) |
352 | { |
353 | assert(!_Py_isabs(*path_p)); |
354 | |
355 | wchar_t abs_path[MAXPATHLEN+1]; |
356 | wchar_t *path = *path_p; |
357 | |
358 | PyStatus status = copy_absolute(abs_path, path, Py_ARRAY_LENGTH(abs_path)); |
359 | if (_PyStatus_EXCEPTION(status)) { |
360 | return status; |
361 | } |
362 | |
363 | PyMem_RawFree(*path_p); |
364 | *path_p = _PyMem_RawWcsdup(abs_path); |
365 | if (*path_p == NULL) { |
366 | return _PyStatus_NO_MEMORY(); |
367 | } |
368 | return _PyStatus_OK(); |
369 | } |
370 | |
371 | |
372 | /* Is module -- check for .pyc too */ |
373 | static PyStatus |
374 | ismodule(const wchar_t *path, int *result) |
375 | { |
376 | wchar_t *filename = joinpath2(path, LANDMARK); |
377 | if (filename == NULL) { |
378 | return _PyStatus_NO_MEMORY(); |
379 | } |
380 | |
381 | if (isfile(filename)) { |
382 | PyMem_RawFree(filename); |
383 | *result = 1; |
384 | return _PyStatus_OK(); |
385 | } |
386 | |
387 | /* Check for the compiled version of prefix. */ |
388 | size_t len = wcslen(filename); |
389 | wchar_t *pyc = PyMem_RawMalloc((len + 2) * sizeof(wchar_t)); |
390 | if (pyc == NULL) { |
391 | PyMem_RawFree(filename); |
392 | return _PyStatus_NO_MEMORY(); |
393 | } |
394 | |
395 | memcpy(pyc, filename, len * sizeof(wchar_t)); |
396 | pyc[len] = L'c'; |
397 | pyc[len + 1] = L'\0'; |
398 | *result = isfile(pyc); |
399 | |
400 | PyMem_RawFree(filename); |
401 | PyMem_RawFree(pyc); |
402 | |
403 | return _PyStatus_OK(); |
404 | } |
405 | |
406 | |
407 | #if defined(__CYGWIN__) || defined(__MINGW32__) |
408 | #ifndef EXE_SUFFIX |
409 | #define EXE_SUFFIX L".exe" |
410 | #endif |
411 | |
412 | /* pathlen: 'path' length in characters including trailing NUL */ |
413 | static PyStatus |
414 | add_exe_suffix(wchar_t **progpath_p) |
415 | { |
416 | wchar_t *progpath = *progpath_p; |
417 | |
418 | /* Check for already have an executable suffix */ |
419 | size_t n = wcslen(progpath); |
420 | size_t s = wcslen(EXE_SUFFIX); |
421 | if (wcsncasecmp(EXE_SUFFIX, progpath + n - s, s) == 0) { |
422 | return _PyStatus_OK(); |
423 | } |
424 | |
425 | wchar_t *progpath2 = PyMem_RawMalloc((n + s + 1) * sizeof(wchar_t)); |
426 | if (progpath2 == NULL) { |
427 | return _PyStatus_NO_MEMORY(); |
428 | } |
429 | |
430 | memcpy(progpath2, progpath, n * sizeof(wchar_t)); |
431 | memcpy(progpath2 + n, EXE_SUFFIX, s * sizeof(wchar_t)); |
432 | progpath2[n+s] = L'\0'; |
433 | |
434 | if (isxfile(progpath2)) { |
435 | PyMem_RawFree(*progpath_p); |
436 | *progpath_p = progpath2; |
437 | } |
438 | else { |
439 | PyMem_RawFree(progpath2); |
440 | } |
441 | return _PyStatus_OK(); |
442 | } |
443 | #endif |
444 | |
445 | |
446 | /* search_for_prefix requires that argv0_path be no more than MAXPATHLEN |
447 | bytes long. |
448 | */ |
449 | static PyStatus |
450 | search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, |
451 | wchar_t *prefix, size_t prefix_len, int *found) |
452 | { |
453 | PyStatus status; |
454 | |
455 | /* If PYTHONHOME is set, we believe it unconditionally */ |
456 | if (pathconfig->home) { |
457 | /* Path: <home> / <lib_python> */ |
458 | if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) { |
459 | return PATHLEN_ERR(); |
460 | } |
461 | wchar_t *delim = wcschr(prefix, DELIM); |
462 | if (delim) { |
463 | *delim = L'\0'; |
464 | } |
465 | status = joinpath(prefix, calculate->lib_python, prefix_len); |
466 | if (_PyStatus_EXCEPTION(status)) { |
467 | return status; |
468 | } |
469 | *found = 1; |
470 | return _PyStatus_OK(); |
471 | } |
472 | |
473 | /* Check to see if argv0_path is in the build directory |
474 | |
475 | Path: <argv0_path> / <BUILD_LANDMARK define> */ |
476 | wchar_t *path = joinpath2(calculate->argv0_path, BUILD_LANDMARK); |
477 | if (path == NULL) { |
478 | return _PyStatus_NO_MEMORY(); |
479 | } |
480 | |
481 | int is_build_dir = isfile(path); |
482 | PyMem_RawFree(path); |
483 | |
484 | if (is_build_dir) { |
485 | /* argv0_path is the build directory (BUILD_LANDMARK exists), |
486 | now also check LANDMARK using ismodule(). */ |
487 | |
488 | /* Path: <argv0_path> / <VPATH macro> / Lib */ |
489 | /* or if VPATH is empty: <argv0_path> / Lib */ |
490 | if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) { |
491 | return PATHLEN_ERR(); |
492 | } |
493 | |
494 | status = joinpath(prefix, calculate->vpath_macro, prefix_len); |
495 | if (_PyStatus_EXCEPTION(status)) { |
496 | return status; |
497 | } |
498 | |
499 | status = joinpath(prefix, L"Lib" , prefix_len); |
500 | if (_PyStatus_EXCEPTION(status)) { |
501 | return status; |
502 | } |
503 | |
504 | int module; |
505 | status = ismodule(prefix, &module); |
506 | if (_PyStatus_EXCEPTION(status)) { |
507 | return status; |
508 | } |
509 | if (module) { |
510 | /* BUILD_LANDMARK and LANDMARK found */ |
511 | *found = -1; |
512 | return _PyStatus_OK(); |
513 | } |
514 | } |
515 | |
516 | /* Search from argv0_path, until root is found */ |
517 | status = copy_absolute(prefix, calculate->argv0_path, prefix_len); |
518 | if (_PyStatus_EXCEPTION(status)) { |
519 | return status; |
520 | } |
521 | |
522 | do { |
523 | /* Path: <argv0_path or substring> / <lib_python> / LANDMARK */ |
524 | size_t n = wcslen(prefix); |
525 | status = joinpath(prefix, calculate->lib_python, prefix_len); |
526 | if (_PyStatus_EXCEPTION(status)) { |
527 | return status; |
528 | } |
529 | |
530 | int module; |
531 | status = ismodule(prefix, &module); |
532 | if (_PyStatus_EXCEPTION(status)) { |
533 | return status; |
534 | } |
535 | if (module) { |
536 | *found = 1; |
537 | return _PyStatus_OK(); |
538 | } |
539 | prefix[n] = L'\0'; |
540 | reduce(prefix); |
541 | } while (prefix[0]); |
542 | |
543 | /* Look at configure's PREFIX. |
544 | Path: <PREFIX macro> / <lib_python> / LANDMARK */ |
545 | if (safe_wcscpy(prefix, calculate->prefix_macro, prefix_len) < 0) { |
546 | return PATHLEN_ERR(); |
547 | } |
548 | status = joinpath(prefix, calculate->lib_python, prefix_len); |
549 | if (_PyStatus_EXCEPTION(status)) { |
550 | return status; |
551 | } |
552 | |
553 | int module; |
554 | status = ismodule(prefix, &module); |
555 | if (_PyStatus_EXCEPTION(status)) { |
556 | return status; |
557 | } |
558 | if (module) { |
559 | *found = 1; |
560 | return _PyStatus_OK(); |
561 | } |
562 | |
563 | /* Fail */ |
564 | *found = 0; |
565 | return _PyStatus_OK(); |
566 | } |
567 | |
568 | |
569 | static PyStatus |
570 | calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
571 | { |
572 | wchar_t prefix[MAXPATHLEN+1]; |
573 | memset(prefix, 0, sizeof(prefix)); |
574 | size_t prefix_len = Py_ARRAY_LENGTH(prefix); |
575 | |
576 | PyStatus status; |
577 | status = search_for_prefix(calculate, pathconfig, |
578 | prefix, prefix_len, |
579 | &calculate->prefix_found); |
580 | if (_PyStatus_EXCEPTION(status)) { |
581 | return status; |
582 | } |
583 | |
584 | if (!calculate->prefix_found) { |
585 | if (calculate->warnings) { |
586 | fprintf(stderr, |
587 | "Could not find platform independent libraries <prefix>\n" ); |
588 | } |
589 | |
590 | calculate->prefix = joinpath2(calculate->prefix_macro, |
591 | calculate->lib_python); |
592 | } |
593 | else { |
594 | calculate->prefix = _PyMem_RawWcsdup(prefix); |
595 | } |
596 | |
597 | if (calculate->prefix == NULL) { |
598 | return _PyStatus_NO_MEMORY(); |
599 | } |
600 | return _PyStatus_OK(); |
601 | } |
602 | |
603 | |
604 | static PyStatus |
605 | calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
606 | { |
607 | /* Reduce prefix and exec_prefix to their essence, |
608 | * e.g. /usr/local/lib/python1.5 is reduced to /usr/local. |
609 | * If we're loading relative to the build directory, |
610 | * return the compiled-in defaults instead. |
611 | */ |
612 | if (calculate->prefix_found > 0) { |
613 | wchar_t *prefix = _PyMem_RawWcsdup(calculate->prefix); |
614 | if (prefix == NULL) { |
615 | return _PyStatus_NO_MEMORY(); |
616 | } |
617 | |
618 | reduce(prefix); |
619 | reduce(prefix); |
620 | if (prefix[0]) { |
621 | pathconfig->prefix = prefix; |
622 | } |
623 | else { |
624 | PyMem_RawFree(prefix); |
625 | |
626 | /* The prefix is the root directory, but reduce() chopped |
627 | off the "/". */ |
628 | pathconfig->prefix = _PyMem_RawWcsdup(separator); |
629 | if (pathconfig->prefix == NULL) { |
630 | return _PyStatus_NO_MEMORY(); |
631 | } |
632 | } |
633 | } |
634 | else { |
635 | pathconfig->prefix = _PyMem_RawWcsdup(calculate->prefix_macro); |
636 | if (pathconfig->prefix == NULL) { |
637 | return _PyStatus_NO_MEMORY(); |
638 | } |
639 | } |
640 | return _PyStatus_OK(); |
641 | } |
642 | |
643 | |
644 | static PyStatus |
645 | calculate_pybuilddir(const wchar_t *argv0_path, |
646 | wchar_t *exec_prefix, size_t exec_prefix_len, |
647 | int *found) |
648 | { |
649 | PyStatus status; |
650 | |
651 | /* Check to see if argv[0] is in the build directory. "pybuilddir.txt" |
652 | is written by setup.py and contains the relative path to the location |
653 | of shared library modules. |
654 | |
655 | Filename: <argv0_path> / "pybuilddir.txt" */ |
656 | wchar_t *filename = joinpath2(argv0_path, L"pybuilddir.txt" ); |
657 | if (filename == NULL) { |
658 | return _PyStatus_NO_MEMORY(); |
659 | } |
660 | |
661 | FILE *fp = _Py_wfopen(filename, L"rb" ); |
662 | PyMem_RawFree(filename); |
663 | if (fp == NULL) { |
664 | errno = 0; |
665 | return _PyStatus_OK(); |
666 | } |
667 | |
668 | char buf[MAXPATHLEN + 1]; |
669 | size_t n = fread(buf, 1, Py_ARRAY_LENGTH(buf) - 1, fp); |
670 | buf[n] = '\0'; |
671 | fclose(fp); |
672 | |
673 | size_t dec_len; |
674 | wchar_t *pybuilddir = _Py_DecodeUTF8_surrogateescape(buf, n, &dec_len); |
675 | if (!pybuilddir) { |
676 | return DECODE_LOCALE_ERR("pybuilddir.txt" , dec_len); |
677 | } |
678 | |
679 | /* Path: <argv0_path> / <pybuilddir content> */ |
680 | if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) { |
681 | PyMem_RawFree(pybuilddir); |
682 | return PATHLEN_ERR(); |
683 | } |
684 | status = joinpath(exec_prefix, pybuilddir, exec_prefix_len); |
685 | PyMem_RawFree(pybuilddir); |
686 | if (_PyStatus_EXCEPTION(status)) { |
687 | return status; |
688 | } |
689 | |
690 | *found = -1; |
691 | return _PyStatus_OK(); |
692 | } |
693 | |
694 | |
695 | /* search_for_exec_prefix requires that argv0_path be no more than |
696 | MAXPATHLEN bytes long. |
697 | */ |
698 | static PyStatus |
699 | search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, |
700 | wchar_t *exec_prefix, size_t exec_prefix_len, |
701 | int *found) |
702 | { |
703 | PyStatus status; |
704 | |
705 | /* If PYTHONHOME is set, we believe it unconditionally */ |
706 | if (pathconfig->home) { |
707 | /* Path: <home> / <lib_python> / "lib-dynload" */ |
708 | wchar_t *delim = wcschr(pathconfig->home, DELIM); |
709 | if (delim) { |
710 | if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) { |
711 | return PATHLEN_ERR(); |
712 | } |
713 | } |
714 | else { |
715 | if (safe_wcscpy(exec_prefix, pathconfig->home, exec_prefix_len) < 0) { |
716 | return PATHLEN_ERR(); |
717 | } |
718 | } |
719 | status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len); |
720 | if (_PyStatus_EXCEPTION(status)) { |
721 | return status; |
722 | } |
723 | status = joinpath(exec_prefix, L"lib-dynload" , exec_prefix_len); |
724 | if (_PyStatus_EXCEPTION(status)) { |
725 | return status; |
726 | } |
727 | *found = 1; |
728 | return _PyStatus_OK(); |
729 | } |
730 | |
731 | /* Check for pybuilddir.txt */ |
732 | assert(*found == 0); |
733 | status = calculate_pybuilddir(calculate->argv0_path, |
734 | exec_prefix, exec_prefix_len, found); |
735 | if (_PyStatus_EXCEPTION(status)) { |
736 | return status; |
737 | } |
738 | if (*found) { |
739 | return _PyStatus_OK(); |
740 | } |
741 | |
742 | /* Search from argv0_path, until root is found */ |
743 | status = copy_absolute(exec_prefix, calculate->argv0_path, exec_prefix_len); |
744 | if (_PyStatus_EXCEPTION(status)) { |
745 | return status; |
746 | } |
747 | |
748 | do { |
749 | /* Path: <argv0_path or substring> / <lib_python> / "lib-dynload" */ |
750 | size_t n = wcslen(exec_prefix); |
751 | status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len); |
752 | if (_PyStatus_EXCEPTION(status)) { |
753 | return status; |
754 | } |
755 | status = joinpath(exec_prefix, L"lib-dynload" , exec_prefix_len); |
756 | if (_PyStatus_EXCEPTION(status)) { |
757 | return status; |
758 | } |
759 | if (isdir(exec_prefix)) { |
760 | *found = 1; |
761 | return _PyStatus_OK(); |
762 | } |
763 | exec_prefix[n] = L'\0'; |
764 | reduce(exec_prefix); |
765 | } while (exec_prefix[0]); |
766 | |
767 | /* Look at configure's EXEC_PREFIX. |
768 | |
769 | Path: <EXEC_PREFIX macro> / <lib_python> / "lib-dynload" */ |
770 | if (safe_wcscpy(exec_prefix, calculate->exec_prefix_macro, exec_prefix_len) < 0) { |
771 | return PATHLEN_ERR(); |
772 | } |
773 | status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len); |
774 | if (_PyStatus_EXCEPTION(status)) { |
775 | return status; |
776 | } |
777 | status = joinpath(exec_prefix, L"lib-dynload" , exec_prefix_len); |
778 | if (_PyStatus_EXCEPTION(status)) { |
779 | return status; |
780 | } |
781 | if (isdir(exec_prefix)) { |
782 | *found = 1; |
783 | return _PyStatus_OK(); |
784 | } |
785 | |
786 | /* Fail */ |
787 | *found = 0; |
788 | return _PyStatus_OK(); |
789 | } |
790 | |
791 | |
792 | static PyStatus |
793 | calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
794 | { |
795 | PyStatus status; |
796 | wchar_t exec_prefix[MAXPATHLEN+1]; |
797 | memset(exec_prefix, 0, sizeof(exec_prefix)); |
798 | size_t exec_prefix_len = Py_ARRAY_LENGTH(exec_prefix); |
799 | |
800 | status = search_for_exec_prefix(calculate, pathconfig, |
801 | exec_prefix, exec_prefix_len, |
802 | &calculate->exec_prefix_found); |
803 | if (_PyStatus_EXCEPTION(status)) { |
804 | return status; |
805 | } |
806 | |
807 | if (!calculate->exec_prefix_found) { |
808 | if (calculate->warnings) { |
809 | fprintf(stderr, |
810 | "Could not find platform dependent libraries <exec_prefix>\n" ); |
811 | } |
812 | |
813 | /* <platlibdir> / "lib-dynload" */ |
814 | wchar_t *lib_dynload = joinpath2(calculate->platlibdir, |
815 | L"lib-dynload" ); |
816 | if (lib_dynload == NULL) { |
817 | return _PyStatus_NO_MEMORY(); |
818 | } |
819 | |
820 | calculate->exec_prefix = joinpath2(calculate->exec_prefix_macro, |
821 | lib_dynload); |
822 | PyMem_RawFree(lib_dynload); |
823 | |
824 | if (calculate->exec_prefix == NULL) { |
825 | return _PyStatus_NO_MEMORY(); |
826 | } |
827 | } |
828 | else { |
829 | /* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */ |
830 | calculate->exec_prefix = _PyMem_RawWcsdup(exec_prefix); |
831 | if (calculate->exec_prefix == NULL) { |
832 | return _PyStatus_NO_MEMORY(); |
833 | } |
834 | } |
835 | return _PyStatus_OK(); |
836 | } |
837 | |
838 | |
839 | static PyStatus |
840 | calculate_set_exec_prefix(PyCalculatePath *calculate, |
841 | _PyPathConfig *pathconfig) |
842 | { |
843 | if (calculate->exec_prefix_found > 0) { |
844 | wchar_t *exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix); |
845 | if (exec_prefix == NULL) { |
846 | return _PyStatus_NO_MEMORY(); |
847 | } |
848 | |
849 | reduce(exec_prefix); |
850 | reduce(exec_prefix); |
851 | reduce(exec_prefix); |
852 | |
853 | if (exec_prefix[0]) { |
854 | pathconfig->exec_prefix = exec_prefix; |
855 | } |
856 | else { |
857 | /* empty string: use SEP instead */ |
858 | PyMem_RawFree(exec_prefix); |
859 | |
860 | /* The exec_prefix is the root directory, but reduce() chopped |
861 | off the "/". */ |
862 | pathconfig->exec_prefix = _PyMem_RawWcsdup(separator); |
863 | if (pathconfig->exec_prefix == NULL) { |
864 | return _PyStatus_NO_MEMORY(); |
865 | } |
866 | } |
867 | } |
868 | else { |
869 | pathconfig->exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix_macro); |
870 | if (pathconfig->exec_prefix == NULL) { |
871 | return _PyStatus_NO_MEMORY(); |
872 | } |
873 | } |
874 | return _PyStatus_OK(); |
875 | } |
876 | |
877 | |
878 | /* Similar to shutil.which(). |
879 | If found, write the path into *abs_path_p. */ |
880 | static PyStatus |
881 | calculate_which(const wchar_t *path_env, wchar_t *program_name, |
882 | wchar_t **abs_path_p) |
883 | { |
884 | while (1) { |
885 | wchar_t *delim = wcschr(path_env, DELIM); |
886 | wchar_t *abs_path; |
887 | |
888 | if (delim) { |
889 | wchar_t *path = substring(path_env, delim - path_env); |
890 | if (path == NULL) { |
891 | return _PyStatus_NO_MEMORY(); |
892 | } |
893 | abs_path = joinpath2(path, program_name); |
894 | PyMem_RawFree(path); |
895 | } |
896 | else { |
897 | abs_path = joinpath2(path_env, program_name); |
898 | } |
899 | |
900 | if (abs_path == NULL) { |
901 | return _PyStatus_NO_MEMORY(); |
902 | } |
903 | |
904 | if (isxfile(abs_path)) { |
905 | *abs_path_p = abs_path; |
906 | return _PyStatus_OK(); |
907 | } |
908 | PyMem_RawFree(abs_path); |
909 | |
910 | if (!delim) { |
911 | break; |
912 | } |
913 | path_env = delim + 1; |
914 | } |
915 | |
916 | /* not found */ |
917 | return _PyStatus_OK(); |
918 | } |
919 | |
920 | |
921 | #ifdef __APPLE__ |
922 | static PyStatus |
923 | calculate_program_macos(wchar_t **abs_path_p) |
924 | { |
925 | char execpath[MAXPATHLEN + 1]; |
926 | uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1; |
927 | |
928 | /* On Mac OS X, if a script uses an interpreter of the form |
929 | "#!/opt/python2.3/bin/python", the kernel only passes "python" |
930 | as argv[0], which falls through to the $PATH search below. |
931 | If /opt/python2.3/bin isn't in your path, or is near the end, |
932 | this algorithm may incorrectly find /usr/bin/python. To work |
933 | around this, we can use _NSGetExecutablePath to get a better |
934 | hint of what the intended interpreter was, although this |
935 | will fail if a relative path was used. but in that case, |
936 | absolutize() should help us out below |
937 | */ |
938 | if (_NSGetExecutablePath(execpath, &nsexeclength) != 0 |
939 | || (wchar_t)execpath[0] != SEP) |
940 | { |
941 | /* _NSGetExecutablePath() failed or the path is relative */ |
942 | return _PyStatus_OK(); |
943 | } |
944 | |
945 | size_t len; |
946 | *abs_path_p = Py_DecodeLocale(execpath, &len); |
947 | if (*abs_path_p == NULL) { |
948 | return DECODE_LOCALE_ERR("executable path" , len); |
949 | } |
950 | return _PyStatus_OK(); |
951 | } |
952 | #endif /* __APPLE__ */ |
953 | |
954 | |
955 | static PyStatus |
956 | calculate_program_impl(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
957 | { |
958 | assert(pathconfig->program_full_path == NULL); |
959 | |
960 | PyStatus status; |
961 | |
962 | /* If there is no slash in the argv0 path, then we have to |
963 | * assume python is on the user's $PATH, since there's no |
964 | * other way to find a directory to start the search from. If |
965 | * $PATH isn't exported, you lose. |
966 | */ |
967 | if (wcschr(pathconfig->program_name, SEP)) { |
968 | pathconfig->program_full_path = _PyMem_RawWcsdup(pathconfig->program_name); |
969 | if (pathconfig->program_full_path == NULL) { |
970 | return _PyStatus_NO_MEMORY(); |
971 | } |
972 | return _PyStatus_OK(); |
973 | } |
974 | |
975 | #ifdef __APPLE__ |
976 | wchar_t *abs_path = NULL; |
977 | status = calculate_program_macos(&abs_path); |
978 | if (_PyStatus_EXCEPTION(status)) { |
979 | return status; |
980 | } |
981 | if (abs_path) { |
982 | pathconfig->program_full_path = abs_path; |
983 | return _PyStatus_OK(); |
984 | } |
985 | #endif /* __APPLE__ */ |
986 | |
987 | if (calculate->path_env) { |
988 | wchar_t *abs_path = NULL; |
989 | status = calculate_which(calculate->path_env, pathconfig->program_name, |
990 | &abs_path); |
991 | if (_PyStatus_EXCEPTION(status)) { |
992 | return status; |
993 | } |
994 | if (abs_path) { |
995 | pathconfig->program_full_path = abs_path; |
996 | return _PyStatus_OK(); |
997 | } |
998 | } |
999 | |
1000 | /* In the last resort, use an empty string */ |
1001 | pathconfig->program_full_path = _PyMem_RawWcsdup(L"" ); |
1002 | if (pathconfig->program_full_path == NULL) { |
1003 | return _PyStatus_NO_MEMORY(); |
1004 | } |
1005 | return _PyStatus_OK(); |
1006 | } |
1007 | |
1008 | |
1009 | /* Calculate pathconfig->program_full_path */ |
1010 | static PyStatus |
1011 | calculate_program(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
1012 | { |
1013 | PyStatus status; |
1014 | |
1015 | status = calculate_program_impl(calculate, pathconfig); |
1016 | if (_PyStatus_EXCEPTION(status)) { |
1017 | return status; |
1018 | } |
1019 | |
1020 | if (pathconfig->program_full_path[0] != '\0') { |
1021 | /* program_full_path is not empty */ |
1022 | |
1023 | /* Make sure that program_full_path is an absolute path */ |
1024 | if (!_Py_isabs(pathconfig->program_full_path)) { |
1025 | status = absolutize(&pathconfig->program_full_path); |
1026 | if (_PyStatus_EXCEPTION(status)) { |
1027 | return status; |
1028 | } |
1029 | } |
1030 | |
1031 | #if defined(__CYGWIN__) || defined(__MINGW32__) |
1032 | /* For these platforms it is necessary to ensure that the .exe suffix |
1033 | * is appended to the filename, otherwise there is potential for |
1034 | * sys.executable to return the name of a directory under the same |
1035 | * path (bpo-28441). |
1036 | */ |
1037 | status = add_exe_suffix(&pathconfig->program_full_path); |
1038 | if (_PyStatus_EXCEPTION(status)) { |
1039 | return status; |
1040 | } |
1041 | #endif |
1042 | } |
1043 | return _PyStatus_OK(); |
1044 | } |
1045 | |
1046 | |
1047 | #if HAVE_READLINK |
1048 | static PyStatus |
1049 | resolve_symlinks(wchar_t **path_p) |
1050 | { |
1051 | wchar_t new_path[MAXPATHLEN + 1]; |
1052 | const size_t new_path_len = Py_ARRAY_LENGTH(new_path); |
1053 | unsigned int nlink = 0; |
1054 | |
1055 | while (1) { |
1056 | int linklen = _Py_wreadlink(*path_p, new_path, new_path_len); |
1057 | if (linklen == -1) { |
1058 | /* not a symbolic link: we are done */ |
1059 | break; |
1060 | } |
1061 | |
1062 | if (_Py_isabs(new_path)) { |
1063 | PyMem_RawFree(*path_p); |
1064 | *path_p = _PyMem_RawWcsdup(new_path); |
1065 | if (*path_p == NULL) { |
1066 | return _PyStatus_NO_MEMORY(); |
1067 | } |
1068 | } |
1069 | else { |
1070 | /* new_path is relative to path */ |
1071 | reduce(*path_p); |
1072 | |
1073 | wchar_t *abs_path = joinpath2(*path_p, new_path); |
1074 | if (abs_path == NULL) { |
1075 | return _PyStatus_NO_MEMORY(); |
1076 | } |
1077 | |
1078 | PyMem_RawFree(*path_p); |
1079 | *path_p = abs_path; |
1080 | } |
1081 | |
1082 | nlink++; |
1083 | /* 40 is the Linux kernel 4.2 limit */ |
1084 | if (nlink >= 40) { |
1085 | return _PyStatus_ERR("maximum number of symbolic links reached" ); |
1086 | } |
1087 | } |
1088 | return _PyStatus_OK(); |
1089 | } |
1090 | #endif /* HAVE_READLINK */ |
1091 | |
1092 | |
1093 | #ifdef WITH_NEXT_FRAMEWORK |
1094 | static PyStatus |
1095 | calculate_argv0_path_framework(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
1096 | { |
1097 | NSModule pythonModule; |
1098 | |
1099 | /* On Mac OS X we have a special case if we're running from a framework. |
1100 | This is because the python home should be set relative to the library, |
1101 | which is in the framework, not relative to the executable, which may |
1102 | be outside of the framework. Except when we're in the build |
1103 | directory... */ |
1104 | pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize" )); |
1105 | |
1106 | /* Use dylib functions to find out where the framework was loaded from */ |
1107 | const char* modPath = NSLibraryNameForModule(pythonModule); |
1108 | if (modPath == NULL) { |
1109 | return _PyStatus_OK(); |
1110 | } |
1111 | |
1112 | /* We're in a framework. |
1113 | See if we might be in the build directory. The framework in the |
1114 | build directory is incomplete, it only has the .dylib and a few |
1115 | needed symlinks, it doesn't have the Lib directories and such. |
1116 | If we're running with the framework from the build directory we must |
1117 | be running the interpreter in the build directory, so we use the |
1118 | build-directory-specific logic to find Lib and such. */ |
1119 | size_t len; |
1120 | wchar_t* wbuf = Py_DecodeLocale(modPath, &len); |
1121 | if (wbuf == NULL) { |
1122 | return DECODE_LOCALE_ERR("framework location" , len); |
1123 | } |
1124 | |
1125 | /* Path: reduce(modPath) / lib_python / LANDMARK */ |
1126 | PyStatus status; |
1127 | |
1128 | wchar_t *parent = _PyMem_RawWcsdup(wbuf); |
1129 | if (parent == NULL) { |
1130 | status = _PyStatus_NO_MEMORY(); |
1131 | goto done; |
1132 | } |
1133 | |
1134 | reduce(parent); |
1135 | wchar_t *lib_python = joinpath2(parent, calculate->lib_python); |
1136 | PyMem_RawFree(parent); |
1137 | |
1138 | if (lib_python == NULL) { |
1139 | status = _PyStatus_NO_MEMORY(); |
1140 | goto done; |
1141 | } |
1142 | |
1143 | int module; |
1144 | status = ismodule(lib_python, &module); |
1145 | PyMem_RawFree(lib_python); |
1146 | |
1147 | if (_PyStatus_EXCEPTION(status)) { |
1148 | goto done; |
1149 | } |
1150 | if (!module) { |
1151 | /* We are in the build directory so use the name of the |
1152 | executable - we know that the absolute path is passed */ |
1153 | PyMem_RawFree(calculate->argv0_path); |
1154 | calculate->argv0_path = _PyMem_RawWcsdup(pathconfig->program_full_path); |
1155 | if (calculate->argv0_path == NULL) { |
1156 | status = _PyStatus_NO_MEMORY(); |
1157 | goto done; |
1158 | } |
1159 | |
1160 | status = _PyStatus_OK(); |
1161 | goto done; |
1162 | } |
1163 | |
1164 | /* Use the location of the library as argv0_path */ |
1165 | PyMem_RawFree(calculate->argv0_path); |
1166 | calculate->argv0_path = wbuf; |
1167 | return _PyStatus_OK(); |
1168 | |
1169 | done: |
1170 | PyMem_RawFree(wbuf); |
1171 | return status; |
1172 | } |
1173 | #endif |
1174 | |
1175 | |
1176 | static PyStatus |
1177 | calculate_argv0_path(PyCalculatePath *calculate, |
1178 | _PyPathConfig *pathconfig) |
1179 | { |
1180 | PyStatus status; |
1181 | |
1182 | calculate->argv0_path = _PyMem_RawWcsdup(pathconfig->program_full_path); |
1183 | if (calculate->argv0_path == NULL) { |
1184 | return _PyStatus_NO_MEMORY(); |
1185 | } |
1186 | |
1187 | #ifdef WITH_NEXT_FRAMEWORK |
1188 | status = calculate_argv0_path_framework(calculate, pathconfig); |
1189 | if (_PyStatus_EXCEPTION(status)) { |
1190 | return status; |
1191 | } |
1192 | #endif |
1193 | |
1194 | status = resolve_symlinks(&calculate->argv0_path); |
1195 | if (_PyStatus_EXCEPTION(status)) { |
1196 | return status; |
1197 | } |
1198 | |
1199 | reduce(calculate->argv0_path); |
1200 | |
1201 | return _PyStatus_OK(); |
1202 | } |
1203 | |
1204 | |
1205 | static PyStatus |
1206 | calculate_open_pyenv(PyCalculatePath *calculate, FILE **env_file_p) |
1207 | { |
1208 | *env_file_p = NULL; |
1209 | |
1210 | const wchar_t *env_cfg = L"pyvenv.cfg" ; |
1211 | |
1212 | /* Filename: <argv0_path> / "pyvenv.cfg" */ |
1213 | wchar_t *filename = joinpath2(calculate->argv0_path, env_cfg); |
1214 | if (filename == NULL) { |
1215 | return _PyStatus_NO_MEMORY(); |
1216 | } |
1217 | |
1218 | *env_file_p = _Py_wfopen(filename, L"r" ); |
1219 | PyMem_RawFree(filename); |
1220 | |
1221 | if (*env_file_p != NULL) { |
1222 | return _PyStatus_OK(); |
1223 | |
1224 | } |
1225 | |
1226 | /* fopen() failed: reset errno */ |
1227 | errno = 0; |
1228 | |
1229 | /* Path: <basename(argv0_path)> / "pyvenv.cfg" */ |
1230 | wchar_t *parent = _PyMem_RawWcsdup(calculate->argv0_path); |
1231 | if (parent == NULL) { |
1232 | return _PyStatus_NO_MEMORY(); |
1233 | } |
1234 | reduce(parent); |
1235 | |
1236 | filename = joinpath2(parent, env_cfg); |
1237 | PyMem_RawFree(parent); |
1238 | if (filename == NULL) { |
1239 | return _PyStatus_NO_MEMORY(); |
1240 | } |
1241 | |
1242 | *env_file_p = _Py_wfopen(filename, L"r" ); |
1243 | PyMem_RawFree(filename); |
1244 | |
1245 | if (*env_file_p == NULL) { |
1246 | /* fopen() failed: reset errno */ |
1247 | errno = 0; |
1248 | } |
1249 | return _PyStatus_OK(); |
1250 | } |
1251 | |
1252 | |
1253 | /* Search for an "pyvenv.cfg" environment configuration file, first in the |
1254 | executable's directory and then in the parent directory. |
1255 | If found, open it for use when searching for prefixes. |
1256 | |
1257 | Write the 'home' variable of pyvenv.cfg into calculate->argv0_path. */ |
1258 | static PyStatus |
1259 | calculate_read_pyenv(PyCalculatePath *calculate) |
1260 | { |
1261 | PyStatus status; |
1262 | FILE *env_file = NULL; |
1263 | |
1264 | status = calculate_open_pyenv(calculate, &env_file); |
1265 | if (_PyStatus_EXCEPTION(status)) { |
1266 | assert(env_file == NULL); |
1267 | return status; |
1268 | } |
1269 | if (env_file == NULL) { |
1270 | /* pyvenv.cfg not found */ |
1271 | return _PyStatus_OK(); |
1272 | } |
1273 | |
1274 | /* Look for a 'home' variable and set argv0_path to it, if found */ |
1275 | wchar_t *home = NULL; |
1276 | status = _Py_FindEnvConfigValue(env_file, L"home" , &home); |
1277 | if (_PyStatus_EXCEPTION(status)) { |
1278 | fclose(env_file); |
1279 | return status; |
1280 | } |
1281 | |
1282 | if (home) { |
1283 | PyMem_RawFree(calculate->argv0_path); |
1284 | calculate->argv0_path = home; |
1285 | } |
1286 | fclose(env_file); |
1287 | return _PyStatus_OK(); |
1288 | } |
1289 | |
1290 | |
1291 | static PyStatus |
1292 | calculate_zip_path(PyCalculatePath *calculate) |
1293 | { |
1294 | PyStatus res; |
1295 | |
1296 | /* Path: <platlibdir> / "pythonXY.zip" */ |
1297 | wchar_t *path = joinpath2(calculate->platlibdir, |
1298 | L"python" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) |
1299 | L".zip" ); |
1300 | if (path == NULL) { |
1301 | return _PyStatus_NO_MEMORY(); |
1302 | } |
1303 | |
1304 | if (calculate->prefix_found > 0) { |
1305 | /* Use the reduced prefix returned by Py_GetPrefix() |
1306 | |
1307 | Path: <basename(basename(prefix))> / <platlibdir> / "pythonXY.zip" */ |
1308 | wchar_t *parent = _PyMem_RawWcsdup(calculate->prefix); |
1309 | if (parent == NULL) { |
1310 | res = _PyStatus_NO_MEMORY(); |
1311 | goto done; |
1312 | } |
1313 | reduce(parent); |
1314 | reduce(parent); |
1315 | calculate->zip_path = joinpath2(parent, path); |
1316 | PyMem_RawFree(parent); |
1317 | } |
1318 | else { |
1319 | calculate->zip_path = joinpath2(calculate->prefix_macro, path); |
1320 | } |
1321 | |
1322 | if (calculate->zip_path == NULL) { |
1323 | res = _PyStatus_NO_MEMORY(); |
1324 | goto done; |
1325 | } |
1326 | |
1327 | res = _PyStatus_OK(); |
1328 | |
1329 | done: |
1330 | PyMem_RawFree(path); |
1331 | return res; |
1332 | } |
1333 | |
1334 | |
1335 | static PyStatus |
1336 | calculate_module_search_path(PyCalculatePath *calculate, |
1337 | _PyPathConfig *pathconfig) |
1338 | { |
1339 | /* Calculate size of return buffer */ |
1340 | size_t bufsz = 0; |
1341 | if (calculate->pythonpath_env != NULL) { |
1342 | bufsz += wcslen(calculate->pythonpath_env) + 1; |
1343 | } |
1344 | |
1345 | wchar_t *defpath = calculate->pythonpath_macro; |
1346 | size_t prefixsz = wcslen(calculate->prefix) + 1; |
1347 | while (1) { |
1348 | wchar_t *delim = wcschr(defpath, DELIM); |
1349 | |
1350 | if (!_Py_isabs(defpath)) { |
1351 | /* Paths are relative to prefix */ |
1352 | bufsz += prefixsz; |
1353 | } |
1354 | |
1355 | if (delim) { |
1356 | bufsz += delim - defpath + 1; |
1357 | } |
1358 | else { |
1359 | bufsz += wcslen(defpath) + 1; |
1360 | break; |
1361 | } |
1362 | defpath = delim + 1; |
1363 | } |
1364 | |
1365 | bufsz += wcslen(calculate->zip_path) + 1; |
1366 | bufsz += wcslen(calculate->exec_prefix) + 1; |
1367 | |
1368 | /* Allocate the buffer */ |
1369 | wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t)); |
1370 | if (buf == NULL) { |
1371 | return _PyStatus_NO_MEMORY(); |
1372 | } |
1373 | buf[0] = '\0'; |
1374 | |
1375 | /* Run-time value of $PYTHONPATH goes first */ |
1376 | if (calculate->pythonpath_env) { |
1377 | wcscpy(buf, calculate->pythonpath_env); |
1378 | wcscat(buf, delimiter); |
1379 | } |
1380 | |
1381 | /* Next is the default zip path */ |
1382 | wcscat(buf, calculate->zip_path); |
1383 | wcscat(buf, delimiter); |
1384 | |
1385 | /* Next goes merge of compile-time $PYTHONPATH with |
1386 | * dynamically located prefix. |
1387 | */ |
1388 | defpath = calculate->pythonpath_macro; |
1389 | while (1) { |
1390 | wchar_t *delim = wcschr(defpath, DELIM); |
1391 | |
1392 | if (!_Py_isabs(defpath)) { |
1393 | wcscat(buf, calculate->prefix); |
1394 | if (prefixsz >= 2 && calculate->prefix[prefixsz - 2] != SEP && |
1395 | defpath[0] != (delim ? DELIM : L'\0')) |
1396 | { |
1397 | /* not empty */ |
1398 | wcscat(buf, separator); |
1399 | } |
1400 | } |
1401 | |
1402 | if (delim) { |
1403 | size_t len = delim - defpath + 1; |
1404 | size_t end = wcslen(buf) + len; |
1405 | wcsncat(buf, defpath, len); |
1406 | buf[end] = '\0'; |
1407 | } |
1408 | else { |
1409 | wcscat(buf, defpath); |
1410 | break; |
1411 | } |
1412 | defpath = delim + 1; |
1413 | } |
1414 | wcscat(buf, delimiter); |
1415 | |
1416 | /* Finally, on goes the directory for dynamic-load modules */ |
1417 | wcscat(buf, calculate->exec_prefix); |
1418 | |
1419 | pathconfig->module_search_path = buf; |
1420 | return _PyStatus_OK(); |
1421 | } |
1422 | |
1423 | |
1424 | static PyStatus |
1425 | calculate_init(PyCalculatePath *calculate, const PyConfig *config) |
1426 | { |
1427 | size_t len; |
1428 | |
1429 | calculate->warnings = config->pathconfig_warnings; |
1430 | calculate->pythonpath_env = config->pythonpath_env; |
1431 | calculate->platlibdir = config->platlibdir; |
1432 | |
1433 | const char *path = getenv("PATH" ); |
1434 | if (path) { |
1435 | calculate->path_env = Py_DecodeLocale(path, &len); |
1436 | if (!calculate->path_env) { |
1437 | return DECODE_LOCALE_ERR("PATH environment variable" , len); |
1438 | } |
1439 | } |
1440 | |
1441 | /* Decode macros */ |
1442 | calculate->pythonpath_macro = Py_DecodeLocale(PYTHONPATH, &len); |
1443 | if (!calculate->pythonpath_macro) { |
1444 | return DECODE_LOCALE_ERR("PYTHONPATH macro" , len); |
1445 | } |
1446 | calculate->prefix_macro = Py_DecodeLocale(PREFIX, &len); |
1447 | if (!calculate->prefix_macro) { |
1448 | return DECODE_LOCALE_ERR("PREFIX macro" , len); |
1449 | } |
1450 | calculate->exec_prefix_macro = Py_DecodeLocale(EXEC_PREFIX, &len); |
1451 | if (!calculate->exec_prefix_macro) { |
1452 | return DECODE_LOCALE_ERR("EXEC_PREFIX macro" , len); |
1453 | } |
1454 | calculate->vpath_macro = Py_DecodeLocale(VPATH, &len); |
1455 | if (!calculate->vpath_macro) { |
1456 | return DECODE_LOCALE_ERR("VPATH macro" , len); |
1457 | } |
1458 | |
1459 | // <platlibdir> / "pythonX.Y" |
1460 | wchar_t *pyversion = Py_DecodeLocale("python" VERSION, &len); |
1461 | if (!pyversion) { |
1462 | return DECODE_LOCALE_ERR("VERSION macro" , len); |
1463 | } |
1464 | calculate->lib_python = joinpath2(config->platlibdir, pyversion); |
1465 | PyMem_RawFree(pyversion); |
1466 | if (calculate->lib_python == NULL) { |
1467 | return _PyStatus_NO_MEMORY(); |
1468 | } |
1469 | |
1470 | return _PyStatus_OK(); |
1471 | } |
1472 | |
1473 | |
1474 | static void |
1475 | calculate_free(PyCalculatePath *calculate) |
1476 | { |
1477 | PyMem_RawFree(calculate->pythonpath_macro); |
1478 | PyMem_RawFree(calculate->prefix_macro); |
1479 | PyMem_RawFree(calculate->exec_prefix_macro); |
1480 | PyMem_RawFree(calculate->vpath_macro); |
1481 | PyMem_RawFree(calculate->lib_python); |
1482 | PyMem_RawFree(calculate->path_env); |
1483 | PyMem_RawFree(calculate->zip_path); |
1484 | PyMem_RawFree(calculate->argv0_path); |
1485 | PyMem_RawFree(calculate->prefix); |
1486 | PyMem_RawFree(calculate->exec_prefix); |
1487 | } |
1488 | |
1489 | |
1490 | static PyStatus |
1491 | calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
1492 | { |
1493 | PyStatus status; |
1494 | |
1495 | if (pathconfig->program_full_path == NULL) { |
1496 | status = calculate_program(calculate, pathconfig); |
1497 | if (_PyStatus_EXCEPTION(status)) { |
1498 | return status; |
1499 | } |
1500 | } |
1501 | |
1502 | status = calculate_argv0_path(calculate, pathconfig); |
1503 | if (_PyStatus_EXCEPTION(status)) { |
1504 | return status; |
1505 | } |
1506 | |
1507 | /* If a pyvenv.cfg configure file is found, |
1508 | argv0_path is overridden with its 'home' variable. */ |
1509 | status = calculate_read_pyenv(calculate); |
1510 | if (_PyStatus_EXCEPTION(status)) { |
1511 | return status; |
1512 | } |
1513 | |
1514 | status = calculate_prefix(calculate, pathconfig); |
1515 | if (_PyStatus_EXCEPTION(status)) { |
1516 | return status; |
1517 | } |
1518 | |
1519 | status = calculate_zip_path(calculate); |
1520 | if (_PyStatus_EXCEPTION(status)) { |
1521 | return status; |
1522 | } |
1523 | |
1524 | status = calculate_exec_prefix(calculate, pathconfig); |
1525 | if (_PyStatus_EXCEPTION(status)) { |
1526 | return status; |
1527 | } |
1528 | |
1529 | if ((!calculate->prefix_found || !calculate->exec_prefix_found) |
1530 | && calculate->warnings) |
1531 | { |
1532 | fprintf(stderr, |
1533 | "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n" ); |
1534 | } |
1535 | |
1536 | if (pathconfig->module_search_path == NULL) { |
1537 | status = calculate_module_search_path(calculate, pathconfig); |
1538 | if (_PyStatus_EXCEPTION(status)) { |
1539 | return status; |
1540 | } |
1541 | } |
1542 | |
1543 | if (pathconfig->prefix == NULL) { |
1544 | status = calculate_set_prefix(calculate, pathconfig); |
1545 | if (_PyStatus_EXCEPTION(status)) { |
1546 | return status; |
1547 | } |
1548 | } |
1549 | |
1550 | if (pathconfig->exec_prefix == NULL) { |
1551 | status = calculate_set_exec_prefix(calculate, pathconfig); |
1552 | if (_PyStatus_EXCEPTION(status)) { |
1553 | return status; |
1554 | } |
1555 | } |
1556 | return _PyStatus_OK(); |
1557 | } |
1558 | |
1559 | |
1560 | /* Calculate the Python path configuration. |
1561 | |
1562 | Inputs: |
1563 | |
1564 | - PATH environment variable |
1565 | - Macros: PYTHONPATH, PREFIX, EXEC_PREFIX, VERSION (ex: "3.9"). |
1566 | PREFIX and EXEC_PREFIX are generated by the configure script. |
1567 | PYTHONPATH macro is the default search path. |
1568 | - pybuilddir.txt file |
1569 | - pyvenv.cfg configuration file |
1570 | - PyConfig fields ('config' function argument): |
1571 | |
1572 | - pathconfig_warnings |
1573 | - pythonpath_env (PYTHONPATH environment variable) |
1574 | |
1575 | - _PyPathConfig fields ('pathconfig' function argument): |
1576 | |
1577 | - program_name: see config_init_program_name() |
1578 | - home: Py_SetPythonHome() or PYTHONHOME environment variable |
1579 | |
1580 | - current working directory: see copy_absolute() |
1581 | |
1582 | Outputs, 'pathconfig' fields: |
1583 | |
1584 | - program_full_path |
1585 | - module_search_path |
1586 | - prefix |
1587 | - exec_prefix |
1588 | |
1589 | If a field is already set (non NULL), it is left unchanged. */ |
1590 | PyStatus |
1591 | _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) |
1592 | { |
1593 | PyStatus status; |
1594 | PyCalculatePath calculate; |
1595 | memset(&calculate, 0, sizeof(calculate)); |
1596 | |
1597 | status = calculate_init(&calculate, config); |
1598 | if (_PyStatus_EXCEPTION(status)) { |
1599 | goto done; |
1600 | } |
1601 | |
1602 | status = calculate_path(&calculate, pathconfig); |
1603 | if (_PyStatus_EXCEPTION(status)) { |
1604 | goto done; |
1605 | } |
1606 | |
1607 | /* program_full_path must an either an empty string or an absolute path */ |
1608 | assert(wcslen(pathconfig->program_full_path) == 0 |
1609 | || _Py_isabs(pathconfig->program_full_path)); |
1610 | |
1611 | status = _PyStatus_OK(); |
1612 | |
1613 | done: |
1614 | calculate_free(&calculate); |
1615 | return status; |
1616 | } |
1617 | |
1618 | #ifdef __cplusplus |
1619 | } |
1620 | #endif |
1621 | |