summaryrefslogtreecommitdiff
path: root/setuptools/command/bdist_egg.py
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2014-02-11 22:55:49 -0500
committerJason R. Coombs <jaraco@jaraco.com>2014-02-11 22:55:49 -0500
commitd4da24296de6dbe7fb4a59fdf4f075bebbd29489 (patch)
tree8180e49ebdefb2b9364aa047e3a12698c015206f /setuptools/command/bdist_egg.py
parentc26f710208962f89e384775789e63decf55d1cfd (diff)
parent78ff89adc622c57283498fa408d944c6f9b29a30 (diff)
downloadpython-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.py297
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
-
-
-
+#