Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c985d08

Browse files
committed
Closes #18491: Added script-wrapper functionality to launcher source (but not to executable).
1 parent 7c8cd25 commit c985d08

1 file changed

Lines changed: 138 additions & 34 deletions

File tree

PC/launcher.c

Lines changed: 138 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,27 @@
2020
#define SKIP_PREFIX
2121
#define SEARCH_PATH
2222

23+
/* Error codes */
24+
25+
#define RC_NO_STD_HANDLES 100
26+
#define RC_CREATE_PROCESS 101
27+
#define RC_BAD_VIRTUAL_PATH 102
28+
#define RC_NO_PYTHON 103
29+
#define RC_NO_MEMORY 104
30+
/*
31+
* SCRIPT_WRAPPER is used to choose between two variants of an executable built
32+
* from this source file. If not defined, the PEP 397 Python launcher is built;
33+
* if defined, a script launcher of the type used by setuptools is built, which
34+
* looks for a script name related to the executable name and runs that script
35+
* with the appropriate Python interpreter.
36+
*
37+
* SCRIPT_WRAPPER should be undefined in the source, and defined in a VS project
38+
* which builds the setuptools-style launcher.
39+
*/
40+
#if defined(SCRIPT_WRAPPER)
41+
#define RC_NO_SCRIPT 105
42+
#endif
43+
2344
/* Just for now - static definition */
2445

2546
static FILE * log_fp = NULL;
@@ -32,32 +53,6 @@ skip_whitespace(wchar_t * p)
3253
return p;
3354
}
3455

35-
/*
36-
* This function is here to simplify memory management
37-
* and to treat blank values as if they are absent.
38-
*/
39-
static wchar_t * get_env(wchar_t * key)
40-
{
41-
/* This is not thread-safe, just like getenv */
42-
static wchar_t buf[256];
43-
DWORD result = GetEnvironmentVariableW(key, buf, 256);
44-
45-
if (result > 255) {
46-
/* Large environment variable. Accept some leakage */
47-
wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1));
48-
GetEnvironmentVariableW(key, buf2, result);
49-
return buf2;
50-
}
51-
52-
if (result == 0)
53-
/* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND,
54-
or an empty environment variable. */
55-
return NULL;
56-
57-
return buf;
58-
}
59-
60-
6156
static void
6257
debug(wchar_t * format, ...)
6358
{
@@ -100,11 +95,40 @@ error(int rc, wchar_t * format, ... )
10095
#if !defined(_WINDOWS)
10196
fwprintf(stderr, L"%s\n", message);
10297
#else
103-
MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), MB_OK);
98+
MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."),
99+
MB_OK);
104100
#endif
105101
ExitProcess(rc);
106102
}
107103

104+
/*
105+
* This function is here to simplify memory management
106+
* and to treat blank values as if they are absent.
107+
*/
108+
static wchar_t * get_env(wchar_t * key)
109+
{
110+
/* This is not thread-safe, just like getenv */
111+
static wchar_t buf[BUFSIZE];
112+
DWORD result = GetEnvironmentVariableW(key, buf, BUFSIZE);
113+
114+
if (result >= BUFSIZE) {
115+
/* Large environment variable. Accept some leakage */
116+
wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1));
117+
if (buf2 = NULL) {
118+
error(RC_NO_MEMORY, L"Could not allocate environment buffer");
119+
}
120+
GetEnvironmentVariableW(key, buf2, result);
121+
return buf2;
122+
}
123+
124+
if (result == 0)
125+
/* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND,
126+
or an empty environment variable. */
127+
return NULL;
128+
129+
return buf;
130+
}
131+
108132
#if defined(_WINDOWS)
109133

110134
#define PYTHON_EXECUTABLE L"pythonw.exe"
@@ -115,11 +139,6 @@ error(int rc, wchar_t * format, ... )
115139

116140
#endif
117141

118-
#define RC_NO_STD_HANDLES 100
119-
#define RC_CREATE_PROCESS 101
120-
#define RC_BAD_VIRTUAL_PATH 102
121-
#define RC_NO_PYTHON 103
122-
123142
#define MAX_VERSION_SIZE 4
124143

125144
typedef struct {
@@ -457,6 +476,51 @@ locate_python(wchar_t * wanted_ver)
457476
return result;
458477
}
459478

