Commit ad0eebab authored by PJ Eby's avatar PJ Eby

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
parent 9159d6d8
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
To build/rebuild with mingw32, do this in the setuptools project directory: 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 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 actually run Python in the same process. Note that using 'exec' instead
...@@ -32,14 +33,13 @@ ...@@ -32,14 +33,13 @@
int fail(char *format, char *data) { int fail(char *format, char *data) {
/* Print error message to stderr and return 1 */ /* Print error message to stderr and return 1 */
fprintf(stderr, format, data); fprintf(stderr, format, data);
return 1; return 2;
} }
int run(int argc, char **argv, int is_gui) {
int main(int argc, char **argv) {
char python[256]; /* python executable's filename*/ char python[256]; /* python executable's filename*/
char script[256]; /* the script's filename */ char script[256]; /* the script's filename */
...@@ -55,7 +55,7 @@ int main(int argc, char **argv) { ...@@ -55,7 +55,7 @@ int main(int argc, char **argv) {
end = script + strlen(script); end = script + strlen(script);
while( end>script && *end != '.') while( end>script && *end != '.')
*end-- = '\0'; *end-- = '\0';
strcat(script, "py"); strcat(script, (GUI ? "pyw" : "py"));
/* figure out the target python executable */ /* figure out the target python executable */
...@@ -102,19 +102,19 @@ int main(int argc, char **argv) { ...@@ -102,19 +102,19 @@ int main(int argc, char **argv) {
newargs[argc+1] = NULL; newargs[argc+1] = NULL;
/* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */ /* 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)); 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);
}
......
...@@ -24,6 +24,7 @@ bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm ...@@ -24,6 +24,7 @@ bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
rotate = setuptools.command.rotate:rotate rotate = setuptools.command.rotate:rotate
develop = setuptools.command.develop:develop develop = setuptools.command.develop:develop
setopt = setuptools.command.setopt:setopt setopt = setuptools.command.setopt:setopt
build_py = setuptools.command.build_py:build_py
saveopts = setuptools.command.saveopts:saveopts saveopts = setuptools.command.saveopts:saveopts
egg_info = setuptools.command.egg_info:egg_info egg_info = setuptools.command.egg_info:egg_info
upload = setuptools.command.upload:upload upload = setuptools.command.upload:upload
......
...@@ -39,8 +39,9 @@ Feature Highlights: ...@@ -39,8 +39,9 @@ Feature Highlights:
without needing to create a ``MANIFEST.in`` file, and without having to force without needing to create a ``MANIFEST.in`` file, and without having to force
regeneration of the ``MANIFEST`` file when your source tree changes. regeneration of the ``MANIFEST`` file when your source tree changes.
* Automatically generate wrapper scripts or Windows (console) .exe files for * Automatically generate wrapper scripts or Windows (console and GUI) .exe
any number of "main" functions in your project. files for any number of "main" functions in your project. (Note: this is not
a py2exe replacement; the .exe files rely on the local Python installation.)
* Transparent Pyrex support, so that your setup.py can list ``.pyx`` files and * Transparent Pyrex support, so that your setup.py can list ``.pyx`` files and
still work even when the end-user doesn't have Pyrex installed (as long as still work even when the end-user doesn't have Pyrex installed (as long as
...@@ -314,8 +315,8 @@ for you with the correct extension, and on Windows it will even create an ...@@ -314,8 +315,8 @@ for you with the correct extension, and on Windows it will even create an
``.exe`` file so that users don't have to change their ``PATHEXT`` settings. ``.exe`` file so that users don't have to change their ``PATHEXT`` settings.
The way to use this feature is to define "entry points" in your setup script The way to use this feature is to define "entry points" in your setup script
that indicate what function the generated script should import and run. For that indicate what function the generated script should import and run. For
example, to create two scripts called ``foo`` and ``bar``, you might do example, to create two console scripts called ``foo`` and ``bar``, and a GUI
something like this:: script called ``baz``, you might do something like this::
setup( setup(
# other arguments here... # other arguments here...
...@@ -323,23 +324,31 @@ something like this:: ...@@ -323,23 +324,31 @@ something like this::
'console_scripts': [ 'console_scripts': [
'foo = my_package.some_module:main_func', 'foo = my_package.some_module:main_func',
'bar = other_module:some_func', 'bar = other_module:some_func',
],
'gui_scripts': [
'baz = my_package_gui.start_func',
] ]
} }
) )
When this project is installed on non-Windows platforms (using "setup.py When this project is installed on non-Windows platforms (using "setup.py
install", "setup.py develop", or by using EasyInstall), a pair of ``foo`` and install", "setup.py develop", or by using EasyInstall), a set of ``foo``,
``bar`` scripts will be installed that import ``main_func`` and ``some_func`` ``bar``, and ``baz`` scripts will be installed that import ``main_func`` and
from the specified modules. The functions you specify are called with no ``some_func`` from the specified modules. The functions you specify are called
arguments, and their return value is passed to ``sys.exit()``, so you can with no arguments, and their return value is passed to ``sys.exit()``, so you
return an errorlevel or message to print to stderr. can return an errorlevel or message to print to stderr.
You may define as many "console script" entry points as you like, and each one On Windows, a set of ``foo.exe``, ``bar.exe``, and ``baz.exe`` launchers are
can optionally specify "extras" that it depends on, and that will be added to created, alongside a set of ``foo.py``, ``bar.py``, and ``baz.pyw`` files. The
``sys.path`` when the script is run. For more information on "extras", see ``.exe`` wrappers find and execute the right version of Python to run the
section below on `Declaring Extras`_. For more information on "entry points" ``.py`` or ``.pyw`` file.
in general, see the section below on `Dynamic Discovery of Services and
Plugins`_. You may define as many "console script" and "gui script" entry points as you
like, and each one can optionally specify "extras" that it depends on, that
will be added to ``sys.path`` when the script is run. For more information on
"extras", see the section below on `Declaring Extras`_. For more information
on "entry points" in general, see the section below on `Dynamic Discovery of
Services and Plugins`_.
Declaring Dependencies Declaring Dependencies
...@@ -1848,6 +1857,11 @@ XXX ...@@ -1848,6 +1857,11 @@ XXX
Release Notes/Change History Release Notes/Change History
---------------------------- ----------------------------
0.6a3
* Added ``gui_scripts`` entry point group to allow installing GUI scripts
on Windows and other platforms. (The special handling is only for Windows;
other platforms are treated the same as for ``console_scripts``.)
0.6a2 0.6a2
* Added ``console_scripts`` entry point group to allow installing scripts * Added ``console_scripts`` entry point group to allow installing scripts
without the need to create separate script files. On Windows, console without the need to create separate script files. On Windows, console
......
...@@ -103,7 +103,7 @@ class develop(easy_install): ...@@ -103,7 +103,7 @@ class develop(easy_install):
# create wrapper scripts in the script dir, pointing to dist.scripts # create wrapper scripts in the script dir, pointing to dist.scripts
# new-style... # new-style...
self.install_console_scripts(dist) self.install_wrapper_scripts(dist)
# ...and old-style # ...and old-style
for script_name in self.distribution.scripts or []: for script_name in self.distribution.scripts or []:
......
...@@ -10,7 +10,7 @@ file, or visit the `EasyInstall home page`__. ...@@ -10,7 +10,7 @@ file, or visit the `EasyInstall home page`__.
__ http://peak.telecommunity.com/DevCenter/EasyInstall __ http://peak.telecommunity.com/DevCenter/EasyInstall
""" """
import sys, os.path, zipimport, shutil, tempfile, zipfile import sys, os.path, zipimport, shutil, tempfile, zipfile, re
from glob import glob from glob import glob
from setuptools import Command from setuptools import Command
from setuptools.sandbox import run_setup from setuptools.sandbox import run_setup
...@@ -247,7 +247,7 @@ class easy_install(Command): ...@@ -247,7 +247,7 @@ class easy_install(Command):
def install_egg_scripts(self, dist): def install_egg_scripts(self, dist):
"""Write all the scripts for `dist`, unless scripts are excluded""" """Write all the scripts for `dist`, unless scripts are excluded"""
self.install_console_scripts(dist) self.install_wrapper_scripts(dist)
if self.exclude_scripts or not dist.metadata_isdir('scripts'): if self.exclude_scripts or not dist.metadata_isdir('scripts'):
return return
...@@ -440,27 +440,21 @@ class easy_install(Command): ...@@ -440,27 +440,21 @@ class easy_install(Command):
return dst return dst
def install_wrapper_scripts(self, dist):
if self.exclude_scripts:
return
for group in 'console_scripts', 'gui_scripts':
for name,ep in dist.get_entry_map(group).items():
self._install_wrapper_script(dist, group, name, ep)
def _install_wrapper_script(self, dist, group, name, entry_point):
def install_console_scripts(self, dist):
"""Write new-style console scripts, unless excluded""" """Write new-style console scripts, unless excluded"""
if self.exclude_scripts:
return
spec = str(dist.as_requirement()) spec = str(dist.as_requirement())
group = 'console_scripts' header = get_script_header("")
script_text = (
for name,ep in dist.get_entry_map(group).items():
script_text = get_script_header("") + (
"# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n" "# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n"
"__requires__ = %(spec)r\n" "__requires__ = %(spec)r\n"
"import sys\n" "import sys\n"
...@@ -473,19 +467,25 @@ class easy_install(Command): ...@@ -473,19 +467,25 @@ class easy_install(Command):
if sys.platform=='win32': if sys.platform=='win32':
# On Windows, add a .py extension and an .exe launcher # On Windows, add a .py extension and an .exe launcher
self.write_script(name+'.py', script_text) if group=='gui_scripts':
ext, launcher = '.pyw', 'gui.exe'
new_header = re.sub('(?i)python.exe','pythonw.exe',header)
else:
ext, launcher = '.py', 'cli.exe'
new_header = re.sub('(?i)pythonw.exe','pythonw.exe',header)
if os.path.exists(new_header[2:-1]):
header = new_header
self.write_script(name+ext, header+script_text)
self.write_script( self.write_script(
name+'.exe', resource_string('setuptools','launcher.exe'), name+'.exe', resource_string('setuptools', launcher),
'b' # write in binary mode 'b' # write in binary mode
) )
else: else:
# On other platforms, we assume the right thing to do is to # On other platforms, we assume the right thing to do is to just
# write the stub with no extension. # write the stub with no extension.
self.write_script(name, script_text) self.write_script(name, header+script_text)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment