From 673ac23e93f64a287c16a0d0ea45ba9ab9379d2d Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 17 Sep 2005 01:13:02 +0000 Subject: Added support to solve the infamous "we want .py on Windows, no extension elsewhere" problem, while also bypassing the need for PATHEXT on Windows, and in fact the need to even write script files at all, for any platform. Instead, you define "entry points" in your setup script, in this case the names of the scripts you want (without extensions) and the functions that should be imported and run to implement the scripts. Setuptools will then generate platform-appropriate script files at install time, including an .exe wrapper when installing on Windows. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041246 --- launcher.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 launcher.c (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c new file mode 100755 index 00000000..5896805f --- /dev/null +++ b/launcher.c @@ -0,0 +1,123 @@ +/* + Setuptools Script Launcher for Windows + + This is a stub executable for Windows that functions somewhat like + Effbot's "exemaker", in that it runs a script with the same name but + a .py extension, using information from a #! line. It differs in that + it spawns the actual Python executable, rather than attempting to + hook into the Python DLL. This means that the script will run with + sys.executable set to the Python executable, where exemaker ends up with + sys.executable pointing to itself. (Which means it won't work if you try + to run another Python process using sys.executable.) + + To build/rebuild with mingw32, do this in the setuptools project directory: + + gcc -mno-cygwin -O -s -o setuptools/launcher.exe launcher.c + + It links to msvcrt.dll, but this shouldn't be a problem since it doesn't + actually run Python in the same process. Note that using 'exec' instead + of 'spawn' doesn't work, because on Windows this leads to the Python + executable running in the *background*, attached to the same console + window, meaning you get a command prompt back *before* Python even finishes + starting. So, we have to use spawnv() and wait for Python to exit before + continuing. :( +*/ + +#include +#include +#include +#include +#include "windows.h" + +int fail(char *format, char *data) { + /* Print error message to stderr and return 1 */ + fprintf(stderr, format, data); + return 1; +} + + + + + +int main(int argc, char **argv) { + + char python[256]; /* python executable's filename*/ + char script[256]; /* the script's filename */ + + HINSTANCE hPython; /* DLL handle for python executable */ + int scriptf; /* file descriptor for script file */ + + char **newargs; /* argument array for exec */ + char *ptr, *end; /* working pointers for string manipulation */ + + /* compute script name from our .exe name*/ + GetModuleFileName(NULL, script, sizeof(script)); + end = script + strlen(script); + while( end>script && *end != '.') + *end-- = '\0'; + strcat(script, "py"); + + /* figure out the target python executable */ + + scriptf = open(script, O_RDONLY); + if (scriptf == -1) { + return fail("Cannot open %s\n", script); + } + end = python + read(scriptf, python, sizeof(python)); + close(scriptf); + + ptr = python-1; + while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') { + if (*ptr=='/') + *ptr='\\'; /* convert slashes to avoid LoadLibrary crashes... */ + } + + *ptr = '\0'; + while (ptr>python && isspace(*ptr)) *ptr-- = '\0'; /* strip trailing sp */ + + if (strncmp(python, "#!", 2)) { + /* default to python.exe if no #! header */ + strcpy(python, "#!python.exe"); + } + + /* At this point, the python buffer contains "#!pythonfilename" */ + + /* Using spawnv() can fail strangely if you e.g. find the Cygwin + Python, so we'll make sure Windows can find and load it */ + hPython = LoadLibraryEx(python+2, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hPython) { + return fail("Cannot find Python executable %s\n", python+2); + } + + /* And we'll use the absolute filename for spawnv */ + GetModuleFileName(hPython, python, sizeof(python)); + + /* printf("Python executable: %s\n", python); */ + + /* Argument array needs to be argc+1 for args, plus 1 for null sentinel */ + newargs = (char **)calloc(argc+2, sizeof(char *)); + newargs[0] = python; + newargs[1] = script; + memcpy(newargs+2, argv+1, (argc-1)*sizeof(char *)); + newargs[argc+1] = NULL; + + /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */ + + return spawnv(P_WAIT, newargs[0], (const char * const *)(newargs)); +} + + + + + + + + + + + + + + + + -- cgit v1.2.1 From ad0eebab218118fa07e314032bd4097c44fc7eb2 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 24 Sep 2005 20:29:57 +0000 Subject: Support generating .pyw/.exe wrappers for Windows GUI scripts, and "normal" #! wrappers for GUI scripts on other platforms. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041254 --- launcher.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index 5896805f..69102c13 100755 --- a/launcher.c +++ b/launcher.c @@ -12,7 +12,8 @@ To build/rebuild with mingw32, do this in the setuptools project directory: - gcc -mno-cygwin -O -s -o setuptools/launcher.exe launcher.c + gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c + gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c It links to msvcrt.dll, but this shouldn't be a problem since it doesn't actually run Python in the same process. Note that using 'exec' instead @@ -32,14 +33,13 @@ int fail(char *format, char *data) { /* Print error message to stderr and return 1 */ fprintf(stderr, format, data); - return 1; + return 2; } - -int main(int argc, char **argv) { +int run(int argc, char **argv, int is_gui) { char python[256]; /* python executable's filename*/ char script[256]; /* the script's filename */ @@ -55,7 +55,7 @@ int main(int argc, char **argv) { end = script + strlen(script); while( end>script && *end != '.') *end-- = '\0'; - strcat(script, "py"); + strcat(script, (GUI ? "pyw" : "py")); /* figure out the target python executable */ @@ -102,19 +102,19 @@ int main(int argc, char **argv) { newargs[argc+1] = NULL; /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */ - + if (is_gui) { + /* Use exec, we don't need to wait for the GUI to finish */ + execv(newargs[0], (const char * const *)(newargs)); + return fail("Could not exec %s", python); /* shouldn't get here! */ + } + /* We *do* need to wait for a CLI to finish, so use spawn */ return spawnv(P_WAIT, newargs[0], (const char * const *)(newargs)); } - - - - - - - - +int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) { + return run(__argc, __argv, GUI); +} -- cgit v1.2.1 From c23b0fb2bfbd8df35ebee9551458ed00e0f2095c Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 16 Oct 2005 20:45:30 +0000 Subject: Fix problem with Windows console scripts conflicting with module names, thereby confusing the import process. Scripts are now generated with a suffix of the form '-script.py' to avoid conflicts. (The .exe's are still generated without the '-script' part, so you don't have to type it.) Thanks to Matthew R. Scott for reporting the problem. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041261 --- launcher.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index 69102c13..b5405911 100755 --- a/launcher.c +++ b/launcher.c @@ -12,8 +12,8 @@ To build/rebuild with mingw32, do this in the setuptools project directory: - gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c - gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c + gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c + gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c It links to msvcrt.dll, but this shouldn't be a problem since it doesn't actually run Python in the same process. Note that using 'exec' instead @@ -55,7 +55,8 @@ int run(int argc, char **argv, int is_gui) { end = script + strlen(script); while( end>script && *end != '.') *end-- = '\0'; - strcat(script, (GUI ? "pyw" : "py")); + *end-- = '\0'; + strcat(script, (GUI ? "-script.pyw" : "-script.py")); /* figure out the target python executable */ @@ -74,7 +75,6 @@ int run(int argc, char **argv, int is_gui) { *ptr = '\0'; while (ptr>python && isspace(*ptr)) *ptr-- = '\0'; /* strip trailing sp */ - if (strncmp(python, "#!", 2)) { /* default to python.exe if no #! header */ strcpy(python, "#!python.exe"); -- cgit v1.2.1 From fd21ab7f9ab4b462d6ffb81b77b699c5eef6d28c Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 17 Nov 2005 03:01:33 +0000 Subject: Quote arguments to python.exe (including python's path) to avoid problems when Python (or a script) is installed in a directory whose name contains spaces. :( --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041460 --- launcher.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index b5405911..1bb1097f 100755 --- a/launcher.c +++ b/launcher.c @@ -1,5 +1,4 @@ -/* - Setuptools Script Launcher for Windows +/* Setuptools Script Launcher for Windows This is a stub executable for Windows that functions somewhat like Effbot's "exemaker", in that it runs a script with the same name but @@ -23,7 +22,6 @@ starting. So, we have to use spawnv() and wait for Python to exit before continuing. :( */ - #include #include #include @@ -35,9 +33,11 @@ int fail(char *format, char *data) { fprintf(stderr, format, data); return 2; } - - - +char *quoted(char *data) { + char *result = calloc(strlen(data)+3,sizeof(char)); + strcat(result,"\""); strcat(result,data); strcat(result,"\""); + return result; +} int run(int argc, char **argv, int is_gui) { @@ -96,19 +96,19 @@ int run(int argc, char **argv, int is_gui) { /* Argument array needs to be argc+1 for args, plus 1 for null sentinel */ newargs = (char **)calloc(argc+2, sizeof(char *)); - newargs[0] = python; - newargs[1] = script; + newargs[0] = quoted(python); + newargs[1] = quoted(script); memcpy(newargs+2, argv+1, (argc-1)*sizeof(char *)); newargs[argc+1] = NULL; /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */ if (is_gui) { /* Use exec, we don't need to wait for the GUI to finish */ - execv(newargs[0], (const char * const *)(newargs)); + execv(python, (const char * const *)(newargs)); return fail("Could not exec %s", python); /* shouldn't get here! */ } /* We *do* need to wait for a CLI to finish, so use spawn */ - return spawnv(P_WAIT, newargs[0], (const char * const *)(newargs)); + return spawnv(P_WAIT, python, (const char * const *)(newargs)); } -- cgit v1.2.1 From cd84d4b5eb8113b416f6cb080b3445799ed2ff99 Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Tue, 22 Aug 2006 19:58:05 +0000 Subject: Added quoting of script arguments and extended the quoting logic to handle embedded quotes. Added support for passing a single argument on the shebang line to pass things like -O and -i. Fixed bug in handling trailing whitespace in Python command. --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4051487 --- launcher.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 10 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index 1bb1097f..79dbbbd4 100755 --- a/launcher.c +++ b/launcher.c @@ -33,22 +33,80 @@ int fail(char *format, char *data) { fprintf(stderr, format, data); return 2; } + char *quoted(char *data) { - char *result = calloc(strlen(data)+3,sizeof(char)); - strcat(result,"\""); strcat(result,data); strcat(result,"\""); + int i, l = strlen(data), nb; + /* We allocate twice as much space as needed to deal with worse-case + of having to escape everything. */ + char *result = calloc(l*2+3, sizeof(char)); + char *presult = result; + + *presult++ = '"'; + for (nb=0, i=0; i < l; i++) + { + if (data[i] == '\\') + nb += 1; + else if (data[i] == '"') + { + for (; nb > 0; nb--) + *presult++ = '\\'; + *presult++ = '\\'; + } + else + nb = 0; + *presult++ = data[i]; + } + for (; nb > 0; nb--) /* Deal w trailing slashes */ + *presult++ = '\\'; + + *presult++ = '"'; + *presult++ = 0; return result; } +char *getpyopt(char *python) +{ + /* Search a Python command string, read from a #! line for an + option. An option must be separated from an executable name by + one or more spaces. An option consistes of a hyphen followed by + one or more letters. + */ + static char *letters = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ; + char *p = python + strlen(python) - 1; + if (strchr(letters, *p) == NULL) + return NULL; /* Path doen't end with a letter. Odd. */ + while (p > python && strchr(letters, *p) != NULL) + p--; + if (p == python || *p != '-') + return NULL; /* Can't be an option */ + p--; + if (p > python && isspace(*p)) + { /* BINGO, we have an option */ + char *pyopt = p+1; + /* strip trailing spaces from remainder of python command */ + while (p > python && isspace(*p)) + *p-- = '\0'; + return pyopt; + } + else + return NULL; +} + int run(int argc, char **argv, int is_gui) { char python[256]; /* python executable's filename*/ + char *pyopt; /* Python option */ char script[256]; /* the script's filename */ HINSTANCE hPython; /* DLL handle for python executable */ int scriptf; /* file descriptor for script file */ - char **newargs; /* argument array for exec */ + char **newargs, **newargsp; /* argument array for exec */ char *ptr, *end; /* working pointers for string manipulation */ + int i; /* loop counter */ /* compute script name from our .exe name*/ GetModuleFileName(NULL, script, sizeof(script)); @@ -73,13 +131,16 @@ int run(int argc, char **argv, int is_gui) { *ptr='\\'; /* convert slashes to avoid LoadLibrary crashes... */ } - *ptr = '\0'; + *ptr-- = '\0'; while (ptr>python && isspace(*ptr)) *ptr-- = '\0'; /* strip trailing sp */ if (strncmp(python, "#!", 2)) { /* default to python.exe if no #! header */ strcpy(python, "#!python.exe"); } + /* Check for Python options */ + pyopt = getpyopt(python); + /* At this point, the python buffer contains "#!pythonfilename" */ /* Using spawnv() can fail strangely if you e.g. find the Cygwin @@ -94,12 +155,19 @@ int run(int argc, char **argv, int is_gui) { /* printf("Python executable: %s\n", python); */ - /* Argument array needs to be argc+1 for args, plus 1 for null sentinel */ - newargs = (char **)calloc(argc+2, sizeof(char *)); - newargs[0] = quoted(python); - newargs[1] = quoted(script); - memcpy(newargs+2, argv+1, (argc-1)*sizeof(char *)); - newargs[argc+1] = NULL; + /* Argument array needs to be + argc+1 for python executable, + plus 1 for possible python opts, + plus 1 for null sentinel */ + newargs = (char **)calloc(argc+3, sizeof(char *)); + newargsp = newargs; + *newargsp++ = quoted(python); + if (pyopt) + *newargsp++ = pyopt; + *newargsp++ = quoted(script); + for (i = 1; i < argc; i++) + *newargsp++ = quoted(argv[i]); + *newargsp++ = NULL; /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */ if (is_gui) { -- cgit v1.2.1 From e44a3a3aff42cc965851490e6b1f6973c01473d4 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Fri, 29 Dec 2006 17:43:39 +0000 Subject: Overhauled Windows script wrapping to support ``bdist_wininst`` better. Scripts installed with ``bdist_wininst`` will always use ``#!python.exe`` or ``#!pythonw.exe`` as the executable name (even when built on non-Windows platforms!), and the wrappers will look for the executable in the script's parent directory (which should find the right version of Python). (backport from trunk) --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4053194 --- launcher.c | 187 +++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 121 insertions(+), 66 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index 79dbbbd4..b1da7725 100755 --- a/launcher.c +++ b/launcher.c @@ -22,6 +22,7 @@ starting. So, we have to use spawnv() and wait for Python to exit before continuing. :( */ + #include #include #include @@ -29,20 +30,25 @@ #include "windows.h" int fail(char *format, char *data) { - /* Print error message to stderr and return 1 */ + /* Print error message to stderr and return 2 */ fprintf(stderr, format, data); return 2; } + + + + char *quoted(char *data) { - int i, l = strlen(data), nb; + int i, ln = strlen(data), nb; + /* We allocate twice as much space as needed to deal with worse-case of having to escape everything. */ - char *result = calloc(l*2+3, sizeof(char)); + char *result = calloc(ln*2+3, sizeof(char)); char *presult = result; *presult++ = '"'; - for (nb=0, i=0; i < l; i++) + for (nb=0, i=0; i < ln; i++) { if (data[i] == '\\') nb += 1; @@ -56,6 +62,7 @@ char *quoted(char *data) { nb = 0; *presult++ = data[i]; } + for (; nb > 0; nb--) /* Deal w trailing slashes */ *presult++ = '\\'; @@ -64,49 +71,108 @@ char *quoted(char *data) { return result; } -char *getpyopt(char *python) -{ - /* Search a Python command string, read from a #! line for an - option. An option must be separated from an executable name by - one or more spaces. An option consistes of a hyphen followed by - one or more letters. - */ - static char *letters = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - ; - char *p = python + strlen(python) - 1; - if (strchr(letters, *p) == NULL) - return NULL; /* Path doen't end with a letter. Odd. */ - while (p > python && strchr(letters, *p) != NULL) - p--; - if (p == python || *p != '-') - return NULL; /* Can't be an option */ - p--; - if (p > python && isspace(*p)) - { /* BINGO, we have an option */ - char *pyopt = p+1; - /* strip trailing spaces from remainder of python command */ - while (p > python && isspace(*p)) - *p-- = '\0'; - return pyopt; + + + + + + + + + +char *loadable_exe(char *exename) { + HINSTANCE hPython; /* DLL handle for python executable */ + char *result; + + hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hPython) return NULL; + + /* Return the absolute filename for spawnv */ + result = calloc(MAX_PATH, sizeof(char)); + if (result) GetModuleFileName(hPython, result, MAX_PATH); + + FreeLibrary(hPython); + return result; +} + + +char *find_exe(char *exename, char *script) { + char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT]; + char path[_MAX_PATH], c, *result; + + /* convert slashes to backslashes for uniform search below */ + result = exename; + while (c = *result++) if (c=='/') result[-1] = '\\'; + + _splitpath(exename, drive, dir, fname, ext); + if (drive[0] || dir[0]=='\\') { + return loadable_exe(exename); /* absolute path, use directly */ } - else - return NULL; + /* Use the script's parent directory, which should be the Python home + (This should only be used for bdist_wininst-installed scripts, because + easy_install-ed scripts use the absolute path to python[w].exe + */ + _splitpath(script, drive, dir, fname, ext); + result = dir + strlen(dir) -1; + if (*result == '\\') result--; + while (*result != '\\' && result>=dir) *result-- = 0; + _makepath(path, drive, dir, exename, NULL); + return loadable_exe(path); } + +char **parse_argv(char *cmdline, int *argc) +{ + /* Parse a command line in-place using MS C rules */ + + char **result = calloc(strlen(cmdline), sizeof(char *)); + char *output = cmdline; + char c; + int nb = 0; + *argc = 0; + + result[0] = output; + while (isspace(*cmdline)) cmdline++; /* skip leading spaces */ + + do { + c = *cmdline++; + if (!c || isspace(c)) { + while (nb) {*output++ = '\\'; nb--; } + *output++ = 0; + result[++*argc] = output; + if (!c) return result; + while (isspace(*cmdline)) cmdline++; /* skip leading spaces */ + continue; + } + if (c == '\\') + ++nb; /* count \'s */ + else { + if (c == '"') { + if (!(nb & 1)) c = 0; /* skip " unless odd # of \ */ + nb = nb >> 1; /* cut \'s in half */ + } + while (nb) {*output++ = '\\'; nb--; } + if (c) *output++ = c; + } + } while (1); +} + + + + + + int run(int argc, char **argv, int is_gui) { char python[256]; /* python executable's filename*/ char *pyopt; /* Python option */ char script[256]; /* the script's filename */ - HINSTANCE hPython; /* DLL handle for python executable */ int scriptf; /* file descriptor for script file */ - char **newargs, **newargsp; /* argument array for exec */ + char **newargs, **newargsp, **parsedargs; /* argument array for exec */ char *ptr, *end; /* working pointers for string manipulation */ - int i; /* loop counter */ + int i, parsedargc; /* loop counter */ /* compute script name from our .exe name*/ GetModuleFileName(NULL, script, sizeof(script)); @@ -126,57 +192,51 @@ int run(int argc, char **argv, int is_gui) { close(scriptf); ptr = python-1; - while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') { - if (*ptr=='/') - *ptr='\\'; /* convert slashes to avoid LoadLibrary crashes... */ - } + while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;} *ptr-- = '\0'; - while (ptr>python && isspace(*ptr)) *ptr-- = '\0'; /* strip trailing sp */ + if (strncmp(python, "#!", 2)) { /* default to python.exe if no #! header */ strcpy(python, "#!python.exe"); } - /* Check for Python options */ - pyopt = getpyopt(python); - - /* At this point, the python buffer contains "#!pythonfilename" */ + parsedargs = parse_argv(python+2, &parsedargc); /* Using spawnv() can fail strangely if you e.g. find the Cygwin Python, so we'll make sure Windows can find and load it */ - hPython = LoadLibraryEx(python+2, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!hPython) { - return fail("Cannot find Python executable %s\n", python+2); + + ptr = find_exe(parsedargs[0], script); + if (!ptr) { + return fail("Cannot find Python executable %s\n", parsedargs[0]); } - /* And we'll use the absolute filename for spawnv */ - GetModuleFileName(hPython, python, sizeof(python)); + /* printf("Python executable: %s\n", ptr); */ - /* printf("Python executable: %s\n", python); */ + /* Argument array needs to be + parsedargc + argc, plus 1 for null sentinel */ - /* Argument array needs to be - argc+1 for python executable, - plus 1 for possible python opts, - plus 1 for null sentinel */ - newargs = (char **)calloc(argc+3, sizeof(char *)); + newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *)); newargsp = newargs; - *newargsp++ = quoted(python); - if (pyopt) - *newargsp++ = pyopt; + + *newargsp++ = quoted(ptr); + for (i = 1; i Date: Wed, 24 Jan 2007 21:00:55 +0000 Subject: Fix ``#!`` parsing problems in Windows ``.exe`` script wrappers, when there was whitespace inside a quoted argument or at the end of the ``#!`` line (backport from trunk) --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4053548 --- launcher.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index b1da7725..09e2bafb 100755 --- a/launcher.c +++ b/launcher.c @@ -129,6 +129,7 @@ char **parse_argv(char *cmdline, int *argc) char *output = cmdline; char c; int nb = 0; + int iq = 0; *argc = 0; result[0] = output; @@ -136,19 +137,20 @@ char **parse_argv(char *cmdline, int *argc) do { c = *cmdline++; - if (!c || isspace(c)) { + if (!c || (isspace(c) && !iq)) { while (nb) {*output++ = '\\'; nb--; } *output++ = 0; result[++*argc] = output; if (!c) return result; while (isspace(*cmdline)) cmdline++; /* skip leading spaces */ + if (!*cmdline) return result; /* avoid empty arg if trailing ws */ continue; } if (c == '\\') ++nb; /* count \'s */ else { if (c == '"') { - if (!(nb & 1)) c = 0; /* skip " unless odd # of \ */ + if (!(nb & 1)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */ nb = nb >> 1; /* cut \'s in half */ } while (nb) {*output++ = '\\'; nb--; } @@ -160,8 +162,6 @@ char **parse_argv(char *cmdline, int *argc) - - int run(int argc, char **argv, int is_gui) { char python[256]; /* python executable's filename*/ -- cgit v1.2.1 From 8e3c998ec1aeeb87094e75d4fdc86db0ae3a2e1e Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 3 Jan 2008 23:48:02 +0000 Subject: Backport gui.exe launcher fix. --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4059684 --- launcher.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index 09e2bafb..c8022505 100755 --- a/launcher.c +++ b/launcher.c @@ -231,8 +231,8 @@ int run(int argc, char **argv, int is_gui) { if (is_gui) { /* Use exec, we don't need to wait for the GUI to finish */ - execv(python, (const char * const *)(newargs)); - return fail("Could not exec %s", python); /* shouldn't get here! */ + execv(ptr, (const char * const *)(newargs)); + return fail("Could not exec %s", ptr); /* shouldn't get here! */ } /* We *do* need to wait for a CLI to finish, so use spawn */ -- cgit v1.2.1 From efa78ba0a0e1f89b92da4fa0fa6e5e029efecf3f Mon Sep 17 00:00:00 2001 From: agronholm Date: Fri, 25 Sep 2009 22:01:27 +0300 Subject: Fix script launcher creation on 64-bit Windows, patch by Jason R. Coombs (http://bugs.python.org/setuptools/issue2) --HG-- branch : distribute extra : rebase_source : 60c07b0639b77a3e8ff13eb12161ebe03ab47430 --- launcher.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index c8022505..0dca2e16 100755 --- a/launcher.c +++ b/launcher.c @@ -25,8 +25,9 @@ #include #include -#include +#include #include +#include "tchar.h" #include "windows.h" int fail(char *format, char *data) { @@ -236,11 +237,18 @@ int run(int argc, char **argv, int is_gui) { } /* We *do* need to wait for a CLI to finish, so use spawn */ - return spawnv(P_WAIT, ptr, (const char * const *)(newargs)); + return _spawnv(_P_WAIT, ptr, (const char * const *)(newargs)); } - +/* int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) { return run(__argc, __argv, GUI); } +*/ + +int _tmain(int argc, _TCHAR* argv[]) +{ + return run(argc, argv, GUI); +} + -- cgit v1.2.1 From e6e0e5add9321df50c5da9ee2f1650740024c8ed Mon Sep 17 00:00:00 2001 From: agronholm Date: Tue, 20 Oct 2009 01:37:01 +0300 Subject: Reversing patch for 64-bit Windows script launcher, applied PJE's simpler solution instead --HG-- branch : distribute extra : rebase_source : 320927dbc962a262853cae7d8b3734794bb9f21d --- launcher.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index 0dca2e16..201219cc 100755 --- a/launcher.c +++ b/launcher.c @@ -25,9 +25,8 @@ #include #include -#include +#include #include -#include "tchar.h" #include "windows.h" int fail(char *format, char *data) { @@ -82,17 +81,18 @@ char *quoted(char *data) { char *loadable_exe(char *exename) { - HINSTANCE hPython; /* DLL handle for python executable */ + /* HINSTANCE hPython; DLL handle for python executable */ char *result; - hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!hPython) return NULL; + /* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hPython) return NULL; */ /* Return the absolute filename for spawnv */ result = calloc(MAX_PATH, sizeof(char)); - if (result) GetModuleFileName(hPython, result, MAX_PATH); + strncpy(result, exename, MAX_PATH); + /*if (result) GetModuleFileName(hPython, result, MAX_PATH); - FreeLibrary(hPython); + FreeLibrary(hPython); */ return result; } @@ -237,18 +237,11 @@ int run(int argc, char **argv, int is_gui) { } /* We *do* need to wait for a CLI to finish, so use spawn */ - return _spawnv(_P_WAIT, ptr, (const char * const *)(newargs)); + return spawnv(P_WAIT, ptr, (const char * const *)(newargs)); } -/* + int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) { return run(__argc, __argv, GUI); } -*/ - -int _tmain(int argc, _TCHAR* argv[]) -{ - return run(argc, argv, GUI); -} - -- cgit v1.2.1 From 793092ce4a71e3c2ac8f5ea2bf30144f7104a4ad Mon Sep 17 00:00:00 2001 From: guyroz Date: Sat, 17 Sep 2011 16:25:22 +0300 Subject: Issue #207: passing ctrl-c events to python child process --HG-- branch : distribute extra : rebase_source : e8f22b9c0389b62edf48c5c540498f46fac206ea --- launcher.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 11 deletions(-) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index 201219cc..ea4c80b5 100755 --- a/launcher.c +++ b/launcher.c @@ -25,9 +25,12 @@ #include #include -#include +#include +#include +#include #include -#include "windows.h" + +int child_pid=0; int fail(char *format, char *data) { /* Print error message to stderr and return 2 */ @@ -35,10 +38,6 @@ int fail(char *format, char *data) { return 2; } - - - - char *quoted(char *data) { int i, ln = strlen(data), nb; @@ -90,7 +89,7 @@ char *loadable_exe(char *exename) { /* Return the absolute filename for spawnv */ result = calloc(MAX_PATH, sizeof(char)); strncpy(result, exename, MAX_PATH); - /*if (result) GetModuleFileName(hPython, result, MAX_PATH); + /*if (result) GetModuleFileNameA(hPython, result, MAX_PATH); FreeLibrary(hPython); */ return result; @@ -160,8 +159,82 @@ char **parse_argv(char *cmdline, int *argc) } while (1); } +void pass_control_to_child(DWORD control_type) { + /* + * distribute-issue207 + * passes the control event to child process (Python) + */ + if (!child_pid) { + return; + } + GenerateConsoleCtrlEvent(child_pid,0); +} + +BOOL control_handler(DWORD control_type) { + /* + * distribute-issue207 + * control event handler callback function + */ + switch (control_type) { + case CTRL_C_EVENT: + pass_control_to_child(0); + break; + } + return TRUE; +} + +int create_and_wait_for_subprocess(char* command) { + /* + * distribute-issue207 + * launches child process (Python) + */ + DWORD return_value = 0; + LPSTR commandline = command; + STARTUPINFOA s_info; + PROCESS_INFORMATION p_info; + ZeroMemory(&p_info, sizeof(p_info)); + ZeroMemory(&s_info, sizeof(s_info)); + s_info.cb = sizeof(STARTUPINFO); + // set-up control handler callback funciotn + SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE); + if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) { + fprintf(stderr, "failed to create process.\n"); + return 0; + } + child_pid = p_info.dwProcessId; + // wait for Python to exit + WaitForSingleObject(p_info.hProcess, INFINITE); + if (!GetExitCodeProcess(p_info.hProcess, &return_value)) { + fprintf(stderr, "failed to get exit code from process.\n"); + return 0; + } + return return_value; +} +char* join_executable_and_args(char *executable, char **args, int argc) +{ + /* + * distribute-issue207 + * CreateProcess needs a long string of the executable and command-line arguments, + * so we need to convert it from the args that was built + */ + int len,counter; + char* cmdline; + + len=strlen(executable)+2; + for (counter=1; counterscript && *end != '.') *end-- = '\0'; @@ -236,12 +310,18 @@ int run(int argc, char **argv, int is_gui) { return fail("Could not exec %s", ptr); /* shouldn't get here! */ } - /* We *do* need to wait for a CLI to finish, so use spawn */ - return spawnv(P_WAIT, ptr, (const char * const *)(newargs)); + /* + * distribute-issue207: using CreateProcessA instead of spawnv + */ + cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc); + return create_and_wait_for_subprocess(cmdline); } - int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) { return run(__argc, __argv, GUI); } +int main(int argc, char** argv) { + return run(argc, argv, GUI); +} + -- cgit v1.2.1 From d9c4d3c51e93c08a76d310e549c6d5de260667bb Mon Sep 17 00:00:00 2001 From: Grigory Petrov Date: Wed, 3 Apr 2013 02:02:44 +0400 Subject: Added support for Windows RT (arm). --HG-- branch : distribute extra : rebase_source : c181b8cc551936e48bdc88d9435018d0d9de00b2 --- launcher.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index ea4c80b5..be69f0c6 100755 --- a/launcher.c +++ b/launcher.c @@ -14,6 +14,14 @@ gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c + To build for Windows RT, install both Visual Studio Express for Windows 8 + and for Windows Desktop (both freeware), create "win32" application using + "Windows Desktop" version, create new "ARM" target via + "Configuration Manager" menu and modify ".vcxproj" file by adding + "true" tag + as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM" + properties. + It links to msvcrt.dll, but this shouldn't be a problem since it doesn't actually run Python in the same process. Note that using 'exec' instead of 'spawn' doesn't work, because on Windows this leads to the Python -- cgit v1.2.1 From 760e2e1df9c9c9d1fc072e7b6ad9df4c32bfc835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Fri, 27 Jul 2018 14:36:34 +0200 Subject: Remove spurious executable permissions --- launcher.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 launcher.c (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c old mode 100755 new mode 100644 -- cgit v1.2.1 From fa22b42d9f2f8a15568dd3a3d290e33c9be86796 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 13 Jun 2019 18:31:57 +0200 Subject: launcher: Fix build with mingw-w64 execv() requires process.h to be included according to the MSVC documentation but for some reason it also works without it. mingw-w64 on the other hand fails to build the launcher if the include isn't there, so add it. --- launcher.c | 1 + 1 file changed, 1 insertion(+) (limited to 'launcher.c') diff --git a/launcher.c b/launcher.c index be69f0c6..23ef3ac2 100644 --- a/launcher.c +++ b/launcher.c @@ -37,6 +37,7 @@ #include #include #include +#include int child_pid=0; -- cgit v1.2.1