diff options
author | PJ Eby <distutils-sig@python.org> | 2006-09-22 00:09:06 +0000 |
---|---|---|
committer | PJ Eby <distutils-sig@python.org> | 2006-09-22 00:09:06 +0000 |
commit | ae347c1ed3e39fb2c00b8b9fc8aedcf507313c20 (patch) | |
tree | 2d8a9c2f40a70b768050625aa200b49d3325ebd2 | |
parent | 4ef57c26cbeee3f3afd21f86877bbdcadf0f69c1 (diff) | |
download | external_python_setuptools-ae347c1ed3e39fb2c00b8b9fc8aedcf507313c20.tar.gz external_python_setuptools-ae347c1ed3e39fb2c00b8b9fc8aedcf507313c20.tar.bz2 external_python_setuptools-ae347c1ed3e39fb2c00b8b9fc8aedcf507313c20.zip |
Add support for "eggsecutable" headers: a /bin/sh script that is prepended
to an .egg file to allow it to be run as a script on Unix-ish platforms.
(This is mainly so that setuptools itself can have a single-file installer
on Unix, without doing multiple downloads, dealing with firewalls, etc.)
(Backport from trunk)
--HG--
branch : setuptools-0.6
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4051969
-rwxr-xr-x | setup.py | 10 | ||||
-rwxr-xr-x | setuptools.egg-info/entry_points.txt | 5 | ||||
-rwxr-xr-x | setuptools.txt | 43 | ||||
-rw-r--r-- | setuptools/command/bdist_egg.py | 57 | ||||
-rwxr-xr-x | setuptools/command/easy_install.py | 8 |
5 files changed, 105 insertions, 18 deletions
@@ -84,10 +84,13 @@ setup( "easy_install = setuptools.command.easy_install:main", "easy_install-%s = setuptools.command.easy_install:main" % sys.version[:3] - ], + ], "setuptools.file_finders": - ["svn_cvs = setuptools.command.sdist:_default_revctrl"] + ["svn_cvs = setuptools.command.sdist:_default_revctrl"], + + "setuptools.installation": + ['eggsecutable = setuptools.command.easy_install:bootstrap'], }, classifiers = [f.strip() for f in """ @@ -118,6 +121,3 @@ setup( - - - diff --git a/setuptools.egg-info/entry_points.txt b/setuptools.egg-info/entry_points.txt index 4156edb5..fb9081c8 100755 --- a/setuptools.egg-info/entry_points.txt +++ b/setuptools.egg-info/entry_points.txt @@ -30,6 +30,9 @@ depends.txt = setuptools.command.egg_info:warn_depends_obsolete easy_install = setuptools.command.easy_install:main easy_install-2.4 = setuptools.command.easy_install:main +[setuptools.installation] +eggsecutable = setuptools.command.easy_install:bootstrap + [distutils.commands] bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm rotate = setuptools.command.rotate:rotate @@ -44,10 +47,10 @@ install_egg_info = setuptools.command.install_egg_info:install_egg_info alias = setuptools.command.alias:alias easy_install = setuptools.command.easy_install:easy_install install_scripts = setuptools.command.install_scripts:install_scripts +bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst bdist_egg = setuptools.command.bdist_egg:bdist_egg install = setuptools.command.install:install test = setuptools.command.test:test install_lib = setuptools.command.install_lib:install_lib build_ext = setuptools.command.build_ext:build_ext sdist = setuptools.command.sdist:sdist - diff --git a/setuptools.txt b/setuptools.txt index 43e0e610..26874c88 100755 --- a/setuptools.txt +++ b/setuptools.txt @@ -498,6 +498,43 @@ on "entry points" in general, see the section below on `Dynamic Discovery of Services and Plugins`_. +"Eggsecutable" Scripts +---------------------- + +Occasionally, there are situations where it's desirable to make an ``.egg`` +file directly executable. You can do this by including an entry point such +as the following:: + + setup( + # other arguments here... + entry_points = { + 'setuptools.installation': [ + 'eggsecutable = my_package.some_module:main_func', + ] + } + ) + +Any eggs built from the above setup script will include a short excecutable +prelude that imports and calls ``main_func()`` from ``my_package.some_module``. +The prelude can be run on Unix-like platforms (including Mac and Linux) by +invoking the egg with ``/bin/sh``, or by enabling execute permissions on the +``.egg`` file. For the executable prelude to run, the appropriate version of +Python must be available via the ``PATH`` environment variable, under its +"long" name. That is, if the egg is built for Python 2.3, there must be a +``python2.3`` executable present in a directory on ``PATH``. + +This feature is primarily intended to support bootstrapping the installation of +setuptools itself on non-Windows platforms, but may also be useful for other +projects as well. + +IMPORTANT NOTE: Eggs with an "eggsecutable" header cannot be renamed, or +invoked via symlinks. They *must* be invoked using their original filename, in +order to ensure that, once running, ``pkg_resources`` will know what project +and version is in use. The header script will check this and exit with an +error if the ``.egg`` file has been renamed or is invoked via a symlink that +changes its base name. + + Declaring Dependencies ====================== @@ -2567,6 +2604,12 @@ Release Notes/Change History * Fix ``upload`` command not uploading files built by ``bdist_rpm`` or ``bdist_wininst`` under Python 2.3 and 2.4. + * Add support for "eggsecutable" headers: a ``#!/bin/sh`` script that is + prepended to an ``.egg`` file to allow it to be run as a script on Unix-ish + platforms. (This is mainly so that setuptools itself can have a single-file + installer on Unix, without doing multiple downloads, dealing with firewalls, + etc.) + 0.6c3 * Fixed breakages caused by Subversion 1.4's new "working copy" format diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 981a1f9b..1f768e82 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -8,7 +8,9 @@ from setuptools import Command from distutils.dir_util import remove_tree, mkpath from distutils.sysconfig import get_python_version, get_python_lib from distutils import log +from distutils.errors import DistutilsSetupError from pkg_resources import get_build_platform, Distribution, ensure_directory +from pkg_resources import EntryPoint from types import CodeType from setuptools.extension import Library @@ -37,8 +39,6 @@ def write_stub(resource, pyfile): # stub __init__.py for packages distributed without one NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)' - - class bdist_egg(Command): description = "create an \"egg\" distribution" @@ -233,7 +233,7 @@ class bdist_egg(Command): # Make the archive make_zipfile(self.egg_output, archive_root, verbose=self.verbose, - dry_run=self.dry_run) + dry_run=self.dry_run, mode=self.gen_header()) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) @@ -285,6 +285,47 @@ class bdist_egg(Command): return init_files + def gen_header(self): + epm = EntryPoint.parse_map(self.distribution.entry_points or '') + ep = epm.get('setuptools.installation',{}).get('eggsecutable') + if ep is None: + return 'w' # not an eggsecutable, do it the usual way. + + if not ep.attrs or ep.extras: + raise DistutilsSetupError( + "eggsecutable entry point (%r) cannot have 'extras' " + "or refer to a module" % (ep,) + ) + + pyver = sys.version[:3] + pkg = ep.module_name + full = '.'.join(ep.attrs) + base = ep.attrs[0] + basename = os.path.basename(self.egg_output) + + header = ( + "#!/bin/sh\n" + 'if [[ `basename $0` = "%(basename)s" ]]\n' + 'then exec python%(pyver)s -c "' + "import sys, os; sys.path.insert(0, os.path.abspath('$0')); " + "from %(pkg)s import %(base)s; sys.exit(%(full)s())" + '" "$@"\n' + 'else\n' + ' echo $0 is not the correct name for this egg file.\n' + ' echo Please rename it back to %(basename)s and try again.\n' + ' exec false\n' + 'fi\n' + + ) % locals() + + if not self.dry_run: + f = open(self.egg_output, 'w') + f.write(header) + f.close() + return 'a' + + + def copy_metadata_to(self, target_dir): prefix = os.path.join(self.egg_info,'') for path in self.ei_cmd.filelist.files: @@ -415,7 +456,9 @@ INSTALL_DIRECTORY_ATTRS = [ 'install_lib', 'install_dir', 'install_data', 'install_base' ] -def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0, compress=None): +def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None, + mode='w' +): """Create a zip file from all the files under 'base_dir'. The output zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" Python module (if available) or the InfoZIP "zip" utility (if installed @@ -426,7 +469,7 @@ def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0, compress=None): mkpath(os.path.dirname(zip_filename), dry_run=dry_run) log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - def visit (z, dirname, names): + def visit(z, dirname, names): for name in names: path = os.path.normpath(os.path.join(dirname, name)) if os.path.isfile(path): @@ -440,12 +483,10 @@ def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0, compress=None): compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)] if not dry_run: - z = zipfile.ZipFile(zip_filename, "w", compression=compression) + z = zipfile.ZipFile(zip_filename, mode, compression=compression) os.path.walk(base_dir, visit, z) z.close() else: os.path.walk(base_dir, visit, None) - return zip_filename - # diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 147dfc43..b049ce58 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1550,10 +1550,10 @@ def rmtree(path, ignore_errors=False, onerror=auto_chmod): except os.error: onerror(os.rmdir, path, sys.exc_info()) - - - - +def bootstrap(): + # This function is called when setuptools*.egg is run using /bin/sh + import setuptools; argv0 = os.path.dirname(setuptools.__path__[0]) + sys.argv[0] = argv0; sys.argv.append(argv0); main() def main(argv=None, **kw): |