Skip to content

Commit 7eca0df

Browse files
committed
Add MacOS launcher
1 parent a2241c1 commit 7eca0df

File tree

3 files changed

+175
-1
lines changed

3 files changed

+175
-1
lines changed

graalpython/lib-python/3/venv/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def create_configuration(self, context):
252252
args = ' '.join(args)
253253
f.write(f'command = {sys.executable} -m venv {args}\n')
254254
# Truffle change: setup our a launcher by adding the path to the creating executable
255-
if os.name == 'nt':
255+
if (os.name == 'nt' or sys.platform == 'darwin'):
256256
f.write('venvlauncher_command = %s\n' % (__graalpython__.venvlauncher_command or sys.executable))
257257
# End of Truffle change
258258

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#include <ctype.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <unistd.h>
5+
#include <libgen.h>
6+
#include <string.h>
7+
#include <limits.h>
8+
#include <mach-o/dyld.h>
9+
10+
#define MAX_LINE 4096
11+
#define MAX_ARGS 100
12+
13+
#define GRAAL_PYTHON_EXE_ARG "--python.Executable="
14+
#define GRAAL_PYTHON_BASE_EXE_ARG "--python.VenvlauncherCommand="
15+
16+
/**
17+
* Reads the 'venvlauncher_command' from a pyvenv.cfg file.
18+
* Returns a newly allocated string. Caller must free() it.
19+
*/
20+
char *get_pyenvcfg_command(const char *pyenv_cfg_path) {
21+
const FILE *fp = fopen(pyenv_cfg_path, "r");
22+
if (fp == NULL) {
23+
fprintf(stderr, "Failed to open pyenv.cfg file!\n");
24+
exit(1);
25+
}
26+
27+
char current_line[MAX_LINE];
28+
const char *key = "venvlauncher_command";
29+
size_t key_len = strlen(key);
30+
31+
while (fgets(current_line, sizeof(current_line), fp)) {
32+
char *p = current_line;
33+
34+
while (isspace((unsigned char) *p)) p++;
35+
36+
if (strncmp(p, key, key_len) == 0) {
37+
p += key_len;
38+
39+
// Skip spaces and '='
40+
while (isspace((unsigned char) *p)) p++;
41+
if (*p == '=') p++;
42+
while (isspace((unsigned char) *p)) p++;
43+
if (*p == '\"') {
44+
char *end = p + strlen(p);
45+
while (isspace((unsigned char) end[-1]) || end[-1] == '\n') {
46+
*--end = '\0';
47+
}
48+
if (end[-1] != '\"') {
49+
fprintf(stderr, "venv command is not in correct format!");
50+
exit(1);
51+
}
52+
p++;
53+
end[-1] = '\0';
54+
}
55+
56+
fclose(fp);
57+
return strdup(p);
58+
}
59+
}
60+
}
61+
62+
void split_venv_command_into_args(char *venv_command, char *result[]) {
63+
char *current_token = strtok(venv_command, " ");
64+
for (int i = 0; i < 5; i++) {
65+
result[i] = current_token;
66+
current_token = strtok(NULL, " ");
67+
}
68+
if (current_token != NULL) {
69+
fprintf(stderr, "Unexpected venv_command size!\n");
70+
exit(1);
71+
}
72+
}
73+
74+
void find_pyvenv(char *pyvenv_cfg_path, size_t path_size) {
75+
char executable_path[PATH_MAX];
76+
uint32_t executable_path_size = sizeof(executable_path);
77+
78+
// Get current executable path (macOS)
79+
if (_NSGetExecutablePath(executable_path, &executable_path_size) != 0) {
80+
fprintf(stderr, "Failed to get executable path\n");
81+
exit(1);
82+
}
83+
84+
// First try search for the pyvenv on top level
85+
char *dir_path = dirname(executable_path);
86+
snprintf(pyvenv_cfg_path, path_size, "%s/pyvenv.cfg", dir_path);
87+
if (access(pyvenv_cfg_path, F_OK) != 0) {
88+
// Try searching one level up
89+
dir_path = dirname(dir_path);
90+
snprintf(pyvenv_cfg_path, path_size, "%s/pyvenv.cfg", dir_path);
91+
if (access(pyvenv_cfg_path, F_OK) != 0) {
92+
fprintf(stderr, "Error: pyvenv.cfg file not found at %s\n", pyvenv_cfg_path);
93+
exit(1);
94+
}
95+
}
96+
}
97+
98+
int main(int argc, char *argv[]) {
99+
char pyvenv_cfg_path[PATH_MAX];
100+
101+
find_pyvenv(pyvenv_cfg_path, sizeof(pyvenv_cfg_path));
102+
103+
char *venv_command = get_pyenvcfg_command(pyvenv_cfg_path);
104+
char *venv_command_dup = strdup(venv_command);
105+
106+
char *venv_args[5];
107+
split_venv_command_into_args(venv_command_dup, venv_args);
108+
109+
// Adds "--python.VenvlauncherCommand="
110+
size_t python_base_exec_size = strlen(venv_command) + strlen(GRAAL_PYTHON_BASE_EXE_ARG) + 2 + 1;
111+
char python_base_exec_command[python_base_exec_size];
112+
snprintf(python_base_exec_command, python_base_exec_size, "%s\"%s\"", GRAAL_PYTHON_BASE_EXE_ARG, venv_command);
113+
114+
// Adds "--python.Executable="
115+
size_t python_exec_arg_size = strlen(GRAAL_PYTHON_EXE_ARG) + strlen(argv[0]) + 1;
116+
char python_exec_command[python_exec_arg_size];
117+
snprintf(python_exec_command,
118+
python_exec_arg_size,
119+
"%s%s",
120+
GRAAL_PYTHON_EXE_ARG,
121+
argv[0]);
122+
123+
// venv_args (5) + "--python.VenvlauncherCommand=" + Adds "--python.Executable=" + rest of argc + NULL
124+
size_t args_size = 5 + 2 + (argc - 1) + 1;
125+
char *args[args_size];
126+
127+
// copy venv_args
128+
int k = 0;
129+
for (k = 0; k < 5; k++) {
130+
args[k] = venv_args[k];
131+
}
132+
133+
args[k++] = python_base_exec_command;
134+
args[k++] = python_exec_command;
135+
136+
// copy rest of the args
137+
for (int i = 1; i < argc; i++) {
138+
args[k++] = argv[i];
139+
}
140+
141+
args[k] = NULL;
142+
143+
execv(args[0], args);
144+
perror("execv failed"); // only runs if execv fails
145+
return 1;
146+
}

