diff options
author | PJ Eby <distutils-sig@python.org> | 2006-12-29 17:43:39 +0000 |
---|---|---|
committer | PJ Eby <distutils-sig@python.org> | 2006-12-29 17:43:39 +0000 |
commit | e44a3a3aff42cc965851490e6b1f6973c01473d4 (patch) | |
tree | b29cd7ff9baa69216dcc963f900b8134ebef60ce | |
parent | 47f061eef1d9c4832b9a4394e84642811356ad10 (diff) | |
download | external_python_setuptools-e44a3a3aff42cc965851490e6b1f6973c01473d4.tar.gz external_python_setuptools-e44a3a3aff42cc965851490e6b1f6973c01473d4.tar.bz2 external_python_setuptools-e44a3a3aff42cc965851490e6b1f6973c01473d4.zip |
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
-rwxr-xr-x | README.txt | 7 | ||||
-rwxr-xr-x | launcher.c | 187 | ||||
-rwxr-xr-x | setuptools.txt | 7 | ||||
-rwxr-xr-x | setuptools/cli.exe | bin | 7680 -> 6656 bytes | |||
-rwxr-xr-x | setuptools/command/easy_install.py | 71 | ||||
-rwxr-xr-x | setuptools/gui.exe | bin | 7680 -> 6656 bytes |
6 files changed, 187 insertions, 85 deletions
@@ -14,7 +14,8 @@ Windows Install setuptools using the provided ``.exe`` installer. If you've previously installed older versions of setuptools, please delete all ``setuptools*.egg`` -files from your system FIRST. +and ``setuptools.pth`` files from your system's ``site-packages`` directory +(and any other ``sys.path`` directories) FIRST. If you are upgrading a previous version of setuptools that was installed using an ``.exe`` installer, please be sure to also *uninstall that older version* @@ -23,9 +24,7 @@ version. Once installation is complete, you will find an ``easy_install.exe`` program in your Python ``Scripts`` subdirectory. Be sure to add this directory to your -``PATH`` environment variable, if you haven't already done so. You must also -have your Python installation directory (e.g. ``C:\\Python23``) on the -``PATH``. +``PATH`` environment variable, if you haven't already done so. RPM-Based Systems @@ -22,6 +22,7 @@ starting. So, we have to use spawnv() and wait for Python to exit before continuing. :( */ + #include <stdlib.h> #include <stdio.h> #include <unistd.h> @@ -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<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]); + *newargsp++ = quoted(script); - for (i = 1; i < argc; i++) - *newargsp++ = quoted(argv[i]); + 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(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, python, (const char * const *)(newargs)); + return spawnv(P_WAIT, ptr, (const char * const *)(newargs)); } @@ -184,8 +244,3 @@ int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) { return run(__argc, __argv, GUI); } - - - - - diff --git a/setuptools.txt b/setuptools.txt index dae4e03d..0997122a 100755 --- a/setuptools.txt +++ b/setuptools.txt @@ -2601,6 +2601,13 @@ Release Notes/Change History ---------------------------- 0.6c4 + + * 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). + * Fix ``upload`` command not uploading files built by ``bdist_rpm`` or ``bdist_wininst`` under Python 2.3 and 2.4. diff --git a/setuptools/cli.exe b/setuptools/cli.exe Binary files differindex aa601168..18f23380 100755 --- a/setuptools/cli.exe +++ b/setuptools/cli.exe diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 8f2aa457..f7a39907 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -369,16 +369,13 @@ Please make the appropriate changes for your system and try again. def install_egg_scripts(self, dist): """Write all the scripts for `dist`, unless scripts are excluded""" - + if not self.exclude_scripts and dist.metadata_isdir('scripts'): + for script_name in dist.metadata_listdir('scripts'): + self.install_script( + dist, script_name, + dist.get_metadata('scripts/'+script_name).replace('\r','\n') + ) self.install_wrapper_scripts(dist) - if self.exclude_scripts or not dist.metadata_isdir('scripts'): - return - - for script_name in dist.metadata_listdir('scripts'): - self.install_script( - dist, script_name, - dist.get_metadata('scripts/'+script_name).replace('\r','\n') - ) def add_output(self, path): if os.path.isdir(path): @@ -408,6 +405,9 @@ Please make the appropriate changes for your system and try again. + + + def easy_install(self, spec, deps=False): tmpdir = tempfile.mkdtemp(prefix="easy_install-") download = None @@ -1407,7 +1407,6 @@ class PthDistributions(Environment): else: return path - def get_script_header(script_text, executable=sys_executable, wininst=False): """Create a #! line, getting options (if any) from script_text""" from distutils.command.build_scripts import first_line_re @@ -1418,8 +1417,10 @@ def get_script_header(script_text, executable=sys_executable, wininst=False): options = match.group(1) or '' if options: options = ' '+options - if wininst and sys.platform!='win32': + if wininst: executable = "python.exe" + else: + executable = nt_quote_arg(executable) hdr = "#!%(executable)s%(options)s\n" % locals() if unicode(hdr,'ascii','ignore').encode('ascii') != hdr: # Non-ascii path to sys.executable, use -x to prevent warnings @@ -1432,7 +1433,6 @@ def get_script_header(script_text, executable=sys_executable, wininst=False): hdr = "#!%(executable)s%(options)s\n" % locals() return hdr - def auto_chmod(func, arg, exc): if func is os.remove and os.name=='nt': os.chmod(arg, stat.S_IWRITE) @@ -1474,6 +1474,47 @@ def is_python(text, filename='<string>'): +def nt_quote_arg(arg): + """Quote a command line argument according to Windows parsing rules""" + + result = [] + needquote = False + nb = 0 + + needquote = (" " in arg) or ("\t" in arg) + if needquote: + result.append('"') + + for c in arg: + if c == '\\': + nb += 1 + elif c == '"': + # double preceding backslashes, then add a \" + result.append('\\' * (nb*2) + '\\"') + nb = 0 + else: + if nb: + result.append('\\' * nb) + nb = 0 + result.append(c) + + if nb: + result.append('\\' * nb) + + if needquote: + result.append('\\' * nb) # double the trailing backslashes + result.append('"') + + return ''.join(result) + + + + + + + + + def is_python_script(script_text, filename): """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc. """ @@ -1532,7 +1573,7 @@ def get_script_args(dist, executable=sys_executable, wininst=False): ")\n" ) % locals() if sys.platform=='win32' or wininst: - # On Windows, add a .py extension and an .exe launcher + # On Windows/wininst, add a .py extension and an .exe launcher if group=='gui_scripts': ext, launcher = '-script.pyw', 'gui.exe' old = ['.pyw'] @@ -1540,9 +1581,9 @@ def get_script_args(dist, executable=sys_executable, wininst=False): else: ext, launcher = '-script.py', 'cli.exe' old = ['.py','.pyc','.pyo'] - new_header = re.sub('(?i)pythonw.exe','pythonw.exe',header) + new_header = re.sub('(?i)pythonw.exe','python.exe',header) - if os.path.exists(new_header[2:-1]): + if os.path.exists(new_header[2:-1]) or sys.platform!='win32': hdr = new_header else: hdr = header diff --git a/setuptools/gui.exe b/setuptools/gui.exe Binary files differindex 5589a8f6..2d0b2f26 100755 --- a/setuptools/gui.exe +++ b/setuptools/gui.exe |