blob: d7a86f26432e2d98c6e3b91ff7c4724e847b665b [file] [log] [blame]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001/*
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.
Ronald Oussoren6f6c5622009-12-24 14:03:19 +00006 *
7 * This program uses posix_spawn rather than plain execv because we need
8 * slightly more control over how the "real" interpreter is executed.
Thomas Wouters477c8d52006-05-27 19:21:47 +00009 */
10#include <unistd.h>
Ronald Oussoren6f6c5622009-12-24 14:03:19 +000011#include <spawn.h>
12#include <stdio.h>
13#include <string.h>
14#include <errno.h>
Thomas Wouters477c8d52006-05-27 19:21:47 +000015#include <err.h>
Ronald Oussoren6f6c5622009-12-24 14:03:19 +000016#include <dlfcn.h>
17#include <stdlib.h>
18#include <Python.h>
Thomas Wouters477c8d52006-05-27 19:21:47 +000019
Thomas Wouters477c8d52006-05-27 19:21:47 +000020
Ronald Oussoren6f6c5622009-12-24 14:03:19 +000021extern char** environ;
22
23/*
24 * Locate the python framework by looking for the
25 * library that contains Py_Initialize.
26 *
27 * In a regular framework the structure is:
28 *
29 * Python.framework/Versions/2.7
30 * /Python
31 * /Resources/Python.app/Contents/MacOS/Python
32 *
33 * In a virtualenv style structure the expected
34 * structure is:
35 *
36 * ROOT
37 * /bin/pythonw
38 * /.Python <- the dylib
39 * /.Resources/Python.app/Contents/MacOS/Python
40 *
41 * NOTE: virtualenv's are not an officially supported
42 * feature, support for that structure is provided as
43 * a convenience.
44 */
45static char* get_python_path(void)
46{
47 size_t len;
48 Dl_info info;
49 char* end;
50 char* g_path;
51
52 if (dladdr(Py_Initialize, &info) == 0) {
53 return NULL;
54 }
55
56 len = strlen(info.dli_fname);
57
58 g_path = malloc(len+60);
59 if (g_path == NULL) {
60 return NULL;
61 }
62
63 strcpy(g_path, info.dli_fname);
64 end = g_path + len - 1;
65 while (end != g_path && *end != '/') {
66 end --;
67 }
68 end++;
69 if (end[1] == '.') {
70 end++;
71 }
72 strcpy(end, "Resources/Python.app/Contents/MacOS/Python");
73
74 return g_path;
75}
76
77static void
78setup_spawnattr(posix_spawnattr_t* spawnattr)
79{
80 size_t ocount;
81 size_t count;
82 cpu_type_t cpu_types[1];
83 short flags = 0;
84#ifdef __LP64__
85 int ch;
86#endif
87
88 if ((errno = posix_spawnattr_init(spawnattr)) != 0) {
89 err(2, "posix_spawnattr_int");
90 /* NOTREACHTED */
91 }
92
93 count = 1;
94
95 /* Run the real python executable using the same architure as this
96 * executable, this allows users to controle the architecture using
97 * "arch -ppc python"
98 */
99
100#if defined(__ppc64__)
101 cpu_types[0] = CPU_TYPE_POWERPC64;
102
103#elif defined(__x86_64__)
104 cpu_types[0] = CPU_TYPE_X86_64;
105
106#elif defined(__ppc__)
107 cpu_types[0] = CPU_TYPE_POWERPC;
108#elif defined(__i386__)
109 cpu_types[0] = CPU_TYPE_X86;
110#else
111# error "Unknown CPU"
112#endif
113
114 if (posix_spawnattr_setbinpref_np(spawnattr, count,
115 cpu_types, &ocount) == -1) {
116 err(1, "posix_spawnattr_setbinpref");
117 /* NOTREACHTED */
118 }
119 if (count != ocount) {
120 fprintf(stderr, "posix_spawnattr_setbinpref failed to copy\n");
121 exit(1);
122 /* NOTREACHTED */
123 }
124
125
126 /*
127 * Set flag that causes posix_spawn to behave like execv
128 */
129 flags |= POSIX_SPAWN_SETEXEC;
130 if ((errno = posix_spawnattr_setflags(spawnattr, flags)) != 0) {
131 err(1, "posix_spawnattr_setflags");
132 /* NOTREACHTED */
133 }
134}
135
136int
137main(int argc, char **argv) {
138 posix_spawnattr_t spawnattr = NULL;
139 char* exec_path = get_python_path();
140
141
142 setup_spawnattr(&spawnattr);
143 posix_spawn(NULL, exec_path, NULL,
144 &spawnattr, argv, environ);
145 err(1, "posix_spawn: %s", argv[0]);
Thomas Wouters477c8d52006-05-27 19:21:47 +0000146 /* NOTREACHED */
147}