Index: recipe_engine/third_party/setuptools/command/bdist_egg.py |
diff --git a/recipe_engine/third_party/setuptools/command/bdist_egg.py b/recipe_engine/third_party/setuptools/command/bdist_egg.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..34fdeec21fc0c224e97edf3804248a4e52bb4f80 |
--- /dev/null |
+++ b/recipe_engine/third_party/setuptools/command/bdist_egg.py |
@@ -0,0 +1,479 @@ |
+"""setuptools.command.bdist_egg |
+ |
+Build .egg distributions""" |
+ |
+# This module should be kept compatible with Python 2.3 |
+from distutils.errors import DistutilsSetupError |
+from distutils.dir_util import remove_tree, mkpath |
+from distutils import log |
+from types import CodeType |
+import sys |
+import os |
+import marshal |
+import textwrap |
+ |
+from pkg_resources import get_build_platform, Distribution, ensure_directory |
+from pkg_resources import EntryPoint |
+from setuptools.compat import basestring |
+from setuptools.extension import Library |
+from setuptools import Command |
+ |
+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) |
+ |
+ |
+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): |
+ _stub_template = textwrap.dedent(""" |
+ def __bootstrap__(): |
+ global __bootstrap__, __loader__, __file__ |
+ import sys, pkg_resources, imp |
+ __file__ = pkg_resources.resource_filename(__name__, %r) |
+ __loader__ = None; del __bootstrap__, __loader__ |
+ imp.load_dynamic(__name__,__file__) |
+ __bootstrap__() |
+ """).lstrip() |
+ with open(pyfile, 'w') as f: |
+ f.write(_stub_template % resource) |
+ |
+ |
+class bdist_egg(Command): |
+ description = "create an \"egg\" distribution" |
+ |
+ user_options = [ |
+ ('bdist-dir=', 'b', |
+ "temporary directory for creating the distribution"), |
+ ('plat-name=', 'p', "platform name to embed in generated filenames " |
+ "(default: %s)" % get_build_platform()), |
+ ('exclude-source-files', None, |
+ "remove all .py files from the generated egg"), |
+ ('keep-temp', 'k', |
+ "keep the pseudo-installation tree around after " + |
+ "creating the distribution archive"), |
+ ('dist-dir=', 'd', |
+ "directory to put final built distributions in"), |
+ ('skip-build', None, |
+ "skip rebuilding everything (for testing/debugging)"), |
+ ] |
+ |
+ boolean_options = [ |
+ 'keep-temp', 'skip-build', 'exclude-source-files' |
+ ] |
+ |
+ def initialize_options(self): |
+ self.bdist_dir = None |
+ self.plat_name = None |
+ self.keep_temp = 0 |
+ self.dist_dir = None |
+ self.skip_build = 0 |
+ self.egg_output = None |
+ self.exclude_source_files = None |
+ |
+ def finalize_options(self): |
+ ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info") |
+ self.egg_info = ei_cmd.egg_info |
+ |
+ if self.bdist_dir is None: |
+ bdist_base = self.get_finalized_command('bdist').bdist_base |
+ self.bdist_dir = os.path.join(bdist_base, 'egg') |
+ |
+ if self.plat_name is None: |
+ self.plat_name = get_build_platform() |
+ |
+ self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) |
+ |
+ if self.egg_output is None: |
+ |
+ # Compute filename of the output egg |
+ basename = Distribution( |
+ None, None, ei_cmd.egg_name, ei_cmd.egg_version, |
+ get_python_version(), |
+ self.distribution.has_ext_modules() and self.plat_name |
+ ).egg_name() |
+ |
+ self.egg_output = os.path.join(self.dist_dir, basename + '.egg') |
+ |
+ def do_install_data(self): |
+ # 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_purelib())) |
+ old, self.distribution.data_files = self.distribution.data_files, [] |
+ |
+ for item in old: |
+ if isinstance(item, tuple) and len(item) == 2: |
+ if os.path.isabs(item[0]): |
+ realpath = os.path.realpath(item[0]) |
+ normalized = os.path.normcase(realpath) |
+ if normalized == site_packages or normalized.startswith( |
+ site_packages + os.sep |
+ ): |
+ item = realpath[len(site_packages) + 1:], item[1] |
+ # XXX else: raise ??? |
+ self.distribution.data_files.append(item) |
+ |
+ try: |
+ log.info("installing package data to %s" % self.bdist_dir) |
+ self.call_command('install_data', force=0, root=None) |
+ finally: |
+ self.distribution.data_files = old |
+ |
+ def get_outputs(self): |
+ return [self.egg_output] |
+ |
+ def call_command(self, cmdname, **kw): |
+ """Invoke reinitialized command `cmdname` with keyword args""" |
+ for dirname in INSTALL_DIRECTORY_ATTRS: |
+ kw.setdefault(dirname, self.bdist_dir) |
+ kw.setdefault('skip_build', self.skip_build) |
+ kw.setdefault('dry_run', self.dry_run) |
+ cmd = self.reinitialize_command(cmdname, **kw) |
+ self.run_command(cmdname) |
+ return cmd |
+ |
+ 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 |
+ |
+ 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, strip_module(filename) + |
+ '.py') |
+ self.stubs.append(pyfile) |
+ log.info("creating stub loader for %s" % ext_name) |
+ if not self.dry_run: |
+ write_stub(os.path.basename(ext_name), pyfile) |
+ to_compile.append(pyfile) |
+ ext_outputs[p] = ext_name.replace(os.sep, '/') |
+ |
+ if to_compile: |
+ cmd.byte_compile(to_compile) |
+ if self.distribution.data_files: |
+ self.do_install_data() |
+ |
+ # Make the EGG-INFO directory |
+ 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, |
+ no_ep=1) |
+ |
+ 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(all_outputs)) |
+ libs_file.write('\n') |
+ libs_file.close() |
+ elif os.path.isfile(native_libs): |
+ log.info("removing %s" % native_libs) |
+ if not self.dry_run: |
+ os.unlink(native_libs) |
+ |
+ 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( |
+ "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n" |
+ "Use the install_requires/extras_require setup() args instead." |
+ ) |
+ |
+ 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, mode=self.gen_header()) |
+ if not self.keep_temp: |
+ remove_tree(self.bdist_dir, dry_run=self.dry_run) |
+ |
+ # Add to 'Distribution.dist_files' so that the "upload" command works |
+ getattr(self.distribution, 'dist_files', []).append( |
+ ('bdist_egg', get_python_version(), self.egg_output)) |
+ |
+ def zap_pyfiles(self): |
+ log.info("Removing .py files from temporary directory") |
+ for base, dirs, files in walk_egg(self.bdist_dir): |
+ for name in files: |
+ if name.endswith('.py'): |
+ path = os.path.join(base, name) |
+ log.debug("Deleting %s", path) |
+ os.unlink(path) |
+ |
+ def zip_safe(self): |
+ safe = getattr(self.distribution, 'zip_safe', None) |
+ if safe is not None: |
+ return safe |
+ log.warn("zip_safe flag not set; analyzing archive contents...") |
+ return analyze_egg(self.bdist_dir, self.stubs) |
+ |
+ 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()) |
+ |
+ |
+def walk_egg(egg_dir): |
+ """Walk an unpacked egg's contents, skipping the metadata directory""" |
+ walker = os.walk(egg_dir) |
+ base, dirs, files = next(walker) |
+ if 'EGG-INFO' in dirs: |
+ dirs.remove('EGG-INFO') |
+ yield base, dirs, files |
+ for bdf in walker: |
+ 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: |
+ if name.endswith('.py') or name.endswith('.pyw'): |
+ continue |
+ 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 |
+ return safe |
+ |
+ |
+def write_safety_flag(egg_dir, safe): |
+ # 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""" |
+ |
+ filename = os.path.join(base, name) |
+ if filename[:-1] in stubs: |
+ return True # Extension module |
+ pkg = base[len(egg_dir) + 1:].replace(os.sep, '.') |
+ module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0] |
+ 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__']: |
+ if bad in symbols: |
+ log.warn("%s: module references %s", module, bad) |
+ safe = False |
+ if 'inspect' in symbols: |
+ for bad in [ |
+ 'getsource', 'getabsfile', 'getsourcefile', 'getfile' |
+ 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo', |
+ 'getinnerframes', 'getouterframes', 'stack', 'trace' |
+ ]: |
+ if bad in symbols: |
+ 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 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 |
+ |
+ |
+def iter_symbols(code): |
+ """Yield names and strings used by `code` and its nested code objects""" |
+ for name in code.co_names: |
+ yield name |
+ for const in code.co_consts: |
+ if isinstance(const, basestring): |
+ yield const |
+ elif isinstance(const, CodeType): |
+ 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 |
+ |
+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, |
+ 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 |
+ and found on the default search path). If neither tool is available, |
+ raises DistutilsExecError. Returns the name of the output zip file. |
+ """ |
+ 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): |
+ for name in names: |
+ path = os.path.normpath(os.path.join(dirname, name)) |
+ if os.path.isfile(path): |
+ p = path[len(base_dir) + 1:] |
+ if not dry_run: |
+ z.write(path, p) |
+ log.debug("adding '%s'" % p) |
+ |
+ if compress is None: |
+ # avoid 2.3 zipimport bug when 64 bits |
+ compress = (sys.version >= "2.4") |
+ |
+ compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)] |
+ if not dry_run: |
+ z = zipfile.ZipFile(zip_filename, mode, compression=compression) |
+ for dirname, dirs, files in os.walk(base_dir): |
+ visit(z, dirname, files) |
+ z.close() |
+ else: |
+ for dirname, dirs, files in os.walk(base_dir): |
+ visit(None, dirname, files) |
+ return zip_filename |