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
This commit is contained in:
PJ Eby 2005-09-24 20:29:57 +00:00
parent 9159d6d847
commit ad0eebab21
8 changed files with 89 additions and 74 deletions

View File

@ -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,22 +102,22 @@ 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);
}

View File

@ -24,6 +24,7 @@ bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
rotate = setuptools.command.rotate:rotate
develop = setuptools.command.develop:develop
setopt = setuptools.command.setopt:setopt
build_py = setuptools.command.build_py:build_py
saveopts = setuptools.command.saveopts:saveopts
egg_info = setuptools.command.egg_info:egg_info
upload = setuptools.command.upload:upload

View File

@ -39,8 +39,9 @@ Feature Highlights:
without needing to create a ``MANIFEST.in`` file, and without having to force
regeneration of the ``MANIFEST`` file when your source tree changes.
* Automatically generate wrapper scripts or Windows (console) .exe files for
any number of "main" functions in your project.
* Automatically generate wrapper scripts or Windows (console and GUI) .exe
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
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
``.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
that indicate what function the generated script should import and run. For
example, to create two scripts called ``foo`` and ``bar``, you might do
something like this::
example, to create two console scripts called ``foo`` and ``bar``, and a GUI
script called ``baz``, you might do something like this::
setup(
# other arguments here...
@ -323,23 +324,31 @@ something like this::
'console_scripts': [
'foo = my_package.some_module:main_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
install", "setup.py develop", or by using EasyInstall), a pair of ``foo`` and
``bar`` scripts will be installed that import ``main_func`` and ``some_func``
from the specified modules. The functions you specify are called with no
arguments, and their return value is passed to ``sys.exit()``, so you can
return an errorlevel or message to print to stderr.
install", "setup.py develop", or by using EasyInstall), a set of ``foo``,
``bar``, and ``baz`` scripts will be installed that import ``main_func`` and
``some_func`` from the specified modules. The functions you specify are called
with no arguments, and their return value is passed to ``sys.exit()``, so you
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
can optionally specify "extras" that it depends on, and that will be added to
``sys.path`` when the script is run. For more information on "extras", see
section below on `Declaring Extras`_. For more information on "entry points"
in general, see the section below on `Dynamic Discovery of Services and
Plugins`_.
On Windows, a set of ``foo.exe``, ``bar.exe``, and ``baz.exe`` launchers are
created, alongside a set of ``foo.py``, ``bar.py``, and ``baz.pyw`` files. The
``.exe`` wrappers find and execute the right version of Python to run the
``.py`` or ``.pyw`` file.
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
@ -1848,6 +1857,11 @@ XXX
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
* Added ``console_scripts`` entry point group to allow installing scripts
without the need to create separate script files. On Windows, console

BIN
setuptools/cli.exe Executable file

Binary file not shown.

View File

@ -103,7 +103,7 @@ class develop(easy_install):
# create wrapper scripts in the script dir, pointing to dist.scripts
# new-style...
self.install_console_scripts(dist)
self.install_wrapper_scripts(dist)
# ...and old-style
for script_name in self.distribution.scripts or []:

View File

@ -10,7 +10,7 @@ file, or visit the `EasyInstall home page`__.
__ 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 setuptools import Command
from setuptools.sandbox import run_setup
@ -247,7 +247,7 @@ class easy_install(Command):
def install_egg_scripts(self, dist):
"""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'):
return
@ -440,52 +440,52 @@ class easy_install(Command):
return dst
def install_console_scripts(self, dist):
"""Write new-style console scripts, unless excluded"""
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):
"""Write new-style console scripts, unless excluded"""
spec = str(dist.as_requirement())
group = 'console_scripts'
header = get_script_header("")
script_text = (
"# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n"
"__requires__ = %(spec)r\n"
"import sys\n"
"from pkg_resources import load_entry_point\n"
"\n"
"sys.exit(\n"
" load_entry_point(%(spec)r, %(group)r, %(name)r)()\n"
")\n"
) % locals()
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"
"__requires__ = %(spec)r\n"
"import sys\n"
"from pkg_resources import load_entry_point\n"
"\n"
"sys.exit(\n"
" load_entry_point(%(spec)r, %(group)r, %(name)r)()\n"
")\n"
) % locals()
if sys.platform=='win32':
# On Windows, add a .py extension and an .exe launcher
self.write_script(name+'.py', script_text)
self.write_script(
name+'.exe', resource_string('setuptools','launcher.exe'),
'b' # write in binary mode
)
if sys.platform=='win32':
# On Windows, add a .py extension and an .exe launcher
if group=='gui_scripts':
ext, launcher = '.pyw', 'gui.exe'
new_header = re.sub('(?i)python.exe','pythonw.exe',header)
else:
# On other platforms, we assume the right thing to do is to
# write the stub with no extension.
self.write_script(name, script_text)
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(
name+'.exe', resource_string('setuptools', launcher),
'b' # write in binary mode
)
else:
# On other platforms, we assume the right thing to do is to just
# write the stub with no extension.
self.write_script(name, header+script_text)
@ -495,7 +495,7 @@ class easy_install(Command):
spec = str(dist.as_requirement())
if dev_path:
script_text = get_script_header(script_text) + (
script_text = get_script_header(script_text) + (
"# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r\n"
"__requires__ = %(spec)r\n"
"from pkg_resources import require; require(%(spec)r)\n"
@ -504,7 +504,7 @@ class easy_install(Command):
"execfile(__file__)\n"
) % locals()
else:
script_text = get_script_header(script_text) + (
script_text = get_script_header(script_text) + (
"# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r\n"
"__requires__ = %(spec)r\n"
"import pkg_resources\n"

BIN
setuptools/gui.exe Executable file

Binary file not shown.

Binary file not shown.