| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- /* 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 -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
- "<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" 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
- 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 <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <windows.h>
- #include <tchar.h>
- #include <fcntl.h>
- int child_pid=0;
- int fail(char *format, char *data) {
- /* Print error message to stderr and return 2 */
- fprintf(stderr, format, data);
- return 2;
- }
- char *quoted(char *data) {
- 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(ln*2+3, sizeof(char));
- char *presult = result;
- *presult++ = '"';
- for (nb=0, i=0; i < ln; 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 *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));
- strncpy(result, exename, MAX_PATH);
- /*if (result) GetModuleFileNameA(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 */
- }
- /* 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;
- int iq = 0;
- *argc = 0;
- result[0] = output;
- while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
- do {
- c = *cmdline++;
- 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)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */
- nb = nb >> 1; /* cut \'s in half */
- }
- while (nb) {*output++ = '\\'; nb--; }
- if (c) *output++ = c;
- }
- } 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; counter<argc; counter++) {
- len+=strlen(args[counter])+1;
- }
- cmdline = (char*)calloc(len, sizeof(char));
- sprintf(cmdline, "%s", executable);
- len=strlen(executable);
- for (counter=1; counter<argc; counter++) {
- sprintf(cmdline+len, " %s", args[counter]);
- len+=strlen(args[counter])+1;
- }
- return cmdline;
- }
- 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 */
- int scriptf; /* file descriptor for script file */
- char **newargs, **newargsp, **parsedargs; /* argument array for exec */
- char *ptr, *end; /* working pointers for string manipulation */
- char *cmdline;
- int i, parsedargc; /* loop counter */
- /* compute script name from our .exe name*/
- GetModuleFileNameA(NULL, script, sizeof(script));
- end = script + strlen(script);
- while( end>script && *end != '.')
- *end-- = '\0';
- *end-- = '\0';
- strcat(script, (GUI ? "-script.pyw" : "-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') {;}
- *ptr-- = '\0';
- if (strncmp(python, "#!", 2)) {
- /* default to python.exe if no #! header */
- strcpy(python, "#!python.exe");
- }
- 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 */
- ptr = find_exe(parsedargs[0], script);
- if (!ptr) {
- return fail("Cannot find Python executable %s\n", parsedargs[0]);
- }
- /* printf("Python executable: %s\n", ptr); */
- /* Argument array needs to be
- parsedargc + argc, plus 1 for null sentinel */
- newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));
- newargsp = newargs;
- *newargsp++ = quoted(ptr);
- for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);
- *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) {
- /* Use exec, we don't need to wait for the GUI to finish */
- execv(ptr, (const char * const *)(newargs));
- return fail("Could not exec %s", ptr); /* shouldn't get here! */
- }
- /*
- * 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);
- }
|