mx.graalpython/suite.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,24 @@
520520
},
521521
},
522522

523+
"python-macos-launcher": {
524+
"subDir": "graalpython",
525+
"native": "executable",
526+
"deliverable": "macos-venvlauncher",
527+
"os_arch": {
528+
"darwin": {
529+
"<others>": {
530+
"defaultBuild": True,
531+
},
532+
},
533+
"<others>": {
534+
"<others>": {
535+
"defaultBuild": False,
536+
},
537+
},
538+
},
539+
},
540+
523541
"python-libbz2": {
524542
"subDir": "graalpython",
525543
"class": "CMakeNinjaProject",
@@ -1207,6 +1225,16 @@
12071225
},
12081226
},
12091227
},
1228+
"darwin": {
1229+
"<others>": {
1230+
"layout": {
1231+
"./META-INF/resources/<os>/<arch>/lib/graalpy<graal_ver:major_minor>/": [
1232+
"dependency:GRAALPYTHON_NATIVE_LIBS/<os>/<arch>/*",
1233+
],
1234+
"./META-INF/resources/<os>/<arch>/lib/venv/scripts/macos/graalpy": "dependency:python-macos-launcher",
1235+
}
1236+
}
1237+
},
12101238
"<others>": {
12111239
"<others>": {
12121240
"layout": {

0 commit comments

Comments
 (0)