diff options
| author | Jason R. Coombs <jaraco@jaraco.com> | 2014-02-11 22:55:49 -0500 |
|---|---|---|
| committer | Jason R. Coombs <jaraco@jaraco.com> | 2014-02-11 22:55:49 -0500 |
| commit | d4da24296de6dbe7fb4a59fdf4f075bebbd29489 (patch) | |
| tree | 8180e49ebdefb2b9364aa047e3a12698c015206f /setuptools/command/bdist_egg.py | |
| parent | c26f710208962f89e384775789e63decf55d1cfd (diff) | |
| parent | 78ff89adc622c57283498fa408d944c6f9b29a30 (diff) | |
| download | python-setuptools-bitbucket-d4da24296de6dbe7fb4a59fdf4f075bebbd29489.tar.gz | |
Merge backout of namespace package __init__ module generation; ref #148.3.0b1
Diffstat (limited to 'setuptools/command/bdist_egg.py')
| -rw-r--r-- | setuptools/command/bdist_egg.py | 297 |
1 files changed, 208 insertions, 89 deletions
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 0a9d9a0c..8f4d44c3 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -3,13 +3,33 @@ Build .egg distributions""" # This module should be kept compatible with Python 2.3 -import os, marshal +import sys, os, marshal from setuptools import Command from distutils.dir_util import remove_tree, mkpath -from distutils.sysconfig import get_python_version, get_python_lib +try: + # Python 2.7 or >=3.2 + from sysconfig import get_path, get_python_version + def _get_purelib(): + return get_path("purelib") +except ImportError: + from distutils.sysconfig import get_python_lib, get_python_version + def _get_purelib(): + return get_python_lib(False) + from distutils import log -from pkg_resources import get_platform, Distribution +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.compat import basestring, next +from setuptools.extension import Library + +def strip_module(filename): + if '.' in filename: + filename = os.path.splitext(filename)[0] + if filename.endswith('module'): + filename = filename[:-6] + return filename def write_stub(resource, pyfile): f = open(pyfile,'w') @@ -19,7 +39,7 @@ def write_stub(resource, pyfile): " import sys, pkg_resources, imp", " __file__ = pkg_resources.resource_filename(__name__,%r)" % resource, - " del __bootstrap__, __loader__", + " __loader__ = None; del __bootstrap__, __loader__", " imp.load_dynamic(__name__,__file__)", "__bootstrap__()", "" # terminal \n @@ -29,16 +49,6 @@ def write_stub(resource, pyfile): - - - - - - - - - - class bdist_egg(Command): description = "create an \"egg\" distribution" @@ -48,7 +58,7 @@ class bdist_egg(Command): "temporary directory for creating the distribution"), ('plat-name=', 'p', "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), + "(default: %s)" % get_build_platform()), ('exclude-source-files', None, "remove all .py files from the generated egg"), ('keep-temp', 'k', @@ -91,7 +101,7 @@ class bdist_egg(Command): def finalize_options(self): - ei_cmd = self.get_finalized_command("egg_info") + ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info") self.egg_info = ei_cmd.egg_info if self.bdist_dir is None: @@ -99,7 +109,7 @@ class bdist_egg(Command): self.bdist_dir = os.path.join(bdist_base, 'egg') if self.plat_name is None: - self.plat_name = get_platform() + self.plat_name = get_build_platform() self.set_undefined_options('bdist',('dist_dir', 'dist_dir')) @@ -125,7 +135,7 @@ class bdist_egg(Command): # Hack for packages that install data to install's --install-lib self.get_finalized_command('install').install_lib = self.bdist_dir - site_packages = os.path.normcase(os.path.realpath(get_python_lib())) + site_packages = os.path.normcase(os.path.realpath(_get_purelib())) old, self.distribution.data_files = self.distribution.data_files,[] for item in old: @@ -165,21 +175,22 @@ class bdist_egg(Command): def run(self): # Generate metadata first self.run_command("egg_info") - # We run install_lib before install_data, because some data hacks # pull their data path from the install_lib command. - log.info("installing library code to %s" % self.bdist_dir) + instcmd = self.get_finalized_command('install') + old_root = instcmd.root; instcmd.root = None + if self.distribution.has_c_libraries() and not self.skip_build: + self.run_command('build_clib') cmd = self.call_command('install_lib', warn_dir=0) + instcmd.root = old_root - ext_outputs = cmd._mutate_outputs( - self.distribution.has_ext_modules(), 'build_ext', 'build_lib', '' - ) + all_outputs, ext_outputs = self.get_ext_outputs() self.stubs = [] to_compile = [] for (p,ext_name) in enumerate(ext_outputs): filename,ext = os.path.splitext(ext_name) - pyfile = os.path.join(self.bdist_dir, filename + '.py') + pyfile = os.path.join(self.bdist_dir, strip_module(filename)+'.py') self.stubs.append(pyfile) log.info("creating stub loader for %s" % ext_name) if not self.dry_run: @@ -189,7 +200,6 @@ class bdist_egg(Command): if to_compile: cmd.byte_compile(to_compile) - if self.distribution.data_files: self.do_install_data() @@ -197,18 +207,19 @@ class bdist_egg(Command): archive_root = self.bdist_dir egg_info = os.path.join(archive_root,'EGG-INFO') self.mkpath(egg_info) - if self.distribution.scripts: script_dir = os.path.join(egg_info, 'scripts') log.info("installing scripts to %s" % script_dir) - self.call_command('install_scripts', install_dir=script_dir) + self.call_command('install_scripts',install_dir=script_dir,no_ep=1) - native_libs = os.path.join(self.egg_info,"native_libs.txt") - if ext_outputs: + self.copy_metadata_to(egg_info) + native_libs = os.path.join(egg_info, "native_libs.txt") + if all_outputs: log.info("writing %s" % native_libs) if not self.dry_run: + ensure_directory(native_libs) libs_file = open(native_libs, 'wt') - libs_file.write('\n'.join(ext_outputs)) + libs_file.write('\n'.join(all_outputs)) libs_file.write('\n') libs_file.close() elif os.path.isfile(native_libs): @@ -216,12 +227,9 @@ class bdist_egg(Command): if not self.dry_run: os.unlink(native_libs) - for filename in os.listdir(self.egg_info): - path = os.path.join(self.egg_info,filename) - if os.path.isfile(path): - self.copy_file(path,os.path.join(egg_info,filename)) - - write_safety_flag(archive_root, self.zip_safe()) + write_safety_flag( + os.path.join(archive_root,'EGG-INFO'), self.zip_safe() + ) if os.path.exists(os.path.join(self.egg_info,'depends.txt')): log.warn( @@ -231,10 +239,10 @@ class bdist_egg(Command): if self.exclude_source_files: self.zap_pyfiles() - + # 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) @@ -244,6 +252,7 @@ class bdist_egg(Command): + def zap_pyfiles(self): log.info("Removing .py files from temporary directory") for base,dirs,files in walk_egg(self.bdist_dir): @@ -262,25 +271,88 @@ class bdist_egg(Command): + 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: + mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run) + f = open(self.egg_output, 'w') + f.write(header) + f.close() + return 'a' + + + def copy_metadata_to(self, target_dir): + "Copy metadata (egg info) to the target_dir" + # normalize the path (so that a forward-slash in egg_info will + # match using startswith below) + norm_egg_info = os.path.normpath(self.egg_info) + prefix = os.path.join(norm_egg_info,'') + for path in self.ei_cmd.filelist.files: + if path.startswith(prefix): + target = os.path.join(target_dir, path[len(prefix):]) + ensure_directory(target) + self.copy_file(path, target) + + def get_ext_outputs(self): + """Get a list of relative paths to C extensions in the output distro""" + + all_outputs = [] + ext_outputs = [] + + paths = {self.bdist_dir:''} + for base, dirs, files in os.walk(self.bdist_dir): + for filename in files: + if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: + all_outputs.append(paths[base]+filename) + for filename in dirs: + paths[os.path.join(base,filename)] = paths[base]+filename+'/' + + if self.distribution.has_ext_modules(): + build_cmd = self.get_finalized_command('build_ext') + for ext in build_cmd.extensions: + if isinstance(ext,Library): + continue + fullname = build_cmd.get_ext_fullname(ext.name) + filename = build_cmd.get_ext_filename(fullname) + if not os.path.basename(filename).startswith('dl-'): + if os.path.exists(os.path.join(self.bdist_dir,filename)): + ext_outputs.append(filename) + + return all_outputs, ext_outputs + + +NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) @@ -288,7 +360,7 @@ class bdist_egg(Command): def walk_egg(egg_dir): """Walk an unpacked egg's contents, skipping the metadata directory""" walker = os.walk(egg_dir) - base,dirs,files = walker.next() + base,dirs,files = next(walker) if 'EGG-INFO' in dirs: dirs.remove('EGG-INFO') yield base,dirs,files @@ -296,6 +368,11 @@ def walk_egg(egg_dir): yield bdf def analyze_egg(egg_dir, stubs): + # check for existing flag in EGG-INFO + for flag,fn in safety_flags.items(): + if os.path.exists(os.path.join(egg_dir,'EGG-INFO',fn)): + return flag + if not can_scan(): return False safe = True for base, dirs, files in walk_egg(egg_dir): for name in files: @@ -304,27 +381,22 @@ def analyze_egg(egg_dir, stubs): elif name.endswith('.pyc') or name.endswith('.pyo'): # always scan, even if we already know we're not safe safe = scan_module(egg_dir, base, name, stubs) and safe - '''elif safe: - log.warn( - "Distribution contains data or extensions; assuming " - "it's unsafe (set zip_safe=True in setup() to change" - ) - safe = False # XXX''' return safe def write_safety_flag(egg_dir, safe): - # Write a zip safety flag file - flag = safe and 'zip-safe' or 'not-zip-safe' - open(os.path.join(egg_dir,'EGG-INFO',flag),'w').close() - - - - - - - - - + # Write or remove zip safety flag file(s) + for flag,fn in safety_flags.items(): + fn = os.path.join(egg_dir, fn) + if os.path.exists(fn): + if safe is None or bool(safe) != flag: + os.unlink(fn) + elif safe is not None and bool(safe)==flag: + f=open(fn,'wt'); f.write('\n'); f.close() + +safety_flags = { + True: 'zip-safe', + False: 'not-zip-safe', +} def scan_module(egg_dir, base, name, stubs): """Check whether module possibly uses unsafe-for-zipfile stuff""" @@ -334,8 +406,12 @@ def scan_module(egg_dir, base, name, stubs): return True # Extension module pkg = base[len(egg_dir)+1:].replace(os.sep,'.') module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0] - f = open(filename,'rb'); f.read(8) # skip magic & date - code = marshal.load(f); f.close() + if sys.version_info < (3, 3): + skip = 8 # skip magic & date + else: + skip = 12 # skip magic & date & file size + f = open(filename,'rb'); f.read(skip) + code = marshal.load(f); f.close() safe = True symbols = dict.fromkeys(iter_symbols(code)) for bad in ['__file__', '__path__']: @@ -352,7 +428,7 @@ def scan_module(egg_dir, base, name, stubs): log.warn("%s: module MAY be using inspect.%s", module, bad) safe = False if '__name__' in symbols and '__main__' in symbols and '.' not in module: - if get_python_version()>="2.4": + if sys.version[:3]=="2.4": # -m works w/zipfiles in 2.5 log.warn("%s: top-level module may be 'python -m' script", module) safe = False return safe @@ -367,6 +443,47 @@ def iter_symbols(code): for name in iter_symbols(const): yield name +def can_scan(): + if not sys.platform.startswith('java') and sys.platform != 'cli': + # CPython, PyPy, etc. + return True + log.warn("Unable to analyze compiled code on this platform.") + log.warn("Please ask the author to include a 'zip_safe'" + " setting (either True or False) in the package's setup.py") + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # Attribute names of options for commands that might need to be convinced to # install to the egg build directory @@ -374,8 +491,9 @@ INSTALL_DIRECTORY_ATTRS = [ 'install_lib', 'install_dir', 'install_data', 'install_base' ] - -def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0): +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 @@ -384,10 +502,9 @@ def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0): """ import zipfile 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): @@ -396,15 +513,17 @@ def make_zipfile (zip_filename, base_dir, verbose=0, dry_run=0): z.write(path, p) log.debug("adding '%s'" % p) + if compress is None: + compress = (sys.version>="2.4") # avoid 2.3 zipimport bug when 64 bits + + compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)] if not dry_run: - z = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) - os.path.walk(base_dir, visit, z) + z = zipfile.ZipFile(zip_filename, mode, compression=compression) + for dirname, dirs, files in os.walk(base_dir): + visit(z, dirname, files) z.close() else: - os.path.walk(base_dir, visit, None) - + for dirname, dirs, files in os.walk(base_dir): + visit(None, dirname, files) return zip_filename - - - +# |