479+
#if defined(SCRIPT_WRAPPER)
480+
/*
481+
* Check for a script located alongside the executable
482+
*/
483+
484+
#if defined(_WINDOWS)
485+
#define SCRIPT_SUFFIX L"-script.pyw"
486+
#else
487+
#define SCRIPT_SUFFIX L"-script.py"
488+
#endif
489+
490+
static wchar_t wrapped_script_path[MAX_PATH];
491+
492+
/* Locate the script being wrapped.
493+
*
494+
* This code should store the name of the wrapped script in
495+
* wrapped_script_path, or terminate the program with an error if there is no
496+
* valid wrapped script file.
497+
*/
498+
static void
499+
locate_wrapped_script()
500+
{
501+
wchar_t * p;
502+
size_t plen;
503+
DWORD attrs;
504+
505+
plen = GetModuleFileNameW(NULL, wrapped_script_path, MAX_PATH);
506+
p = wcsrchr(wrapped_script_path, L'.');
507+
if (p == NULL) {
508+
debug(L"GetModuleFileNameW returned value has no extension: %s\n",
509+
wrapped_script_path);
510+
error(RC_NO_SCRIPT, L"Wrapper name '%s' is not valid.", wrapped_script_path);
511+
}
512+
513+
wcsncpy_s(p, MAX_PATH - (p - wrapped_script_path) + 1, SCRIPT_SUFFIX, _TRUNCATE);
514+
attrs = GetFileAttributesW(wrapped_script_path);
515+
if (attrs == INVALID_FILE_ATTRIBUTES) {
516+
debug(L"File '%s' non-existent\n", wrapped_script_path);
517+
error(RC_NO_SCRIPT, L"Script file '%s' is not present.", wrapped_script_path);
518+
}
519+
520+
debug(L"Using wrapped script file '%s'\n", wrapped_script_path);
521+
}
522+
#endif
523+
460524
/*
461525
* Process creation code
462526
*/
@@ -863,7 +927,7 @@ typedef struct {
863927
} BOM;
864928

865929
/*
866-
* Strictly, we don't need to handle UTF-16 anf UTF-32, since Python itself
930+
* Strictly, we don't need to handle UTF-16 and UTF-32, since Python itself
867931
* doesn't. Never mind, one day it might - there's no harm leaving it in.
868932
*/
869933
static BOM BOMs[] = {
@@ -1250,6 +1314,11 @@ process(int argc, wchar_t ** argv)
12501314
VS_FIXEDFILEINFO * file_info;
12511315
UINT block_size;
12521316
int index;
1317+
#if defined(SCRIPT_WRAPPER)
1318+
int newlen;
1319+
wchar_t * newcommand;
1320+
wchar_t * av[2];
1321+
#endif
12531322

12541323
wp = get_env(L"PYLAUNCH_DEBUG");
12551324
if ((wp != NULL) && (*wp != L'\0'))
@@ -1329,7 +1398,40 @@ process(int argc, wchar_t ** argv)
13291398
}
13301399

13311400
command = skip_me(GetCommandLineW());
1332-
debug(L"Called with command line: %s", command);
1401+
debug(L"Called with command line: %s\n", command);
1402+
1403+
#if defined(SCRIPT_WRAPPER)
1404+
/* The launcher is being used in "script wrapper" mode.
1405+
* There should therefore be a Python script named <exename>-script.py in
1406+
* the same directory as the launcher executable.
1407+
* Put the script name into argv as the first (script name) argument.
1408+
*/
1409+
1410+
/* Get the wrapped script name - if the script is not present, this will
1411+
* terminate the program with an error.
1412+
*/
1413+
locate_wrapped_script();
1414+
1415+
/* Add the wrapped script to the start of command */
1416+
newlen = wcslen(wrapped_script_path) + wcslen(command) + 2; /* ' ' + NUL */
1417+
newcommand = malloc(sizeof(wchar_t) * newlen);
1418+
if (!newcommand) {
1419+
error(RC_NO_MEMORY, L"Could not allocate new command line");
1420+
}
1421+
else {
1422+
wcscpy_s(newcommand, newlen, wrapped_script_path);
1423+
wcscat_s(newcommand, newlen, L" ");
1424+
wcscat_s(newcommand, newlen, command);
1425+
debug(L"Running wrapped script with command line '%s'\n", newcommand);
1426+
read_commands();
1427+
av[0] = wrapped_script_path;
1428+
av[1] = NULL;
1429+
maybe_handle_shebang(av, newcommand);
1430+
/* Returns if no shebang line - pass to default processing */
1431+
command = newcommand;
1432+
valid = FALSE;
1433+
}
1434+
#else
13331435
if (argc <= 1) {
13341436
valid = FALSE;
13351437
p = NULL;
@@ -1357,6 +1459,8 @@ installed", &p[1]);
13571459
}
13581460
}
13591461
}
1462+
#endif
1463+
13601464
if (!valid) {
13611465
ip = locate_python(L"");
13621466
if (ip == NULL)

0 commit comments

Comments
 (0)