Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Mac/Tools/pythonw.c
12 views
1
/*
2
* This wrapper program executes a python executable hidden inside an
3
* application bundle inside the Python framework. This is needed to run
4
* GUI code: some GUI API's don't work unless the program is inside an
5
* application bundle.
6
*
7
* This program uses posix_spawn rather than plain execv because we need
8
* slightly more control over how the "real" interpreter is executed.
9
*
10
* On OSX 10.4 (and earlier) this falls back to using exec because the
11
* posix_spawnv functions aren't available there.
12
*/
13
14
#pragma weak_import posix_spawnattr_init
15
#pragma weak_import posix_spawnattr_setbinpref_np
16
#pragma weak_import posix_spawnattr_setflags
17
#pragma weak_import posix_spawn
18
19
#include <Python.h>
20
#include <unistd.h>
21
#ifdef HAVE_SPAWN_H
22
#include <spawn.h>
23
#endif
24
#include <stdio.h>
25
#include <string.h>
26
#include <errno.h>
27
#include <err.h>
28
#include <dlfcn.h>
29
#include <stdlib.h>
30
#include <mach-o/dyld.h>
31
32
33
extern char** environ;
34
35
/*
36
* Locate the python framework by looking for the
37
* library that contains Py_Initialize.
38
*
39
* In a regular framework the structure is:
40
*
41
* Python.framework/Versions/2.7
42
* /Python
43
* /Resources/Python.app/Contents/MacOS/Python
44
*
45
* In a virtualenv style structure the expected
46
* structure is:
47
*
48
* ROOT
49
* /bin/pythonw
50
* /.Python <- the dylib
51
* /.Resources/Python.app/Contents/MacOS/Python
52
*
53
* NOTE: virtualenv's are not an officially supported
54
* feature, support for that structure is provided as
55
* a convenience.
56
*/
57
static char* get_python_path(void)
58
{
59
size_t len;
60
Dl_info info;
61
char* end;
62
char* g_path;
63
64
if (dladdr(Py_Initialize, &info) == 0) {
65
return NULL;
66
}
67
68
len = strlen(info.dli_fname);
69
70
g_path = malloc(len+60);
71
if (g_path == NULL) {
72
return NULL;
73
}
74
75
strcpy(g_path, info.dli_fname);
76
end = g_path + len - 1;
77
while (end != g_path && *end != '/') {
78
end --;
79
}
80
end++;
81
if (*end == '.') {
82
end++;
83
}
84
strcpy(end, "Resources/Python.app/Contents/MacOS/" PYTHONFRAMEWORK);
85
86
return g_path;
87
}
88
89
#ifdef HAVE_SPAWN_H
90
static void
91
setup_spawnattr(posix_spawnattr_t* spawnattr)
92
{
93
size_t ocount;
94
size_t count;
95
cpu_type_t cpu_types[1];
96
short flags = 0;
97
98
if ((errno = posix_spawnattr_init(spawnattr)) != 0) {
99
err(2, "posix_spawnattr_int");
100
/* NOTREACHTED */
101
}
102
103
count = 1;
104
105
/* Run the real python executable using the same architecture as this
106
* executable, this allows users to control the architecture using
107
* "arch -ppc python"
108
*/
109
110
#if defined(__ppc64__)
111
cpu_types[0] = CPU_TYPE_POWERPC64;
112
113
#elif defined(__x86_64__)
114
cpu_types[0] = CPU_TYPE_X86_64;
115
116
#elif defined(__ppc__)
117
cpu_types[0] = CPU_TYPE_POWERPC;
118
119
#elif defined(__i386__)
120
cpu_types[0] = CPU_TYPE_X86;
121
122
#elif defined(__arm64__)
123
cpu_types[0] = CPU_TYPE_ARM64;
124
125
#else
126
# error "Unknown CPU"
127
128
#endif
129
130
if (posix_spawnattr_setbinpref_np(spawnattr, count,
131
cpu_types, &ocount) == -1) {
132
err(1, "posix_spawnattr_setbinpref");
133
/* NOTREACHTED */
134
}
135
if (count != ocount) {
136
fprintf(stderr, "posix_spawnattr_setbinpref failed to copy\n");
137
exit(1);
138
/* NOTREACHTED */
139
}
140
141
142
/*
143
* Set flag that causes posix_spawn to behave like execv
144
*/
145
flags |= POSIX_SPAWN_SETEXEC;
146
if ((errno = posix_spawnattr_setflags(spawnattr, flags)) != 0) {
147
err(1, "posix_spawnattr_setflags");
148
/* NOTREACHTED */
149
}
150
}
151
#endif
152
153
int
154
main(int argc, char **argv) {
155
char* exec_path = get_python_path();
156
static char path[PATH_MAX * 2];
157
static char real_path[PATH_MAX * 2];
158
int status;
159
uint32_t size = PATH_MAX * 2;
160
161
/* Set the original executable path in the environment. */
162
status = _NSGetExecutablePath(path, &size);
163
if (status == 0) {
164
/*
165
* Note: don't call 'realpath', that will
166
* erase symlink information, and that
167
* breaks "pyvenv --symlink"
168
*
169
* It is nice to have the directory name
170
* as a cleaned up absolute path though,
171
* therefore call realpath on dirname(path)
172
*/
173
char* slash = strrchr(path, '/');
174
if (slash) {
175
char replaced;
176
replaced = slash[1];
177
slash[1] = 0;
178
if (realpath(path, real_path) == NULL) {
179
err(1, "realpath: %s", path);
180
}
181
slash[1] = replaced;
182
if (strlcat(real_path, slash, sizeof(real_path)) > sizeof(real_path)) {
183
errno = EINVAL;
184
err(1, "realpath: %s", path);
185
}
186
187
} else {
188
if (realpath(".", real_path) == NULL) {
189
err(1, "realpath: %s", path);
190
}
191
if (strlcat(real_path, "/", sizeof(real_path)) > sizeof(real_path)) {
192
errno = EINVAL;
193
err(1, "realpath: %s", path);
194
}
195
if (strlcat(real_path, path, sizeof(real_path)) > sizeof(real_path)) {
196
errno = EINVAL;
197
err(1, "realpath: %s", path);
198
}
199
}
200
201
/*
202
* The environment variable is used to pass the value of real_path
203
* to the actual python interpreter, and is read by code in
204
* Python/coreconfig.c.
205
*
206
* This way the real interpreter knows how the user invoked the
207
* interpreter and can behave as if this launcher is the real
208
* interpreter (looking for pyvenv configuration, ...)
209
*/
210
setenv("__PYVENV_LAUNCHER__", real_path, 1);
211
}
212
213
/*
214
* Let argv[0] refer to the new interpreter. This is needed to
215
* get the effect we want on OSX 10.5 or earlier. That is, without
216
* changing argv[0] the real interpreter won't have access to
217
* the Window Server.
218
*/
219
argv[0] = exec_path;
220
221
#ifdef HAVE_SPAWN_H
222
/* We're weak-linking to posix-spawnv to ensure that
223
* an executable build on 10.5 can work on 10.4.
224
*/
225
226
if (&posix_spawn != NULL) {
227
posix_spawnattr_t spawnattr = NULL;
228
229
setup_spawnattr(&spawnattr);
230
posix_spawn(NULL, exec_path, NULL,
231
&spawnattr, argv, environ);
232
err(1, "posix_spawn: %s", exec_path);
233
}
234
#endif
235
execve(exec_path, argv, environ);
236
err(1, "execve: %s", argv[0]);
237
/* NOTREACHED */
238
}
239
240