You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
4.2 KiB

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