Home | History | Annotate | Download | only in Tools
      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 #pragma weak_import posix_spawnattr_init
     14 #pragma weak_import posix_spawnattr_setbinpref_np
     15 #pragma weak_import posix_spawnattr_setflags
     16 #pragma weak_import posix_spawn
     17 
     18 #include <Python.h>
     19 #include <unistd.h>
     20 #ifdef HAVE_SPAWN_H
     21 #include <spawn.h>
     22 #endif
     23 #include <stdio.h>
     24 #include <string.h>
     25 #include <errno.h>
     26 #include <err.h>
     27 #include <dlfcn.h>
     28 #include <stdlib.h>
     29 
     30 
     31 extern char** environ;
     32 
     33 /*
     34  * Locate the python framework by looking for the
     35  * library that contains Py_Initialize.
     36  *
     37  * In a regular framework the structure is:
     38  *
     39  *    Python.framework/Versions/2.7
     40  *              /Python
     41  *              /Resources/Python.app/Contents/MacOS/Python
     42  *
     43  * In a virtualenv style structure the expected
     44  * structure is:
     45  *
     46  *    ROOT
     47  *       /bin/pythonw
     48  *       /.Python   <- the dylib
     49  *       /.Resources/Python.app/Contents/MacOS/Python
     50  *
     51  * NOTE: virtualenv's are not an officially supported
     52  * feature, support for that structure is provided as
     53  * a convenience.
     54  */
     55 static char* get_python_path(void)
     56 {
     57     size_t len;
     58     Dl_info info;
     59     char* end;
     60     char* g_path;
     61 
     62     if (dladdr(Py_Initialize, &info) == 0) {
     63         return NULL;
     64     }
     65 
     66     len = strlen(info.dli_fname);
     67 
     68     g_path = malloc(len+60);
     69     if (g_path == NULL) {
     70         return NULL;
     71     }
     72 
     73     strcpy(g_path, info.dli_fname);
     74     end = g_path + len - 1;
     75     while (end != g_path && *end != '/') {
     76         end --;
     77     }
     78     end++;
     79     if (*end == '.') {
     80         end++;
     81     }
     82     strcpy(end, "Resources/Python.app/Contents/MacOS/" PYTHONFRAMEWORK);
     83 
     84     return g_path;
     85 }
     86 
     87 #ifdef HAVE_SPAWN_H
     88 static void
     89 setup_spawnattr(posix_spawnattr_t* spawnattr)
     90 {
     91     size_t ocount;
     92     size_t count;
     93     cpu_type_t cpu_types[1];
     94     short flags = 0;
     95 #ifdef __LP64__
     96     int   ch;
     97 #endif
     98 
     99     if ((errno = posix_spawnattr_init(spawnattr)) != 0) {
    100         err(2, "posix_spawnattr_int");
    101         /* NOTREACHTED */
    102     }
    103 
    104     count = 1;
    105 
    106     /* Run the real python executable using the same architecture as this
    107      * executable, this allows users to control the architecture using
    108      * "arch -ppc python"
    109      */
    110 
    111 #if defined(__ppc64__)
    112     cpu_types[0] = CPU_TYPE_POWERPC64;
    113 
    114 #elif defined(__x86_64__)
    115     cpu_types[0] = CPU_TYPE_X86_64;
    116 
    117 #elif defined(__ppc__)
    118     cpu_types[0] = CPU_TYPE_POWERPC;
    119 #elif defined(__i386__)
    120     cpu_types[0] = CPU_TYPE_X86;
    121 #else
    122 #       error "Unknown CPU"
    123 #endif
    124 
    125     if (posix_spawnattr_setbinpref_np(spawnattr, count,
    126                             cpu_types, &ocount) == -1) {
    127         err(1, "posix_spawnattr_setbinpref");
    128         /* NOTREACHTED */
    129     }
    130     if (count != ocount) {
    131         fprintf(stderr, "posix_spawnattr_setbinpref failed to copy\n");
    132         exit(1);
    133         /* NOTREACHTED */
    134     }
    135 
    136 
    137     /*
    138      * Set flag that causes posix_spawn to behave like execv
    139      */
    140     flags |= POSIX_SPAWN_SETEXEC;
    141     if ((errno = posix_spawnattr_setflags(spawnattr, flags)) != 0) {
    142         err(1, "posix_spawnattr_setflags");
    143         /* NOTREACHTED */
    144     }
    145 }
    146 #endif
    147 
    148 int
    149 main(int argc, char **argv) {
    150     char* exec_path = get_python_path();
    151 
    152     /*
    153      * Let argv[0] refer to the new interpreter. This is needed to
    154      * get the effect we want on OSX 10.5 or earlier. That is, without
    155      * changing argv[0] the real interpreter won't have access to
    156      * the Window Server.
    157      */
    158     argv[0] = exec_path;
    159 
    160 #ifdef HAVE_SPAWN_H
    161 
    162     /* We're weak-linking to posix-spawnv to ensure that
    163      * an executable build on 10.5 can work on 10.4.
    164      */
    165     if (posix_spawn != NULL) {
    166         posix_spawnattr_t spawnattr = NULL;
    167 
    168 
    169         setup_spawnattr(&spawnattr);
    170         posix_spawn(NULL, exec_path, NULL,
    171             &spawnattr, argv, environ);
    172         err(1, "posix_spawn: %s", exec_path);
    173     }
    174 #endif
    175     execve(exec_path, argv, environ);
    176     err(1, "execve: %s", argv[0]);
    177     /* NOTREACHED */
    178 }
    179