mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-04 16:15:25 +00:00
4789ca749c
We're trying to stay up to date on virtualenv releases so we don't fall too far behind and so we can leverage new features, such as wheels. This virtualenv release features upgrades to pip (1.4.1 to 1.5.4) and setuptools (0.9.8 to 2.2). These may fix bugs people are encountering in the wild. Tarball obtained from https://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.11.4.tar.gz#md5=9accc2d3f0ec1da479ce2c3d1fdff06e. the egg-info directory was removed (consistent with previous virtualenv upgrades). --HG-- extra : rebase_source : 0cef066c747d51b8f47e4218353dd47f391ff831 extra : amend_source : 08a255f64093e9280da04a4bead2db3e6f9abe42
2343 lines
96 KiB
Python
Executable File
2343 lines
96 KiB
Python
Executable File
#!/usr/bin/env python
|
|
"""Create a "virtual" Python installation
|
|
"""
|
|
|
|
__version__ = "1.11.4"
|
|
virtualenv_version = __version__ # legacy
|
|
|
|
import base64
|
|
import sys
|
|
import os
|
|
import codecs
|
|
import optparse
|
|
import re
|
|
import shutil
|
|
import logging
|
|
import tempfile
|
|
import zlib
|
|
import errno
|
|
import glob
|
|
import distutils.sysconfig
|
|
from distutils.util import strtobool
|
|
import struct
|
|
import subprocess
|
|
import tarfile
|
|
|
|
if sys.version_info < (2, 6):
|
|
print('ERROR: %s' % sys.exc_info()[1])
|
|
print('ERROR: this script requires Python 2.6 or greater.')
|
|
sys.exit(101)
|
|
|
|
try:
|
|
set
|
|
except NameError:
|
|
from sets import Set as set
|
|
try:
|
|
basestring
|
|
except NameError:
|
|
basestring = str
|
|
|
|
try:
|
|
import ConfigParser
|
|
except ImportError:
|
|
import configparser as ConfigParser
|
|
|
|
join = os.path.join
|
|
py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
|
|
|
|
is_jython = sys.platform.startswith('java')
|
|
is_pypy = hasattr(sys, 'pypy_version_info')
|
|
is_win = (sys.platform == 'win32')
|
|
is_cygwin = (sys.platform == 'cygwin')
|
|
is_darwin = (sys.platform == 'darwin')
|
|
abiflags = getattr(sys, 'abiflags', '')
|
|
|
|
user_dir = os.path.expanduser('~')
|
|
if is_win:
|
|
default_storage_dir = os.path.join(user_dir, 'virtualenv')
|
|
else:
|
|
default_storage_dir = os.path.join(user_dir, '.virtualenv')
|
|
default_config_file = os.path.join(default_storage_dir, 'virtualenv.ini')
|
|
|
|
if is_pypy:
|
|
expected_exe = 'pypy'
|
|
elif is_jython:
|
|
expected_exe = 'jython'
|
|
else:
|
|
expected_exe = 'python'
|
|
|
|
# Return a mapping of version -> Python executable
|
|
# Only provided for Windows, where the information in the registry is used
|
|
if not is_win:
|
|
def get_installed_pythons():
|
|
return {}
|
|
else:
|
|
try:
|
|
import winreg
|
|
except ImportError:
|
|
import _winreg as winreg
|
|
|
|
def get_installed_pythons():
|
|
python_core = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE,
|
|
"Software\\Python\\PythonCore")
|
|
i = 0
|
|
versions = []
|
|
while True:
|
|
try:
|
|
versions.append(winreg.EnumKey(python_core, i))
|
|
i = i + 1
|
|
except WindowsError:
|
|
break
|
|
exes = dict()
|
|
for ver in versions:
|
|
path = winreg.QueryValue(python_core, "%s\\InstallPath" % ver)
|
|
exes[ver] = join(path, "python.exe")
|
|
|
|
winreg.CloseKey(python_core)
|
|
|
|
# Add the major versions
|
|
# Sort the keys, then repeatedly update the major version entry
|
|
# Last executable (i.e., highest version) wins with this approach
|
|
for ver in sorted(exes):
|
|
exes[ver[0]] = exes[ver]
|
|
|
|
return exes
|
|
|
|
REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath',
|
|
'fnmatch', 'locale', 'encodings', 'codecs',
|
|
'stat', 'UserDict', 'readline', 'copy_reg', 'types',
|
|
're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile',
|
|
'zlib']
|
|
|
|
REQUIRED_FILES = ['lib-dynload', 'config']
|
|
|
|
majver, minver = sys.version_info[:2]
|
|
if majver == 2:
|
|
if minver >= 6:
|
|
REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc'])
|
|
if minver >= 7:
|
|
REQUIRED_MODULES.extend(['_weakrefset'])
|
|
if minver <= 3:
|
|
REQUIRED_MODULES.extend(['sets', '__future__'])
|
|
elif majver == 3:
|
|
# Some extra modules are needed for Python 3, but different ones
|
|
# for different versions.
|
|
REQUIRED_MODULES.extend(['_abcoll', 'warnings', 'linecache', 'abc', 'io',
|
|
'_weakrefset', 'copyreg', 'tempfile', 'random',
|
|
'__future__', 'collections', 'keyword', 'tarfile',
|
|
'shutil', 'struct', 'copy', 'tokenize', 'token',
|
|
'functools', 'heapq', 'bisect', 'weakref',
|
|
'reprlib'])
|
|
if minver >= 2:
|
|
REQUIRED_FILES[-1] = 'config-%s' % majver
|
|
if minver >= 3:
|
|
import sysconfig
|
|
platdir = sysconfig.get_config_var('PLATDIR')
|
|
REQUIRED_FILES.append(platdir)
|
|
# The whole list of 3.3 modules is reproduced below - the current
|
|
# uncommented ones are required for 3.3 as of now, but more may be
|
|
# added as 3.3 development continues.
|
|
REQUIRED_MODULES.extend([
|
|
#"aifc",
|
|
#"antigravity",
|
|
#"argparse",
|
|
#"ast",
|
|
#"asynchat",
|
|
#"asyncore",
|
|
"base64",
|
|
#"bdb",
|
|
#"binhex",
|
|
#"bisect",
|
|
#"calendar",
|
|
#"cgi",
|
|
#"cgitb",
|
|
#"chunk",
|
|
#"cmd",
|
|
#"codeop",
|
|
#"code",
|
|
#"colorsys",
|
|
#"_compat_pickle",
|
|
#"compileall",
|
|
#"concurrent",
|
|
#"configparser",
|
|
#"contextlib",
|
|
#"cProfile",
|
|
#"crypt",
|
|
#"csv",
|
|
#"ctypes",
|
|
#"curses",
|
|
#"datetime",
|
|
#"dbm",
|
|
#"decimal",
|
|
#"difflib",
|
|
#"dis",
|
|
#"doctest",
|
|
#"dummy_threading",
|
|
"_dummy_thread",
|
|
#"email",
|
|
#"filecmp",
|
|
#"fileinput",
|
|
#"formatter",
|
|
#"fractions",
|
|
#"ftplib",
|
|
#"functools",
|
|
#"getopt",
|
|
#"getpass",
|
|
#"gettext",
|
|
#"glob",
|
|
#"gzip",
|
|
"hashlib",
|
|
#"heapq",
|
|
"hmac",
|
|
#"html",
|
|
#"http",
|
|
#"idlelib",
|
|
#"imaplib",
|
|
#"imghdr",
|
|
"imp",
|
|
"importlib",
|
|
#"inspect",
|
|
#"json",
|
|
#"lib2to3",
|
|
#"logging",
|
|
#"macpath",
|
|
#"macurl2path",
|
|
#"mailbox",
|
|
#"mailcap",
|
|
#"_markupbase",
|
|
#"mimetypes",
|
|
#"modulefinder",
|
|
#"multiprocessing",
|
|
#"netrc",
|
|
#"nntplib",
|
|
#"nturl2path",
|
|
#"numbers",
|
|
#"opcode",
|
|
#"optparse",
|
|
#"os2emxpath",
|
|
#"pdb",
|
|
#"pickle",
|
|
#"pickletools",
|
|
#"pipes",
|
|
#"pkgutil",
|
|
#"platform",
|
|
#"plat-linux2",
|
|
#"plistlib",
|
|
#"poplib",
|
|
#"pprint",
|
|
#"profile",
|
|
#"pstats",
|
|
#"pty",
|
|
#"pyclbr",
|
|
#"py_compile",
|
|
#"pydoc_data",
|
|
#"pydoc",
|
|
#"_pyio",
|
|
#"queue",
|
|
#"quopri",
|
|
#"reprlib",
|
|
"rlcompleter",
|
|
#"runpy",
|
|
#"sched",
|
|
#"shelve",
|
|
#"shlex",
|
|
#"smtpd",
|
|
#"smtplib",
|
|
#"sndhdr",
|
|
#"socket",
|
|
#"socketserver",
|
|
#"sqlite3",
|
|
#"ssl",
|
|
#"stringprep",
|
|
#"string",
|
|
#"_strptime",
|
|
#"subprocess",
|
|
#"sunau",
|
|
#"symbol",
|
|
#"symtable",
|
|
#"sysconfig",
|
|
#"tabnanny",
|
|
#"telnetlib",
|
|
#"test",
|
|
#"textwrap",
|
|
#"this",
|
|
#"_threading_local",
|
|
#"threading",
|
|
#"timeit",
|
|
#"tkinter",
|
|
#"tokenize",
|
|
#"token",
|
|
#"traceback",
|
|
#"trace",
|
|
#"tty",
|
|
#"turtledemo",
|
|
#"turtle",
|
|
#"unittest",
|
|
#"urllib",
|
|
#"uuid",
|
|
#"uu",
|
|
#"wave",
|
|
#"weakref",
|
|
#"webbrowser",
|
|
#"wsgiref",
|
|
#"xdrlib",
|
|
#"xml",
|
|
#"xmlrpc",
|
|
#"zipfile",
|
|
])
|
|
if minver >= 4:
|
|
REQUIRED_MODULES.extend([
|
|
'operator',
|
|
'_collections_abc',
|
|
'_bootlocale',
|
|
])
|
|
|
|
if is_pypy:
|
|
# these are needed to correctly display the exceptions that may happen
|
|
# during the bootstrap
|
|
REQUIRED_MODULES.extend(['traceback', 'linecache'])
|
|
|
|
class Logger(object):
|
|
|
|
"""
|
|
Logging object for use in command-line script. Allows ranges of
|
|
levels, to avoid some redundancy of displayed information.
|
|
"""
|
|
|
|
DEBUG = logging.DEBUG
|
|
INFO = logging.INFO
|
|
NOTIFY = (logging.INFO+logging.WARN)/2
|
|
WARN = WARNING = logging.WARN
|
|
ERROR = logging.ERROR
|
|
FATAL = logging.FATAL
|
|
|
|
LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL]
|
|
|
|
def __init__(self, consumers):
|
|
self.consumers = consumers
|
|
self.indent = 0
|
|
self.in_progress = None
|
|
self.in_progress_hanging = False
|
|
|
|
def debug(self, msg, *args, **kw):
|
|
self.log(self.DEBUG, msg, *args, **kw)
|
|
def info(self, msg, *args, **kw):
|
|
self.log(self.INFO, msg, *args, **kw)
|
|
def notify(self, msg, *args, **kw):
|
|
self.log(self.NOTIFY, msg, *args, **kw)
|
|
def warn(self, msg, *args, **kw):
|
|
self.log(self.WARN, msg, *args, **kw)
|
|
def error(self, msg, *args, **kw):
|
|
self.log(self.ERROR, msg, *args, **kw)
|
|
def fatal(self, msg, *args, **kw):
|
|
self.log(self.FATAL, msg, *args, **kw)
|
|
def log(self, level, msg, *args, **kw):
|
|
if args:
|
|
if kw:
|
|
raise TypeError(
|
|
"You may give positional or keyword arguments, not both")
|
|
args = args or kw
|
|
rendered = None
|
|
for consumer_level, consumer in self.consumers:
|
|
if self.level_matches(level, consumer_level):
|
|
if (self.in_progress_hanging
|
|
and consumer in (sys.stdout, sys.stderr)):
|
|
self.in_progress_hanging = False
|
|
sys.stdout.write('\n')
|
|
sys.stdout.flush()
|
|
if rendered is None:
|
|
if args:
|
|
rendered = msg % args
|
|
else:
|
|
rendered = msg
|
|
rendered = ' '*self.indent + rendered
|
|
if hasattr(consumer, 'write'):
|
|
consumer.write(rendered+'\n')
|
|
else:
|
|
consumer(rendered)
|
|
|
|
def start_progress(self, msg):
|
|
assert not self.in_progress, (
|
|
"Tried to start_progress(%r) while in_progress %r"
|
|
% (msg, self.in_progress))
|
|
if self.level_matches(self.NOTIFY, self._stdout_level()):
|
|
sys.stdout.write(msg)
|
|
sys.stdout.flush()
|
|
self.in_progress_hanging = True
|
|
else:
|
|
self.in_progress_hanging = False
|
|
self.in_progress = msg
|
|
|
|
def end_progress(self, msg='done.'):
|
|
assert self.in_progress, (
|
|
"Tried to end_progress without start_progress")
|
|
if self.stdout_level_matches(self.NOTIFY):
|
|
if not self.in_progress_hanging:
|
|
# Some message has been printed out since start_progress
|
|
sys.stdout.write('...' + self.in_progress + msg + '\n')
|
|
sys.stdout.flush()
|
|
else:
|
|
sys.stdout.write(msg + '\n')
|
|
sys.stdout.flush()
|
|
self.in_progress = None
|
|
self.in_progress_hanging = False
|
|
|
|
def show_progress(self):
|
|
"""If we are in a progress scope, and no log messages have been
|
|
shown, write out another '.'"""
|
|
if self.in_progress_hanging:
|
|
sys.stdout.write('.')
|
|
sys.stdout.flush()
|
|
|
|
def stdout_level_matches(self, level):
|
|
"""Returns true if a message at this level will go to stdout"""
|
|
return self.level_matches(level, self._stdout_level())
|
|
|
|
def _stdout_level(self):
|
|
"""Returns the level that stdout runs at"""
|
|
for level, consumer in self.consumers:
|
|
if consumer is sys.stdout:
|
|
return level
|
|
return self.FATAL
|
|
|
|
def level_matches(self, level, consumer_level):
|
|
"""
|
|
>>> l = Logger([])
|
|
>>> l.level_matches(3, 4)
|
|
False
|
|
>>> l.level_matches(3, 2)
|
|
True
|
|
>>> l.level_matches(slice(None, 3), 3)
|
|
False
|
|
>>> l.level_matches(slice(None, 3), 2)
|
|
True
|
|
>>> l.level_matches(slice(1, 3), 1)
|
|
True
|
|
>>> l.level_matches(slice(2, 3), 1)
|
|
False
|
|
"""
|
|
if isinstance(level, slice):
|
|
start, stop = level.start, level.stop
|
|
if start is not None and start > consumer_level:
|
|
return False
|
|
if stop is not None and stop <= consumer_level:
|
|
return False
|
|
return True
|
|
else:
|
|
return level >= consumer_level
|
|
|
|
#@classmethod
|
|
def level_for_integer(cls, level):
|
|
levels = cls.LEVELS
|
|
if level < 0:
|
|
return levels[0]
|
|
if level >= len(levels):
|
|
return levels[-1]
|
|
return levels[level]
|
|
|
|
level_for_integer = classmethod(level_for_integer)
|
|
|
|
# create a silent logger just to prevent this from being undefined
|
|
# will be overridden with requested verbosity main() is called.
|
|
logger = Logger([(Logger.LEVELS[-1], sys.stdout)])
|
|
|
|
def mkdir(path):
|
|
if not os.path.exists(path):
|
|
logger.info('Creating %s', path)
|
|
os.makedirs(path)
|
|
else:
|
|
logger.info('Directory %s already exists', path)
|
|
|
|
def copyfileordir(src, dest, symlink=True):
|
|
if os.path.isdir(src):
|
|
shutil.copytree(src, dest, symlink)
|
|
else:
|
|
shutil.copy2(src, dest)
|
|
|
|
def copyfile(src, dest, symlink=True):
|
|
if not os.path.exists(src):
|
|
# Some bad symlink in the src
|
|
logger.warn('Cannot find file %s (bad symlink)', src)
|
|
return
|
|
if os.path.exists(dest):
|
|
logger.debug('File %s already exists', dest)
|
|
return
|
|
if not os.path.exists(os.path.dirname(dest)):
|
|
logger.info('Creating parent directories for %s', os.path.dirname(dest))
|
|
os.makedirs(os.path.dirname(dest))
|
|
if not os.path.islink(src):
|
|
srcpath = os.path.abspath(src)
|
|
else:
|
|
srcpath = os.readlink(src)
|
|
if symlink and hasattr(os, 'symlink') and not is_win:
|
|
logger.info('Symlinking %s', dest)
|
|
try:
|
|
os.symlink(srcpath, dest)
|
|
except (OSError, NotImplementedError):
|
|
logger.info('Symlinking failed, copying to %s', dest)
|
|
copyfileordir(src, dest, symlink)
|
|
else:
|
|
logger.info('Copying to %s', dest)
|
|
copyfileordir(src, dest, symlink)
|
|
|
|
def writefile(dest, content, overwrite=True):
|
|
if not os.path.exists(dest):
|
|
logger.info('Writing %s', dest)
|
|
f = open(dest, 'wb')
|
|
f.write(content.encode('utf-8'))
|
|
f.close()
|
|
return
|
|
else:
|
|
f = open(dest, 'rb')
|
|
c = f.read()
|
|
f.close()
|
|
if c != content.encode("utf-8"):
|
|
if not overwrite:
|
|
logger.notify('File %s exists with different content; not overwriting', dest)
|
|
return
|
|
logger.notify('Overwriting %s with new content', dest)
|
|
f = open(dest, 'wb')
|
|
f.write(content.encode('utf-8'))
|
|
f.close()
|
|
else:
|
|
logger.info('Content %s already in place', dest)
|
|
|
|
def rmtree(dir):
|
|
if os.path.exists(dir):
|
|
logger.notify('Deleting tree %s', dir)
|
|
shutil.rmtree(dir)
|
|
else:
|
|
logger.info('Do not need to delete %s; already gone', dir)
|
|
|
|
def make_exe(fn):
|
|
if hasattr(os, 'chmod'):
|
|
oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777
|
|
newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777
|
|
os.chmod(fn, newmode)
|
|
logger.info('Changed mode of %s to %s', fn, oct(newmode))
|
|
|
|
def _find_file(filename, dirs):
|
|
for dir in reversed(dirs):
|
|
files = glob.glob(os.path.join(dir, filename))
|
|
if files and os.path.isfile(files[0]):
|
|
return True, files[0]
|
|
return False, filename
|
|
|
|
def file_search_dirs():
|
|
here = os.path.dirname(os.path.abspath(__file__))
|
|
dirs = ['.', here,
|
|
join(here, 'virtualenv_support')]
|
|
if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv':
|
|
# Probably some boot script; just in case virtualenv is installed...
|
|
try:
|
|
import virtualenv
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support'))
|
|
return [d for d in dirs if os.path.isdir(d)]
|
|
|
|
|
|
class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter):
|
|
"""
|
|
Custom help formatter for use in ConfigOptionParser that updates
|
|
the defaults before expanding them, allowing them to show up correctly
|
|
in the help listing
|
|
"""
|
|
def expand_default(self, option):
|
|
if self.parser is not None:
|
|
self.parser.update_defaults(self.parser.defaults)
|
|
return optparse.IndentedHelpFormatter.expand_default(self, option)
|
|
|
|
|
|
class ConfigOptionParser(optparse.OptionParser):
|
|
"""
|
|
Custom option parser which updates its defaults by checking the
|
|
configuration files and environmental variables
|
|
"""
|
|
def __init__(self, *args, **kwargs):
|
|
self.config = ConfigParser.RawConfigParser()
|
|
self.files = self.get_config_files()
|
|
self.config.read(self.files)
|
|
optparse.OptionParser.__init__(self, *args, **kwargs)
|
|
|
|
def get_config_files(self):
|
|
config_file = os.environ.get('VIRTUALENV_CONFIG_FILE', False)
|
|
if config_file and os.path.exists(config_file):
|
|
return [config_file]
|
|
return [default_config_file]
|
|
|
|
def update_defaults(self, defaults):
|
|
"""
|
|
Updates the given defaults with values from the config files and
|
|
the environ. Does a little special handling for certain types of
|
|
options (lists).
|
|
"""
|
|
# Then go and look for the other sources of configuration:
|
|
config = {}
|
|
# 1. config files
|
|
config.update(dict(self.get_config_section('virtualenv')))
|
|
# 2. environmental variables
|
|
config.update(dict(self.get_environ_vars()))
|
|
# Then set the options with those values
|
|
for key, val in config.items():
|
|
key = key.replace('_', '-')
|
|
if not key.startswith('--'):
|
|
key = '--%s' % key # only prefer long opts
|
|
option = self.get_option(key)
|
|
if option is not None:
|
|
# ignore empty values
|
|
if not val:
|
|
continue
|
|
# handle multiline configs
|
|
if option.action == 'append':
|
|
val = val.split()
|
|
else:
|
|
option.nargs = 1
|
|
if option.action == 'store_false':
|
|
val = not strtobool(val)
|
|
elif option.action in ('store_true', 'count'):
|
|
val = strtobool(val)
|
|
try:
|
|
val = option.convert_value(key, val)
|
|
except optparse.OptionValueError:
|
|
e = sys.exc_info()[1]
|
|
print("An error occured during configuration: %s" % e)
|
|
sys.exit(3)
|
|
defaults[option.dest] = val
|
|
return defaults
|
|
|
|
def get_config_section(self, name):
|
|
"""
|
|
Get a section of a configuration
|
|
"""
|
|
if self.config.has_section(name):
|
|
return self.config.items(name)
|
|
return []
|
|
|
|
def get_environ_vars(self, prefix='VIRTUALENV_'):
|
|
"""
|
|
Returns a generator with all environmental vars with prefix VIRTUALENV
|
|
"""
|
|
for key, val in os.environ.items():
|
|
if key.startswith(prefix):
|
|
yield (key.replace(prefix, '').lower(), val)
|
|
|
|
def get_default_values(self):
|
|
"""
|
|
Overridding to make updating the defaults after instantiation of
|
|
the option parser possible, update_defaults() does the dirty work.
|
|
"""
|
|
if not self.process_default_values:
|
|
# Old, pre-Optik 1.5 behaviour.
|
|
return optparse.Values(self.defaults)
|
|
|
|
defaults = self.update_defaults(self.defaults.copy()) # ours
|
|
for option in self._get_all_options():
|
|
default = defaults.get(option.dest)
|
|
if isinstance(default, basestring):
|
|
opt_str = option.get_opt_string()
|
|
defaults[option.dest] = option.check_value(opt_str, default)
|
|
return optparse.Values(defaults)
|
|
|
|
|
|
def main():
|
|
parser = ConfigOptionParser(
|
|
version=virtualenv_version,
|
|
usage="%prog [OPTIONS] DEST_DIR",
|
|
formatter=UpdatingDefaultsHelpFormatter())
|
|
|
|
parser.add_option(
|
|
'-v', '--verbose',
|
|
action='count',
|
|
dest='verbose',
|
|
default=0,
|
|
help="Increase verbosity.")
|
|
|
|
parser.add_option(
|
|
'-q', '--quiet',
|
|
action='count',
|
|
dest='quiet',
|
|
default=0,
|
|
help='Decrease verbosity.')
|
|
|
|
parser.add_option(
|
|
'-p', '--python',
|
|
dest='python',
|
|
metavar='PYTHON_EXE',
|
|
help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 '
|
|
'interpreter to create the new environment. The default is the interpreter that '
|
|
'virtualenv was installed with (%s)' % sys.executable)
|
|
|
|
parser.add_option(
|
|
'--clear',
|
|
dest='clear',
|
|
action='store_true',
|
|
help="Clear out the non-root install and start from scratch.")
|
|
|
|
parser.set_defaults(system_site_packages=False)
|
|
parser.add_option(
|
|
'--no-site-packages',
|
|
dest='system_site_packages',
|
|
action='store_false',
|
|
help="DEPRECATED. Retained only for backward compatibility. "
|
|
"Not having access to global site-packages is now the default behavior.")
|
|
|
|
parser.add_option(
|
|
'--system-site-packages',
|
|
dest='system_site_packages',
|
|
action='store_true',
|
|
help="Give the virtual environment access to the global site-packages.")
|
|
|
|
parser.add_option(
|
|
'--always-copy',
|
|
dest='symlink',
|
|
action='store_false',
|
|
default=True,
|
|
help="Always copy files rather than symlinking.")
|
|
|
|
parser.add_option(
|
|
'--unzip-setuptools',
|
|
dest='unzip_setuptools',
|
|
action='store_true',
|
|
help="Unzip Setuptools when installing it.")
|
|
|
|
parser.add_option(
|
|
'--relocatable',
|
|
dest='relocatable',
|
|
action='store_true',
|
|
help='Make an EXISTING virtualenv environment relocatable. '
|
|
'This fixes up scripts and makes all .pth files relative.')
|
|
|
|
parser.add_option(
|
|
'--no-setuptools',
|
|
dest='no_setuptools',
|
|
action='store_true',
|
|
help='Do not install setuptools (or pip) in the new virtualenv.')
|
|
|
|
parser.add_option(
|
|
'--no-pip',
|
|
dest='no_pip',
|
|
action='store_true',
|
|
help='Do not install pip in the new virtualenv.')
|
|
|
|
default_search_dirs = file_search_dirs()
|
|
parser.add_option(
|
|
'--extra-search-dir',
|
|
dest="search_dirs",
|
|
action="append",
|
|
metavar='DIR',
|
|
default=default_search_dirs,
|
|
help="Directory to look for setuptools/pip distributions in. "
|
|
"This option can be used multiple times.")
|
|
|
|
parser.add_option(
|
|
'--never-download',
|
|
dest="never_download",
|
|
action="store_true",
|
|
default=True,
|
|
help="DEPRECATED. Retained only for backward compatibility. This option has no effect. "
|
|
"Virtualenv never downloads pip or setuptools.")
|
|
|
|
parser.add_option(
|
|
'--prompt',
|
|
dest='prompt',
|
|
help='Provides an alternative prompt prefix for this environment.')
|
|
|
|
parser.add_option(
|
|
'--setuptools',
|
|
dest='setuptools',
|
|
action='store_true',
|
|
help="DEPRECATED. Retained only for backward compatibility. This option has no effect.")
|
|
|
|
parser.add_option(
|
|
'--distribute',
|
|
dest='distribute',
|
|
action='store_true',
|
|
help="DEPRECATED. Retained only for backward compatibility. This option has no effect.")
|
|
|
|
if 'extend_parser' in globals():
|
|
extend_parser(parser)
|
|
|
|
options, args = parser.parse_args()
|
|
|
|
global logger
|
|
|
|
if 'adjust_options' in globals():
|
|
adjust_options(options, args)
|
|
|
|
verbosity = options.verbose - options.quiet
|
|
logger = Logger([(Logger.level_for_integer(2 - verbosity), sys.stdout)])
|
|
|
|
if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
|
|
env = os.environ.copy()
|
|
interpreter = resolve_interpreter(options.python)
|
|
if interpreter == sys.executable:
|
|
logger.warn('Already using interpreter %s' % interpreter)
|
|
else:
|
|
logger.notify('Running virtualenv with interpreter %s' % interpreter)
|
|
env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true'
|
|
file = __file__
|
|
if file.endswith('.pyc'):
|
|
file = file[:-1]
|
|
popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env)
|
|
raise SystemExit(popen.wait())
|
|
|
|
if not args:
|
|
print('You must provide a DEST_DIR')
|
|
parser.print_help()
|
|
sys.exit(2)
|
|
if len(args) > 1:
|
|
print('There must be only one argument: DEST_DIR (you gave %s)' % (
|
|
' '.join(args)))
|
|
parser.print_help()
|
|
sys.exit(2)
|
|
|
|
home_dir = args[0]
|
|
|
|
if os.environ.get('WORKING_ENV'):
|
|
logger.fatal('ERROR: you cannot run virtualenv while in a workingenv')
|
|
logger.fatal('Please deactivate your workingenv, then re-run this script')
|
|
sys.exit(3)
|
|
|
|
if 'PYTHONHOME' in os.environ:
|
|
logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it')
|
|
del os.environ['PYTHONHOME']
|
|
|
|
if options.relocatable:
|
|
make_environment_relocatable(home_dir)
|
|
return
|
|
|
|
if not options.never_download:
|
|
logger.warn('The --never-download option is for backward compatibility only.')
|
|
logger.warn('Setting it to false is no longer supported, and will be ignored.')
|
|
|
|
create_environment(home_dir,
|
|
site_packages=options.system_site_packages,
|
|
clear=options.clear,
|
|
unzip_setuptools=options.unzip_setuptools,
|
|
prompt=options.prompt,
|
|
search_dirs=options.search_dirs,
|
|
never_download=True,
|
|
no_setuptools=options.no_setuptools,
|
|
no_pip=options.no_pip,
|
|
symlink=options.symlink)
|
|
if 'after_install' in globals():
|
|
after_install(options, home_dir)
|
|
|
|
def call_subprocess(cmd, show_stdout=True,
|
|
filter_stdout=None, cwd=None,
|
|
raise_on_returncode=True, extra_env=None,
|
|
remove_from_env=None):
|
|
cmd_parts = []
|
|
for part in cmd:
|
|
if len(part) > 45:
|
|
part = part[:20]+"..."+part[-20:]
|
|
if ' ' in part or '\n' in part or '"' in part or "'" in part:
|
|
part = '"%s"' % part.replace('"', '\\"')
|
|
if hasattr(part, 'decode'):
|
|
try:
|
|
part = part.decode(sys.getdefaultencoding())
|
|
except UnicodeDecodeError:
|
|
part = part.decode(sys.getfilesystemencoding())
|
|
cmd_parts.append(part)
|
|
cmd_desc = ' '.join(cmd_parts)
|
|
if show_stdout:
|
|
stdout = None
|
|
else:
|
|
stdout = subprocess.PIPE
|
|
logger.debug("Running command %s" % cmd_desc)
|
|
if extra_env or remove_from_env:
|
|
env = os.environ.copy()
|
|
if extra_env:
|
|
env.update(extra_env)
|
|
if remove_from_env:
|
|
for varname in remove_from_env:
|
|
env.pop(varname, None)
|
|
else:
|
|
env = None
|
|
try:
|
|
proc = subprocess.Popen(
|
|
cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
|
|
cwd=cwd, env=env)
|
|
except Exception:
|
|
e = sys.exc_info()[1]
|
|
logger.fatal(
|
|
"Error %s while executing command %s" % (e, cmd_desc))
|
|
raise
|
|
all_output = []
|
|
if stdout is not None:
|
|
stdout = proc.stdout
|
|
encoding = sys.getdefaultencoding()
|
|
fs_encoding = sys.getfilesystemencoding()
|
|
while 1:
|
|
line = stdout.readline()
|
|
try:
|
|
line = line.decode(encoding)
|
|
except UnicodeDecodeError:
|
|
line = line.decode(fs_encoding)
|
|
if not line:
|
|
break
|
|
line = line.rstrip()
|
|
all_output.append(line)
|
|
if filter_stdout:
|
|
level = filter_stdout(line)
|
|
if isinstance(level, tuple):
|
|
level, line = level
|
|
logger.log(level, line)
|
|
if not logger.stdout_level_matches(level):
|
|
logger.show_progress()
|
|
else:
|
|
logger.info(line)
|
|
else:
|
|
proc.communicate()
|
|
proc.wait()
|
|
if proc.returncode:
|
|
if raise_on_returncode:
|
|
if all_output:
|
|
logger.notify('Complete output from command %s:' % cmd_desc)
|
|
logger.notify('\n'.join(all_output) + '\n----------------------------------------')
|
|
raise OSError(
|
|
"Command %s failed with error code %s"
|
|
% (cmd_desc, proc.returncode))
|
|
else:
|
|
logger.warn(
|
|
"Command %s had error code %s"
|
|
% (cmd_desc, proc.returncode))
|
|
|
|
def filter_install_output(line):
|
|
if line.strip().startswith('running'):
|
|
return Logger.INFO
|
|
return Logger.DEBUG
|
|
|
|
def find_wheels(projects, search_dirs):
|
|
"""Find wheels from which we can import PROJECTS.
|
|
|
|
Scan through SEARCH_DIRS for a wheel for each PROJECT in turn. Return
|
|
a list of the first wheel found for each PROJECT
|
|
"""
|
|
|
|
wheels = []
|
|
|
|
# Look through SEARCH_DIRS for the first suitable wheel. Don't bother
|
|
# about version checking here, as this is simply to get something we can
|
|
# then use to install the correct version.
|
|
for project in projects:
|
|
for dirname in search_dirs:
|
|
# This relies on only having "universal" wheels available.
|
|
# The pattern could be tightened to require -py2.py3-none-any.whl.
|
|
files = glob.glob(os.path.join(dirname, project + '-*.whl'))
|
|
if files:
|
|
wheels.append(os.path.abspath(files[0]))
|
|
break
|
|
else:
|
|
# We're out of luck, so quit with a suitable error
|
|
logger.fatal('Cannot find a wheel for %s' % (project,))
|
|
|
|
return wheels
|
|
|
|
def install_wheel(project_names, py_executable, search_dirs=None):
|
|
if search_dirs is None:
|
|
search_dirs = file_search_dirs()
|
|
|
|
wheels = find_wheels(['setuptools', 'pip'], search_dirs)
|
|
pythonpath = os.pathsep.join(wheels)
|
|
findlinks = ' '.join(search_dirs)
|
|
|
|
cmd = [
|
|
py_executable, '-c',
|
|
'import sys, pip; sys.exit(pip.main(["install", "--ignore-installed"] + sys.argv[1:]))',
|
|
] + project_names
|
|
logger.start_progress('Installing %s...' % (', '.join(project_names)))
|
|
logger.indent += 2
|
|
try:
|
|
call_subprocess(cmd, show_stdout=False,
|
|
extra_env = {
|
|
'PYTHONPATH': pythonpath,
|
|
'PIP_FIND_LINKS': findlinks,
|
|
'PIP_USE_WHEEL': '1',
|
|
'PIP_PRE': '1',
|
|
'PIP_NO_INDEX': '1'
|
|
}
|
|
)
|
|
finally:
|
|
logger.indent -= 2
|
|
logger.end_progress()
|
|
|
|
def create_environment(home_dir, site_packages=False, clear=False,
|
|
unzip_setuptools=False,
|
|
prompt=None, search_dirs=None, never_download=False,
|
|
no_setuptools=False, no_pip=False, symlink=True):
|
|
"""
|
|
Creates a new environment in ``home_dir``.
|
|
|
|
If ``site_packages`` is true, then the global ``site-packages/``
|
|
directory will be on the path.
|
|
|
|
If ``clear`` is true (default False) then the environment will
|
|
first be cleared.
|
|
"""
|
|
home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
|
|
|
|
py_executable = os.path.abspath(install_python(
|
|
home_dir, lib_dir, inc_dir, bin_dir,
|
|
site_packages=site_packages, clear=clear, symlink=symlink))
|
|
|
|
install_distutils(home_dir)
|
|
|
|
if not no_setuptools:
|
|
to_install = ['setuptools']
|
|
if not no_pip:
|
|
to_install.append('pip')
|
|
install_wheel(to_install, py_executable, search_dirs)
|
|
|
|
install_activate(home_dir, bin_dir, prompt)
|
|
|
|
def is_executable_file(fpath):
|
|
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
|
|
|
def path_locations(home_dir):
|
|
"""Return the path locations for the environment (where libraries are,
|
|
where scripts go, etc)"""
|
|
# XXX: We'd use distutils.sysconfig.get_python_inc/lib but its
|
|
# prefix arg is broken: http://bugs.python.org/issue3386
|
|
if is_win:
|
|
# Windows has lots of problems with executables with spaces in
|
|
# the name; this function will remove them (using the ~1
|
|
# format):
|
|
mkdir(home_dir)
|
|
if ' ' in home_dir:
|
|
import ctypes
|
|
GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW
|
|
size = max(len(home_dir)+1, 256)
|
|
buf = ctypes.create_unicode_buffer(size)
|
|
try:
|
|
u = unicode
|
|
except NameError:
|
|
u = str
|
|
ret = GetShortPathName(u(home_dir), buf, size)
|
|
if not ret:
|
|
print('Error: the path "%s" has a space in it' % home_dir)
|
|
print('We could not determine the short pathname for it.')
|
|
print('Exiting.')
|
|
sys.exit(3)
|
|
home_dir = str(buf.value)
|
|
lib_dir = join(home_dir, 'Lib')
|
|
inc_dir = join(home_dir, 'Include')
|
|
bin_dir = join(home_dir, 'Scripts')
|
|
if is_jython:
|
|
lib_dir = join(home_dir, 'Lib')
|
|
inc_dir = join(home_dir, 'Include')
|
|
bin_dir = join(home_dir, 'bin')
|
|
elif is_pypy:
|
|
lib_dir = home_dir
|
|
inc_dir = join(home_dir, 'include')
|
|
bin_dir = join(home_dir, 'bin')
|
|
elif not is_win:
|
|
lib_dir = join(home_dir, 'lib', py_version)
|
|
multiarch_exec = '/usr/bin/multiarch-platform'
|
|
if is_executable_file(multiarch_exec):
|
|
# In Mageia (2) and Mandriva distros the include dir must be like:
|
|
# virtualenv/include/multiarch-x86_64-linux/python2.7
|
|
# instead of being virtualenv/include/python2.7
|
|
p = subprocess.Popen(multiarch_exec, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
stdout, stderr = p.communicate()
|
|
# stdout.strip is needed to remove newline character
|
|
inc_dir = join(home_dir, 'include', stdout.strip(), py_version + abiflags)
|
|
else:
|
|
inc_dir = join(home_dir, 'include', py_version + abiflags)
|
|
bin_dir = join(home_dir, 'bin')
|
|
return home_dir, lib_dir, inc_dir, bin_dir
|
|
|
|
|
|
def change_prefix(filename, dst_prefix):
|
|
prefixes = [sys.prefix]
|
|
|
|
if is_darwin:
|
|
prefixes.extend((
|
|
os.path.join("/Library/Python", sys.version[:3], "site-packages"),
|
|
os.path.join(sys.prefix, "Extras", "lib", "python"),
|
|
os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"),
|
|
# Python 2.6 no-frameworks
|
|
os.path.join("~", ".local", "lib","python", sys.version[:3], "site-packages"),
|
|
# System Python 2.7 on OSX Mountain Lion
|
|
os.path.join("~", "Library", "Python", sys.version[:3], "lib", "python", "site-packages")))
|
|
|
|
if hasattr(sys, 'real_prefix'):
|
|
prefixes.append(sys.real_prefix)
|
|
if hasattr(sys, 'base_prefix'):
|
|
prefixes.append(sys.base_prefix)
|
|
prefixes = list(map(os.path.expanduser, prefixes))
|
|
prefixes = list(map(os.path.abspath, prefixes))
|
|
# Check longer prefixes first so we don't split in the middle of a filename
|
|
prefixes = sorted(prefixes, key=len, reverse=True)
|
|
filename = os.path.abspath(filename)
|
|
for src_prefix in prefixes:
|
|
if filename.startswith(src_prefix):
|
|
_, relpath = filename.split(src_prefix, 1)
|
|
if src_prefix != os.sep: # sys.prefix == "/"
|
|
assert relpath[0] == os.sep
|
|
relpath = relpath[1:]
|
|
return join(dst_prefix, relpath)
|
|
assert False, "Filename %s does not start with any of these prefixes: %s" % \
|
|
(filename, prefixes)
|
|
|
|
def copy_required_modules(dst_prefix, symlink):
|
|
import imp
|
|
# If we are running under -p, we need to remove the current
|
|
# directory from sys.path temporarily here, so that we
|
|
# definitely get the modules from the site directory of
|
|
# the interpreter we are running under, not the one
|
|
# virtualenv.py is installed under (which might lead to py2/py3
|
|
# incompatibility issues)
|
|
_prev_sys_path = sys.path
|
|
if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
|
|
sys.path = sys.path[1:]
|
|
try:
|
|
for modname in REQUIRED_MODULES:
|
|
if modname in sys.builtin_module_names:
|
|
logger.info("Ignoring built-in bootstrap module: %s" % modname)
|
|
continue
|
|
try:
|
|
f, filename, _ = imp.find_module(modname)
|
|
except ImportError:
|
|
logger.info("Cannot import bootstrap module: %s" % modname)
|
|
else:
|
|
if f is not None:
|
|
f.close()
|
|
# special-case custom readline.so on OS X, but not for pypy:
|
|
if modname == 'readline' and sys.platform == 'darwin' and not (
|
|
is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
|
|
dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so')
|
|
elif modname == 'readline' and sys.platform == 'win32':
|
|
# special-case for Windows, where readline is not a
|
|
# standard module, though it may have been installed in
|
|
# site-packages by a third-party package
|
|
pass
|
|
else:
|
|
dst_filename = change_prefix(filename, dst_prefix)
|
|
copyfile(filename, dst_filename, symlink)
|
|
if filename.endswith('.pyc'):
|
|
pyfile = filename[:-1]
|
|
if os.path.exists(pyfile):
|
|
copyfile(pyfile, dst_filename[:-1], symlink)
|
|
finally:
|
|
sys.path = _prev_sys_path
|
|
|
|
|
|
def subst_path(prefix_path, prefix, home_dir):
|
|
prefix_path = os.path.normpath(prefix_path)
|
|
prefix = os.path.normpath(prefix)
|
|
home_dir = os.path.normpath(home_dir)
|
|
if not prefix_path.startswith(prefix):
|
|
logger.warn('Path not in prefix %r %r', prefix_path, prefix)
|
|
return
|
|
return prefix_path.replace(prefix, home_dir, 1)
|
|
|
|
|
|
def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear, symlink=True):
|
|
"""Install just the base environment, no distutils patches etc"""
|
|
if sys.executable.startswith(bin_dir):
|
|
print('Please use the *system* python to run this script')
|
|
return
|
|
|
|
if clear:
|
|
rmtree(lib_dir)
|
|
## FIXME: why not delete it?
|
|
## Maybe it should delete everything with #!/path/to/venv/python in it
|
|
logger.notify('Not deleting %s', bin_dir)
|
|
|
|
if hasattr(sys, 'real_prefix'):
|
|
logger.notify('Using real prefix %r' % sys.real_prefix)
|
|
prefix = sys.real_prefix
|
|
elif hasattr(sys, 'base_prefix'):
|
|
logger.notify('Using base prefix %r' % sys.base_prefix)
|
|
prefix = sys.base_prefix
|
|
else:
|
|
prefix = sys.prefix
|
|
mkdir(lib_dir)
|
|
fix_lib64(lib_dir, symlink)
|
|
stdlib_dirs = [os.path.dirname(os.__file__)]
|
|
if is_win:
|
|
stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs'))
|
|
elif is_darwin:
|
|
stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages'))
|
|
if hasattr(os, 'symlink'):
|
|
logger.info('Symlinking Python bootstrap modules')
|
|
else:
|
|
logger.info('Copying Python bootstrap modules')
|
|
logger.indent += 2
|
|
try:
|
|
# copy required files...
|
|
for stdlib_dir in stdlib_dirs:
|
|
if not os.path.isdir(stdlib_dir):
|
|
continue
|
|
for fn in os.listdir(stdlib_dir):
|
|
bn = os.path.splitext(fn)[0]
|
|
if fn != 'site-packages' and bn in REQUIRED_FILES:
|
|
copyfile(join(stdlib_dir, fn), join(lib_dir, fn), symlink)
|
|
# ...and modules
|
|
copy_required_modules(home_dir, symlink)
|
|
finally:
|
|
logger.indent -= 2
|
|
mkdir(join(lib_dir, 'site-packages'))
|
|
import site
|
|
site_filename = site.__file__
|
|
if site_filename.endswith('.pyc'):
|
|
site_filename = site_filename[:-1]
|
|
elif site_filename.endswith('$py.class'):
|
|
site_filename = site_filename.replace('$py.class', '.py')
|
|
site_filename_dst = change_prefix(site_filename, home_dir)
|
|
site_dir = os.path.dirname(site_filename_dst)
|
|
writefile(site_filename_dst, SITE_PY)
|
|
writefile(join(site_dir, 'orig-prefix.txt'), prefix)
|
|
site_packages_filename = join(site_dir, 'no-global-site-packages.txt')
|
|
if not site_packages:
|
|
writefile(site_packages_filename, '')
|
|
|
|
if is_pypy or is_win:
|
|
stdinc_dir = join(prefix, 'include')
|
|
else:
|
|
stdinc_dir = join(prefix, 'include', py_version + abiflags)
|
|
if os.path.exists(stdinc_dir):
|
|
copyfile(stdinc_dir, inc_dir, symlink)
|
|
else:
|
|
logger.debug('No include dir %s' % stdinc_dir)
|
|
|
|
platinc_dir = distutils.sysconfig.get_python_inc(plat_specific=1)
|
|
if platinc_dir != stdinc_dir:
|
|
platinc_dest = distutils.sysconfig.get_python_inc(
|
|
plat_specific=1, prefix=home_dir)
|
|
if platinc_dir == platinc_dest:
|
|
# Do platinc_dest manually due to a CPython bug;
|
|
# not http://bugs.python.org/issue3386 but a close cousin
|
|
platinc_dest = subst_path(platinc_dir, prefix, home_dir)
|
|
if platinc_dest:
|
|
# PyPy's stdinc_dir and prefix are relative to the original binary
|
|
# (traversing virtualenvs), whereas the platinc_dir is relative to
|
|
# the inner virtualenv and ignores the prefix argument.
|
|
# This seems more evolved than designed.
|
|
copyfile(platinc_dir, platinc_dest, symlink)
|
|
|
|
# pypy never uses exec_prefix, just ignore it
|
|
if sys.exec_prefix != prefix and not is_pypy:
|
|
if is_win:
|
|
exec_dir = join(sys.exec_prefix, 'lib')
|
|
elif is_jython:
|
|
exec_dir = join(sys.exec_prefix, 'Lib')
|
|
else:
|
|
exec_dir = join(sys.exec_prefix, 'lib', py_version)
|
|
for fn in os.listdir(exec_dir):
|
|
copyfile(join(exec_dir, fn), join(lib_dir, fn), symlink)
|
|
|
|
if is_jython:
|
|
# Jython has either jython-dev.jar and javalib/ dir, or just
|
|
# jython.jar
|
|
for name in 'jython-dev.jar', 'javalib', 'jython.jar':
|
|
src = join(prefix, name)
|
|
if os.path.exists(src):
|
|
copyfile(src, join(home_dir, name), symlink)
|
|
# XXX: registry should always exist after Jython 2.5rc1
|
|
src = join(prefix, 'registry')
|
|
if os.path.exists(src):
|
|
copyfile(src, join(home_dir, 'registry'), symlink=False)
|
|
copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'),
|
|
symlink=False)
|
|
|
|
mkdir(bin_dir)
|
|
py_executable = join(bin_dir, os.path.basename(sys.executable))
|
|
if 'Python.framework' in prefix:
|
|
# OS X framework builds cause validation to break
|
|
# https://github.com/pypa/virtualenv/issues/322
|
|
if os.environ.get('__PYVENV_LAUNCHER__'):
|
|
del os.environ["__PYVENV_LAUNCHER__"]
|
|
if re.search(r'/Python(?:-32|-64)*$', py_executable):
|
|
# The name of the python executable is not quite what
|
|
# we want, rename it.
|
|
py_executable = os.path.join(
|
|
os.path.dirname(py_executable), 'python')
|
|
|
|
logger.notify('New %s executable in %s', expected_exe, py_executable)
|
|
pcbuild_dir = os.path.dirname(sys.executable)
|
|
pyd_pth = os.path.join(lib_dir, 'site-packages', 'virtualenv_builddir_pyd.pth')
|
|
if is_win and os.path.exists(os.path.join(pcbuild_dir, 'build.bat')):
|
|
logger.notify('Detected python running from build directory %s', pcbuild_dir)
|
|
logger.notify('Writing .pth file linking to build directory for *.pyd files')
|
|
writefile(pyd_pth, pcbuild_dir)
|
|
else:
|
|
pcbuild_dir = None
|
|
if os.path.exists(pyd_pth):
|
|
logger.info('Deleting %s (not Windows env or not build directory python)' % pyd_pth)
|
|
os.unlink(pyd_pth)
|
|
|
|
if sys.executable != py_executable:
|
|
## FIXME: could I just hard link?
|
|
executable = sys.executable
|
|
shutil.copyfile(executable, py_executable)
|
|
make_exe(py_executable)
|
|
if is_win or is_cygwin:
|
|
pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe')
|
|
if os.path.exists(pythonw):
|
|
logger.info('Also created pythonw.exe')
|
|
shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe'))
|
|
python_d = os.path.join(os.path.dirname(sys.executable), 'python_d.exe')
|
|
python_d_dest = os.path.join(os.path.dirname(py_executable), 'python_d.exe')
|
|
if os.path.exists(python_d):
|
|
logger.info('Also created python_d.exe')
|
|
shutil.copyfile(python_d, python_d_dest)
|
|
elif os.path.exists(python_d_dest):
|
|
logger.info('Removed python_d.exe as it is no longer at the source')
|
|
os.unlink(python_d_dest)
|
|
# we need to copy the DLL to enforce that windows will load the correct one.
|
|
# may not exist if we are cygwin.
|
|
py_executable_dll = 'python%s%s.dll' % (
|
|
sys.version_info[0], sys.version_info[1])
|
|
py_executable_dll_d = 'python%s%s_d.dll' % (
|
|
sys.version_info[0], sys.version_info[1])
|
|
pythondll = os.path.join(os.path.dirname(sys.executable), py_executable_dll)
|
|
pythondll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d)
|
|
pythondll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d)
|
|
if os.path.exists(pythondll):
|
|
logger.info('Also created %s' % py_executable_dll)
|
|
shutil.copyfile(pythondll, os.path.join(os.path.dirname(py_executable), py_executable_dll))
|
|
if os.path.exists(pythondll_d):
|
|
logger.info('Also created %s' % py_executable_dll_d)
|
|
shutil.copyfile(pythondll_d, pythondll_d_dest)
|
|
elif os.path.exists(pythondll_d_dest):
|
|
logger.info('Removed %s as the source does not exist' % pythondll_d_dest)
|
|
os.unlink(pythondll_d_dest)
|
|
if is_pypy:
|
|
# make a symlink python --> pypy-c
|
|
python_executable = os.path.join(os.path.dirname(py_executable), 'python')
|
|
if sys.platform in ('win32', 'cygwin'):
|
|
python_executable += '.exe'
|
|
logger.info('Also created executable %s' % python_executable)
|
|
copyfile(py_executable, python_executable, symlink)
|
|
|
|
if is_win:
|
|
for name in 'libexpat.dll', 'libpypy.dll', 'libpypy-c.dll', 'libeay32.dll', 'ssleay32.dll', 'sqlite.dll':
|
|
src = join(prefix, name)
|
|
if os.path.exists(src):
|
|
copyfile(src, join(bin_dir, name), symlink)
|
|
|
|
if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe:
|
|
secondary_exe = os.path.join(os.path.dirname(py_executable),
|
|
expected_exe)
|
|
py_executable_ext = os.path.splitext(py_executable)[1]
|
|
if py_executable_ext.lower() == '.exe':
|
|
# python2.4 gives an extension of '.4' :P
|
|
secondary_exe += py_executable_ext
|
|
if os.path.exists(secondary_exe):
|
|
logger.warn('Not overwriting existing %s script %s (you must use %s)'
|
|
% (expected_exe, secondary_exe, py_executable))
|
|
else:
|
|
logger.notify('Also creating executable in %s' % secondary_exe)
|
|
shutil.copyfile(sys.executable, secondary_exe)
|
|
make_exe(secondary_exe)
|
|
|
|
if '.framework' in prefix:
|
|
if 'Python.framework' in prefix:
|
|
logger.debug('MacOSX Python framework detected')
|
|
# Make sure we use the the embedded interpreter inside
|
|
# the framework, even if sys.executable points to
|
|
# the stub executable in ${sys.prefix}/bin
|
|
# See http://groups.google.com/group/python-virtualenv/
|
|
# browse_thread/thread/17cab2f85da75951
|
|
original_python = os.path.join(
|
|
prefix, 'Resources/Python.app/Contents/MacOS/Python')
|
|
if 'EPD' in prefix:
|
|
logger.debug('EPD framework detected')
|
|
original_python = os.path.join(prefix, 'bin/python')
|
|
shutil.copy(original_python, py_executable)
|
|
|
|
# Copy the framework's dylib into the virtual
|
|
# environment
|
|
virtual_lib = os.path.join(home_dir, '.Python')
|
|
|
|
if os.path.exists(virtual_lib):
|
|
os.unlink(virtual_lib)
|
|
copyfile(
|
|
os.path.join(prefix, 'Python'),
|
|
virtual_lib,
|
|
symlink)
|
|
|
|
# And then change the install_name of the copied python executable
|
|
try:
|
|
mach_o_change(py_executable,
|
|
os.path.join(prefix, 'Python'),
|
|
'@executable_path/../.Python')
|
|
except:
|
|
e = sys.exc_info()[1]
|
|
logger.warn("Could not call mach_o_change: %s. "
|
|
"Trying to call install_name_tool instead." % e)
|
|
try:
|
|
call_subprocess(
|
|
["install_name_tool", "-change",
|
|
os.path.join(prefix, 'Python'),
|
|
'@executable_path/../.Python',
|
|
py_executable])
|
|
except:
|
|
logger.fatal("Could not call install_name_tool -- you must "
|
|
"have Apple's development tools installed")
|
|
raise
|
|
|
|
if not is_win:
|
|
# Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist
|
|
py_exe_version_major = 'python%s' % sys.version_info[0]
|
|
py_exe_version_major_minor = 'python%s.%s' % (
|
|
sys.version_info[0], sys.version_info[1])
|
|
py_exe_no_version = 'python'
|
|
required_symlinks = [ py_exe_no_version, py_exe_version_major,
|
|
py_exe_version_major_minor ]
|
|
|
|
py_executable_base = os.path.basename(py_executable)
|
|
|
|
if py_executable_base in required_symlinks:
|
|
# Don't try to symlink to yourself.
|
|
required_symlinks.remove(py_executable_base)
|
|
|
|
for pth in required_symlinks:
|
|
full_pth = join(bin_dir, pth)
|
|
if os.path.exists(full_pth):
|
|
os.unlink(full_pth)
|
|
if symlink:
|
|
os.symlink(py_executable_base, full_pth)
|
|
else:
|
|
copyfile(py_executable, full_pth, symlink)
|
|
|
|
if is_win and ' ' in py_executable:
|
|
# There's a bug with subprocess on Windows when using a first
|
|
# argument that has a space in it. Instead we have to quote
|
|
# the value:
|
|
py_executable = '"%s"' % py_executable
|
|
# NOTE: keep this check as one line, cmd.exe doesn't cope with line breaks
|
|
cmd = [py_executable, '-c', 'import sys;out=sys.stdout;'
|
|
'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))']
|
|
logger.info('Testing executable with %s %s "%s"' % tuple(cmd))
|
|
try:
|
|
proc = subprocess.Popen(cmd,
|
|
stdout=subprocess.PIPE)
|
|
proc_stdout, proc_stderr = proc.communicate()
|
|
except OSError:
|
|
e = sys.exc_info()[1]
|
|
if e.errno == errno.EACCES:
|
|
logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e))
|
|
sys.exit(100)
|
|
else:
|
|
raise e
|
|
|
|
proc_stdout = proc_stdout.strip().decode("utf-8")
|
|
proc_stdout = os.path.normcase(os.path.abspath(proc_stdout))
|
|
norm_home_dir = os.path.normcase(os.path.abspath(home_dir))
|
|
if hasattr(norm_home_dir, 'decode'):
|
|
norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding())
|
|
if proc_stdout != norm_home_dir:
|
|
logger.fatal(
|
|
'ERROR: The executable %s is not functioning' % py_executable)
|
|
logger.fatal(
|
|
'ERROR: It thinks sys.prefix is %r (should be %r)'
|
|
% (proc_stdout, norm_home_dir))
|
|
logger.fatal(
|
|
'ERROR: virtualenv is not compatible with this system or executable')
|
|
if is_win:
|
|
logger.fatal(
|
|
'Note: some Windows users have reported this error when they '
|
|
'installed Python for "Only this user" or have multiple '
|
|
'versions of Python installed. Copying the appropriate '
|
|
'PythonXX.dll to the virtualenv Scripts/ directory may fix '
|
|
'this problem.')
|
|
sys.exit(100)
|
|
else:
|
|
logger.info('Got sys.prefix result: %r' % proc_stdout)
|
|
|
|
pydistutils = os.path.expanduser('~/.pydistutils.cfg')
|
|
if os.path.exists(pydistutils):
|
|
logger.notify('Please make sure you remove any previous custom paths from '
|
|
'your %s file.' % pydistutils)
|
|
## FIXME: really this should be calculated earlier
|
|
|
|
fix_local_scheme(home_dir, symlink)
|
|
|
|
if site_packages:
|
|
if os.path.exists(site_packages_filename):
|
|
logger.info('Deleting %s' % site_packages_filename)
|
|
os.unlink(site_packages_filename)
|
|
|
|
return py_executable
|
|
|
|
|
|
def install_activate(home_dir, bin_dir, prompt=None):
|
|
home_dir = os.path.abspath(home_dir)
|
|
if is_win or is_jython and os._name == 'nt':
|
|
files = {
|
|
'activate.bat': ACTIVATE_BAT,
|
|
'deactivate.bat': DEACTIVATE_BAT,
|
|
'activate.ps1': ACTIVATE_PS,
|
|
}
|
|
|
|
# MSYS needs paths of the form /c/path/to/file
|
|
drive, tail = os.path.splitdrive(home_dir.replace(os.sep, '/'))
|
|
home_dir_msys = (drive and "/%s%s" or "%s%s") % (drive[:1], tail)
|
|
|
|
# Run-time conditional enables (basic) Cygwin compatibility
|
|
home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" %
|
|
(home_dir, home_dir_msys))
|
|
files['activate'] = ACTIVATE_SH.replace('__VIRTUAL_ENV__', home_dir_sh)
|
|
|
|
else:
|
|
files = {'activate': ACTIVATE_SH}
|
|
|
|
# suppling activate.fish in addition to, not instead of, the
|
|
# bash script support.
|
|
files['activate.fish'] = ACTIVATE_FISH
|
|
|
|
# same for csh/tcsh support...
|
|
files['activate.csh'] = ACTIVATE_CSH
|
|
|
|
files['activate_this.py'] = ACTIVATE_THIS
|
|
if hasattr(home_dir, 'decode'):
|
|
home_dir = home_dir.decode(sys.getfilesystemencoding())
|
|
vname = os.path.basename(home_dir)
|
|
for name, content in files.items():
|
|
content = content.replace('__VIRTUAL_PROMPT__', prompt or '')
|
|
content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname)
|
|
content = content.replace('__VIRTUAL_ENV__', home_dir)
|
|
content = content.replace('__VIRTUAL_NAME__', vname)
|
|
content = content.replace('__BIN_NAME__', os.path.basename(bin_dir))
|
|
writefile(os.path.join(bin_dir, name), content)
|
|
|
|
def install_distutils(home_dir):
|
|
distutils_path = change_prefix(distutils.__path__[0], home_dir)
|
|
mkdir(distutils_path)
|
|
## FIXME: maybe this prefix setting should only be put in place if
|
|
## there's a local distutils.cfg with a prefix setting?
|
|
home_dir = os.path.abspath(home_dir)
|
|
## FIXME: this is breaking things, removing for now:
|
|
#distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir
|
|
writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT)
|
|
writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False)
|
|
|
|
def fix_local_scheme(home_dir, symlink=True):
|
|
"""
|
|
Platforms that use the "posix_local" install scheme (like Ubuntu with
|
|
Python 2.7) need to be given an additional "local" location, sigh.
|
|
"""
|
|
try:
|
|
import sysconfig
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
if sysconfig._get_default_scheme() == 'posix_local':
|
|
local_path = os.path.join(home_dir, 'local')
|
|
if not os.path.exists(local_path):
|
|
os.mkdir(local_path)
|
|
for subdir_name in os.listdir(home_dir):
|
|
if subdir_name == 'local':
|
|
continue
|
|
copyfile(os.path.abspath(os.path.join(home_dir, subdir_name)), \
|
|
os.path.join(local_path, subdir_name), symlink)
|
|
|
|
def fix_lib64(lib_dir, symlink=True):
|
|
"""
|
|
Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y
|
|
instead of lib/pythonX.Y. If this is such a platform we'll just create a
|
|
symlink so lib64 points to lib
|
|
"""
|
|
if [p for p in distutils.sysconfig.get_config_vars().values()
|
|
if isinstance(p, basestring) and 'lib64' in p]:
|
|
# PyPy's library path scheme is not affected by this.
|
|
# Return early or we will die on the following assert.
|
|
if is_pypy:
|
|
logger.debug('PyPy detected, skipping lib64 symlinking')
|
|
return
|
|
|
|
logger.debug('This system uses lib64; symlinking lib64 to lib')
|
|
|
|
assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], (
|
|
"Unexpected python lib dir: %r" % lib_dir)
|
|
lib_parent = os.path.dirname(lib_dir)
|
|
top_level = os.path.dirname(lib_parent)
|
|
lib_dir = os.path.join(top_level, 'lib')
|
|
lib64_link = os.path.join(top_level, 'lib64')
|
|
assert os.path.basename(lib_parent) == 'lib', (
|
|
"Unexpected parent dir: %r" % lib_parent)
|
|
if os.path.lexists(lib64_link):
|
|
return
|
|
cp_or_ln = (os.symlink if symlink else copyfile)
|
|
cp_or_ln('lib', lib64_link)
|
|
|
|
def resolve_interpreter(exe):
|
|
"""
|
|
If the executable given isn't an absolute path, search $PATH for the interpreter
|
|
"""
|
|
# If the "executable" is a version number, get the installed executable for
|
|
# that version
|
|
python_versions = get_installed_pythons()
|
|
if exe in python_versions:
|
|
exe = python_versions[exe]
|
|
|
|
if os.path.abspath(exe) != exe:
|
|
paths = os.environ.get('PATH', '').split(os.pathsep)
|
|
for path in paths:
|
|
if os.path.exists(os.path.join(path, exe)):
|
|
exe = os.path.join(path, exe)
|
|
break
|
|
if not os.path.exists(exe):
|
|
logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe))
|
|
raise SystemExit(3)
|
|
if not is_executable(exe):
|
|
logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe))
|
|
raise SystemExit(3)
|
|
return exe
|
|
|
|
def is_executable(exe):
|
|
"""Checks a file is executable"""
|
|
return os.access(exe, os.X_OK)
|
|
|
|
############################################################
|
|
## Relocating the environment:
|
|
|
|
def make_environment_relocatable(home_dir):
|
|
"""
|
|
Makes the already-existing environment use relative paths, and takes out
|
|
the #!-based environment selection in scripts.
|
|
"""
|
|
home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
|
|
activate_this = os.path.join(bin_dir, 'activate_this.py')
|
|
if not os.path.exists(activate_this):
|
|
logger.fatal(
|
|
'The environment doesn\'t have a file %s -- please re-run virtualenv '
|
|
'on this environment to update it' % activate_this)
|
|
fixup_scripts(home_dir, bin_dir)
|
|
fixup_pth_and_egg_link(home_dir)
|
|
## FIXME: need to fix up distutils.cfg
|
|
|
|
OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3],
|
|
'activate', 'activate.bat', 'activate_this.py',
|
|
'activate.fish', 'activate.csh']
|
|
|
|
def fixup_scripts(home_dir, bin_dir):
|
|
if is_win:
|
|
new_shebang_args = (
|
|
'%s /c' % os.path.normcase(os.environ.get('COMSPEC', 'cmd.exe')),
|
|
'', '.exe')
|
|
else:
|
|
new_shebang_args = ('/usr/bin/env', sys.version[:3], '')
|
|
|
|
# This is what we expect at the top of scripts:
|
|
shebang = '#!%s' % os.path.normcase(os.path.join(
|
|
os.path.abspath(bin_dir), 'python%s' % new_shebang_args[2]))
|
|
# This is what we'll put:
|
|
new_shebang = '#!%s python%s%s' % new_shebang_args
|
|
|
|
for filename in os.listdir(bin_dir):
|
|
filename = os.path.join(bin_dir, filename)
|
|
if not os.path.isfile(filename):
|
|
# ignore subdirs, e.g. .svn ones.
|
|
continue
|
|
f = open(filename, 'rb')
|
|
try:
|
|
try:
|
|
lines = f.read().decode('utf-8').splitlines()
|
|
except UnicodeDecodeError:
|
|
# This is probably a binary program instead
|
|
# of a script, so just ignore it.
|
|
continue
|
|
finally:
|
|
f.close()
|
|
if not lines:
|
|
logger.warn('Script %s is an empty file' % filename)
|
|
continue
|
|
|
|
old_shebang = lines[0].strip()
|
|
old_shebang = old_shebang[0:2] + os.path.normcase(old_shebang[2:])
|
|
|
|
if not old_shebang.startswith(shebang):
|
|
if os.path.basename(filename) in OK_ABS_SCRIPTS:
|
|
logger.debug('Cannot make script %s relative' % filename)
|
|
elif lines[0].strip() == new_shebang:
|
|
logger.info('Script %s has already been made relative' % filename)
|
|
else:
|
|
logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)'
|
|
% (filename, shebang))
|
|
continue
|
|
logger.notify('Making script %s relative' % filename)
|
|
script = relative_script([new_shebang] + lines[1:])
|
|
f = open(filename, 'wb')
|
|
f.write('\n'.join(script).encode('utf-8'))
|
|
f.close()
|
|
|
|
def relative_script(lines):
|
|
"Return a script that'll work in a relocatable environment."
|
|
activate = "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); exec(compile(open(activate_this).read(), activate_this, 'exec'), dict(__file__=activate_this)); del os, activate_this"
|
|
# Find the last future statement in the script. If we insert the activation
|
|
# line before a future statement, Python will raise a SyntaxError.
|
|
activate_at = None
|
|
for idx, line in reversed(list(enumerate(lines))):
|
|
if line.split()[:3] == ['from', '__future__', 'import']:
|
|
activate_at = idx + 1
|
|
break
|
|
if activate_at is None:
|
|
# Activate after the shebang.
|
|
activate_at = 1
|
|
return lines[:activate_at] + ['', activate, ''] + lines[activate_at:]
|
|
|
|
def fixup_pth_and_egg_link(home_dir, sys_path=None):
|
|
"""Makes .pth and .egg-link files use relative paths"""
|
|
home_dir = os.path.normcase(os.path.abspath(home_dir))
|
|
if sys_path is None:
|
|
sys_path = sys.path
|
|
for path in sys_path:
|
|
if not path:
|
|
path = '.'
|
|
if not os.path.isdir(path):
|
|
continue
|
|
path = os.path.normcase(os.path.abspath(path))
|
|
if not path.startswith(home_dir):
|
|
logger.debug('Skipping system (non-environment) directory %s' % path)
|
|
continue
|
|
for filename in os.listdir(path):
|
|
filename = os.path.join(path, filename)
|
|
if filename.endswith('.pth'):
|
|
if not os.access(filename, os.W_OK):
|
|
logger.warn('Cannot write .pth file %s, skipping' % filename)
|
|
else:
|
|
fixup_pth_file(filename)
|
|
if filename.endswith('.egg-link'):
|
|
if not os.access(filename, os.W_OK):
|
|
logger.warn('Cannot write .egg-link file %s, skipping' % filename)
|
|
else:
|
|
fixup_egg_link(filename)
|
|
|
|
def fixup_pth_file(filename):
|
|
lines = []
|
|
prev_lines = []
|
|
f = open(filename)
|
|
prev_lines = f.readlines()
|
|
f.close()
|
|
for line in prev_lines:
|
|
line = line.strip()
|
|
if (not line or line.startswith('#') or line.startswith('import ')
|
|
or os.path.abspath(line) != line):
|
|
lines.append(line)
|
|
else:
|
|
new_value = make_relative_path(filename, line)
|
|
if line != new_value:
|
|
logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename))
|
|
lines.append(new_value)
|
|
if lines == prev_lines:
|
|
logger.info('No changes to .pth file %s' % filename)
|
|
return
|
|
logger.notify('Making paths in .pth file %s relative' % filename)
|
|
f = open(filename, 'w')
|
|
f.write('\n'.join(lines) + '\n')
|
|
f.close()
|
|
|
|
def fixup_egg_link(filename):
|
|
f = open(filename)
|
|
link = f.readline().strip()
|
|
f.close()
|
|
if os.path.abspath(link) != link:
|
|
logger.debug('Link in %s already relative' % filename)
|
|
return
|
|
new_link = make_relative_path(filename, link)
|
|
logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link))
|
|
f = open(filename, 'w')
|
|
f.write(new_link)
|
|
f.close()
|
|
|
|
def make_relative_path(source, dest, dest_is_directory=True):
|
|
"""
|
|
Make a filename relative, where the filename is dest, and it is
|
|
being referred to from the filename source.
|
|
|
|
>>> make_relative_path('/usr/share/something/a-file.pth',
|
|
... '/usr/share/another-place/src/Directory')
|
|
'../another-place/src/Directory'
|
|
>>> make_relative_path('/usr/share/something/a-file.pth',
|
|
... '/home/user/src/Directory')
|
|
'../../../home/user/src/Directory'
|
|
>>> make_relative_path('/usr/share/a-file.pth', '/usr/share/')
|
|
'./'
|
|
"""
|
|
source = os.path.dirname(source)
|
|
if not dest_is_directory:
|
|
dest_filename = os.path.basename(dest)
|
|
dest = os.path.dirname(dest)
|
|
dest = os.path.normpath(os.path.abspath(dest))
|
|
source = os.path.normpath(os.path.abspath(source))
|
|
dest_parts = dest.strip(os.path.sep).split(os.path.sep)
|
|
source_parts = source.strip(os.path.sep).split(os.path.sep)
|
|
while dest_parts and source_parts and dest_parts[0] == source_parts[0]:
|
|
dest_parts.pop(0)
|
|
source_parts.pop(0)
|
|
full_parts = ['..']*len(source_parts) + dest_parts
|
|
if not dest_is_directory:
|
|
full_parts.append(dest_filename)
|
|
if not full_parts:
|
|
# Special case for the current directory (otherwise it'd be '')
|
|
return './'
|
|
return os.path.sep.join(full_parts)
|
|
|
|
|
|
|
|
############################################################
|
|
## Bootstrap script creation:
|
|
|
|
def create_bootstrap_script(extra_text, python_version=''):
|
|
"""
|
|
Creates a bootstrap script, which is like this script but with
|
|
extend_parser, adjust_options, and after_install hooks.
|
|
|
|
This returns a string that (written to disk of course) can be used
|
|
as a bootstrap script with your own customizations. The script
|
|
will be the standard virtualenv.py script, with your extra text
|
|
added (your extra text should be Python code).
|
|
|
|
If you include these functions, they will be called:
|
|
|
|
``extend_parser(optparse_parser)``:
|
|
You can add or remove options from the parser here.
|
|
|
|
``adjust_options(options, args)``:
|
|
You can change options here, or change the args (if you accept
|
|
different kinds of arguments, be sure you modify ``args`` so it is
|
|
only ``[DEST_DIR]``).
|
|
|
|
``after_install(options, home_dir)``:
|
|
|
|
After everything is installed, this function is called. This
|
|
is probably the function you are most likely to use. An
|
|
example would be::
|
|
|
|
def after_install(options, home_dir):
|
|
subprocess.call([join(home_dir, 'bin', 'easy_install'),
|
|
'MyPackage'])
|
|
subprocess.call([join(home_dir, 'bin', 'my-package-script'),
|
|
'setup', home_dir])
|
|
|
|
This example immediately installs a package, and runs a setup
|
|
script from that package.
|
|
|
|
If you provide something like ``python_version='2.5'`` then the
|
|
script will start with ``#!/usr/bin/env python2.5`` instead of
|
|
``#!/usr/bin/env python``. You can use this when the script must
|
|
be run with a particular Python version.
|
|
"""
|
|
filename = __file__
|
|
if filename.endswith('.pyc'):
|
|
filename = filename[:-1]
|
|
f = codecs.open(filename, 'r', encoding='utf-8')
|
|
content = f.read()
|
|
f.close()
|
|
py_exe = 'python%s' % python_version
|
|
content = (('#!/usr/bin/env %s\n' % py_exe)
|
|
+ '## WARNING: This file is generated\n'
|
|
+ content)
|
|
return content.replace('##EXT' 'END##', extra_text)
|
|
|
|
##EXTEND##
|
|
|
|
def convert(s):
|
|
b = base64.b64decode(s.encode('ascii'))
|
|
return zlib.decompress(b).decode('utf-8')
|
|
|
|
##file site.py
|
|
SITE_PY = convert("""
|
|
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
|
|
Qc7sRHsA1igE/wfVIOdx
|
|
""")
|
|
|
|
##file activate.sh
|
|
ACTIVATE_SH = convert("""
|
|
eJytVVFvokAQfudXTLEPtTlLeo9tvMSmJpq02hSvl7u2wRUG2QR2DSxSe7n/frOACEVNLlceRHa+
|
|
nfl25pvZDswCnoDPQ4QoTRQsENIEPci4CsBMZBq7CAsuLOYqvmYKTTj3YxnBgiXBudGBjUzBZUJI
|
|
BXEqgCvweIyuCjeG4eF2F5x14bcB9KQiQQWrjSddI1/oQIx6SYYeoFjzWIoIhYI1izlbhJjkKO7D
|
|
M/QEmKfO9O7WeRo/zr4P7pyHwWxkwitcgwpQ5Ej96OX+PmiFwLeVjFUOrNYKaq1Nud3nR2n8nI2m
|
|
k9H0friPTGVsUdptaxGrTEfpNVFEskxpXtUkkCkl1UNF9cgLBkx48J4EXyALuBtAwNYIjF5kcmUU
|
|
abMKmMq1ULoiRbgsDEkTSsKSGFCJ6Z8vY/2xYiSacmtyAfCDdCNTVZoVF8vSTQOoEwSnOrngBkws
|
|
MYGMBMg8/bMBLSYKS7pYEXP0PqT+ZmBT0Xuy+Pplj5yn4aM9nk72JD8/Wi+Gr98sD9eWSMOwkapD
|
|
BbUv91XSvmyVkICt2tmXR4tWmrcUCsjWOpw87YidEC8i0gdTSOFhouJUNxR+4NYBG0MftoCTD9F7
|
|
2rTtxG3oPwY1b2HncYwhrlmj6Wq924xtGDWqfdNxap+OYxplEurnMVo9RWks+rH8qKEtx7kZT5zJ
|
|
4H7oOFclrN6uFe+d+nW2aIUsSgs/42EIPuOhXq+jEo3S6tX6w2ilNkDnIpHCWdEQhFgwj9pkk7FN
|
|
l/y5eQvRSIQ5+TrL05lewxWpt/Lbhes5cJF3mLET1MGhcKCF+40tNWnUulxrpojwDo2sObdje3Bz
|
|
N3QeHqf3D7OjEXMVV8LN3ZlvuzoWHqiUcNKHtwNd0IbvPGKYYM31nPKCgkUILw3KL+Y8l7aO1ArS
|
|
Ad37nIU0fCj5NE5gQCuC5sOSu+UdI2NeXg/lFkQIlFpdWVaWZRfvqGiirC9o6liJ9FXGYrSY9mI1
|
|
D/Ncozgn13vJvsznr7DnkJWXsyMH7e42ljdJ+aqNDF1bFnKWFLdj31xtaJYK6EXFgqmV/ymD/ROG
|
|
+n8O9H8f5vsGOWXsL1+1k3g=
|
|
""")
|
|
|
|
##file activate.fish
|
|
ACTIVATE_FISH = convert("""
|
|
eJydVW2P2jgQ/s6vmAZQoVpA9/WkqqJaTou0u6x2uZVOVWWZZEKsS+yc7UDpr+84bziQbauLxEvs
|
|
eXnsZ56ZIWwTYSAWKUJWGAs7hMJgBEdhEwiMKnSIsBNywUMrDtziPBYmCeBDrFUG7v8HmCTW5n8u
|
|
Fu7NJJim81Bl08EQTqqAkEupLOhCgrAQCY2hTU+DQVxIiqgkRNiEBphFEKy+kd1BaFvwFOUBuIxA
|
|
oy20BKtAKp3xFMo0QNtCK5mhtMEA6BmSpUELKo38TThwLfguRVNaiRgs0llnEoIR29zfstf18/bv
|
|
5T17Wm7vAiiN3ONCzfbfwC3DtWXXDqHfAGX0q6z/bO82j3ebh1VwnbrduwTQbvwcRtesAfMGor/W
|
|
L3fs6Xnz8LRlm9fV8/P61sM0LDNwCZjl9gSpCokJRzpryGQ5t8kNGFUt51QjOZGu0Mj35FlYlXEr
|
|
yC09EVOp4lEXfF84Lz1qbhBsgl59vDedXI3rTV03xipduSgt9kLytI3XmBp3aV6MPoMQGNUU62T6
|
|
uQdeefTy1Hfj10zVHg2pq8fXDoHBiOv94csfXwN49xECqWREy7pwukKfvxdMY2j23vXDPuuxxeE+
|
|
JOdCOhxCE3N44B1ZeSLuZh8Mmkr2wEPAmPfKWHA2uxIRjEopdbQYjDz3BWOf14/scfmwoki1eQvX
|
|
ExBdF60Mqh+Y/QcX4uiH4Amwzx79KOVFtbL63sXJbtcvy8/3q5rupmO5CnE91wBviQAhjUUegYpL
|
|
vVEbpLt2/W+PklRgq5Ku6mp+rpMhhCo/lXthQTxJ2ysO4Ka0ad97S7VT/n6YXus6fzk3fLnBZW5C
|
|
KDC6gSO62QDqgFqLCCtPmjegjnLeAdArtSE8VYGbAJ/aLb+vnQutFhk768E9uRbSxhCMzdgEveYw
|
|
IZ5ZqFKl6+kz7UR4U+buqQZXu9SIujrAfD7f0FXpozB4Q0gwp31H9mVTZGGC4b871/wm7lvyDLu1
|
|
FUyvTj/yvD66k3UPTs08x1AQQaGziOl0S1qRkPG9COtBTSTWM9NzQ4R64B+Px/l3tDzCgxv5C6Ni
|
|
e+QaF9xFWrxx0V/G5uvYQOdiZzvYpQUVQSIsTr1TTghI33GnPbTA7/GCqcE3oE3GZurq4HeQXQD6
|
|
32XS1ITj/qLjN72ob0hc5C9bzw8MhfmL
|
|
""")
|
|
|
|
##file activate.csh
|
|
ACTIVATE_CSH = convert("""
|
|
eJx9VG1P2zAQ/u5fcYQKNgTNPtN1WxlIQ4KCUEGaxuQ6yYVYSuzKdhqVX7+zk3bpy5YPUXL3PPfc
|
|
ne98DLNCWshliVDV1kGCUFvMoJGugMjq2qQIiVSxSJ1cCofD1BYRnOVGV0CfZ0N2DD91DalQSjsw
|
|
tQLpIJMGU1euvPe7QeJlkKzgWixlhnAt4aoUVsLnLBiy5NtbJWQ5THX1ZciYKKWwkOFaE04dUm6D
|
|
r/zh7pq/3D7Nnid3/HEy+wFHY/gEJydg0aFaQrBFgz1c5DG1IhTs+UZgsBC2GMFBlaeH+8dZXwcW
|
|
VPvCjXdlAvCfQsE7al0+07XjZvrSCUevR5dnkVeKlFYZmUztG4BdzL2u9KyLVabTU0bdfg7a0hgs
|
|
cSmUg6UwUiQl2iHrcbcVGNvPCiLOe7+cRwG13z9qRGgx2z6DHjfm/Op2yqeT+xvOLzs0PTKHDz2V
|
|
tkckFHoQfQRXoGJAj9el0FyJCmEMhzgMS4sB7KPOE2ExoLcSieYwDvR+cP8cg11gKkVJc2wRcm1g
|
|
QhYFlXiTaTfO2ki0fQoiFM4tLuO4aZrhOzqR4dIPcWx17hphMBY+Srwh7RTyN83XOWkcSPh1Pg/k
|
|
TXX/jbJTbMtUmcxZ+/bbqOsy82suFQg/BhdSOTRhMNBHlUarCpU7JzBhmkKmRejKOQzayQe6MWoa
|
|
n1wqWmuh6LZAaHxcdeqIlVLhIBJdO9/kbl0It2oEXQj+eGjJOuvOIR/YGRqvFhttUB2XTvLXYN2H
|
|
37CBdbW2W7j2r2+VsCn0doVWcFG1/4y1VwBjfwAyoZhD
|
|
""")
|
|
|
|
##file activate.bat
|
|
ACTIVATE_BAT = convert("""
|
|
eJx9UdEKgjAUfW6wfxjiIH+hEDKUFHSKLCMI7kNOEkIf9P9pTJ3OLJ/03HPPPed4Es9XS9qqwqgT
|
|
PbGKKOdXL4aAFS7A4gvAwgijuiKlqOpGlATS2NeMLE+TjJM9RkQ+SmqAXLrBo1LLIeLdiWlD6jZt
|
|
r7VNubWkndkXaxg5GO3UaOOKS6drO3luDDiO5my3iA0YAKGzPRV1ack8cOdhysI0CYzIPzjSiH5X
|
|
0QcvC8Lfaj0emsVKYF2rhL5L3fCkVjV76kShi59NHwDniAHzkgDgqBcwOgTMx+gDQQqXCw==
|
|
""")
|
|
|
|
##file deactivate.bat
|
|
DEACTIVATE_BAT = convert("""
|
|
eJxzSE3OyFfIT0vj4ipOLVEI8wwKCXX0iXf1C7Pl4spMU0hJTcvMS01RiPf3cYmHyQYE+fsGhCho
|
|
cCkAAUibEkTEVhWLMlUlLk6QGixStlyaeCyJDPHw9/Pw93VFsQguim4ZXAJoIUw5DhX47XUM8UCx
|
|
EchHtwsohN1bILUgw61c/Vy4AJYPYm4=
|
|
""")
|
|
|
|
##file activate.ps1
|
|
ACTIVATE_PS = convert("""
|
|
eJylWdmS40Z2fVeE/oHT6rCloNUEAXDThB6wAyQAEjsB29GBjdgXYiWgmC/zgz/Jv+AEWNVd3S2N
|
|
xuOKYEUxM+/Jmzfvcm7W//zXf/+wUMOoXtyi1F9kbd0sHH/hFc2iLtrK9b3FrSqyxaVQwr8uhqJd
|
|
uHaeg9mqzRdR8/13Pyy8qPLdJh0+LMhi0QCoXxYfFh9WtttEnd34H8p6/f1300KauwrULws39e18
|
|
0ZaLNm9rgN/ZVf3h++/e124Vlc0vKsspHy+Yyi5+XbzPhijvCtduoiL/kA1ukWV27n0o7Sb8LIFj
|
|
CvWR5GQgUJdp1Pw8TS9+rPy6SDv/+e3d+0+4qw8f3v20+PliV37efEYBAB9FTKC+RHn/Cfxn3rdv
|
|
00Fube5O+iyCtHDs9BfPfz3q4sfFv9d91Ljhfy7ei0VO+nVTtdOkv/jpt0l2AX6iG1jXgKnnDuD4
|
|
ke2k/i8fzzz5UedkVcP4pwF+Wvz2FJl+3vt598urXf5Y6LNA5WcFOP7r0sW7b9a+W/xcu0Xpv5zk
|
|
Kfq3P9Dz9di/fCxS72MXVU1rpx9L4Bxl85Wmn5a+zP76Zuh3pL9ROWr87PN+//GHIl+oOtvn9XSU
|
|
qH+p0gQBFnx1uV+JLH5O5zv+PXW+WepXVVHZT0+oQezkIATcIm+ivPV/z5J/+cYj3ir4w0Lx09vC
|
|
e5n/y5/Y5LPPfdrqb88ga/PabxZRVfmp39l588m/6u+/e+OpP+dF7n1WZpJ9//Z4v372fDDz9eHB
|
|
7Juvs/BLMHzrxL9+9twXpJfhd1/DrpQ5Euu/vlss3wp9HXC/54C/Ld69m6zwdx3tC0d8daSv0V8B
|
|
n4b9YYF53sJelJV/ix6LZspw/sJtqyl5LJ5r/23htA1Imfm/gt9R7dqVB1LjhydAX4Gb+zksQF59
|
|
9+P7H//U+376afFuvh2/T6P85Xr/5c8C6OXyFY4BGuN+EE0+GeR201b+wkkLN5mmBY5TfMw8ngqL
|
|
CztXxCSXKMCYrRIElWkEJlEPYsSOeKBVZCAQTKBhApMwRFQzmCThE0YQu2CdEhgjbgmk9GluHpfR
|
|
/hhwJCZhGI5jt5FsAkOrObVyE6g2y1snyhMGFlDY1x+BoHpCMulTj5JYWNAYJmnKpvLxXgmQ8az1
|
|
4fUGxxcitMbbhDFcsiAItg04E+OSBIHTUYD1HI4FHH4kMREPknuYRMyhh3AARWMkfhCketqD1CWJ
|
|
mTCo/nhUScoQcInB1hpFhIKoIXLo5jLpwFCgsnLCx1QlEMlz/iFEGqzH3vWYcpRcThgWnEKm0QcS
|
|
rA8ek2a2IYYeowUanOZOlrbWSJUC4c7y2EMI3uJPMnMF/SSXdk6E495VLhzkWHps0rOhKwqk+xBI
|
|
DhJirhdUCTamMfXz2Hy303hM4DFJ8QL21BcPBULR+gcdYxoeiDqOFSqpi5B5PUISfGg46gFZBPo4
|
|
jdh8lueaWuVSMTURfbAUnLINr/QYuuYoMQV6l1aWxuZVTjlaLC14UzqZ+ziTGDzJzhiYoPLrt3uI
|
|
tXkVR47kAo09lo5BD76CH51cTt1snVpMOttLhY93yxChCQPI4OBecS7++h4p4Bdn4H97bJongtPk
|
|
s9gQnXku1vzsjjmX4/o4YUDkXkjHwDg5FXozU0fW4y5kyeYW0uJWlh536BKr0kMGjtzTkng6Ep62
|
|
uTWnQtiIqKnEsx7e1hLtzlXs7Upw9TwEnp0t9yzCGgUJIZConx9OHJArLkRYW0dW42G9OeR5Nzwk
|
|
yk1mX7du5RGHT7dka7N3AznmSif7y6tuKe2N1Al/1TUPRqH6E2GLVc27h9IptMLkCKQYRqPQJgzV
|
|
2m6WLsSipS3v3b1/WmXEYY1meLEVIU/arOGVkyie7ZsH05ZKpjFW4cpY0YkjySpSExNG2TS8nnJx
|
|
nrQmWh2WY3cP1eISP9wbaVK35ZXc60yC3VN/j9n7UFoK6zvjSTE2+Pvz6Mx322rnftfP8Y0XKIdv
|
|
Qd7AfK0nexBTMqRiErvCMa3Hegpfjdh58glW2oNMsKeAX8x6YJLZs9K8/ozjJkWL+JmECMvhQ54x
|
|
9rsTHwcoGrDi6Y4I+H7yY4/rJVPAbYymUH7C2D3uiUS3KQ1nrCAUkE1dJMneDQIJMQQx5SONxoEO
|
|
OEn1/Ig1eBBUeEDRuOT2WGGGE4bNypBLFh2PeIg3bEbg44PHiqNDbGIQm50LW6MJU62JHCGBrmc9
|
|
2F7WBJrrj1ssnTAK4sxwRgh5LLblhwNAclv3Gd+jC/etCfyfR8TMhcWQz8TBIbG8IIyAQ81w2n/C
|
|
mHWAwRzxd3WoBY7BZnsqGOWrOCKwGkMMNfO0Kci/joZgEocLjNnzgcmdehPHJY0FudXgsr+v44TB
|
|
I3jnMGnsK5veAhgi9iXGifkHMOC09Rh9cAw9sQ0asl6wKMk8mpzFYaaDSgG4F0wisQDDBRpjCINg
|
|
FIxhlhQ31xdSkkk6odXZFpTYOQpOOgw9ugM2cDQ+2MYa7JsEirGBrOuxsQy5nPMRdYjsTJ/j1iNw
|
|
FeSt1jY2+dd5yx1/pzZMOQXUIDcXeAzR7QlDRM8AMkUldXOmGmvYXPABjxqkYKO7VAY6JRU7kpXr
|
|
+Epu2BU3qFFXClFi27784LrDZsJwbNlDw0JzhZ6M0SMXE4iBHehCpHVkrQhpTFn2dsvsZYkiPEEB
|
|
GSEAwdiur9LS1U6P2U9JhGp4hnFpJo4FfkdJHcwV6Q5dV1Q9uNeeu7rV8PAjwdFg9RLtroifOr0k
|
|
uOiRTo/obNPhQIf42Fr4mtThWoSjitEdAmFW66UCe8WFjPk1YVNpL9srFbond7jrLg8tqAasIMpy
|
|
zkH0SY/6zVAwJrEc14zt14YRXdY+fcJ4qOd2XKB0/Kghw1ovd11t2o+zjt+txndo1ZDZ2T+uMVHT
|
|
VSXhedBAHoJIID9xm6wPQI3cXY+HR7vxtrJuCKh6kbXaW5KkVeJsdsjqsYsOwYSh0w5sMbu7LF8J
|
|
5T7U6LJdiTx+ca7RKlulGgS5Z1JSU2Llt32cHFipkaurtBrvNX5UtvNZjkufZ/r1/XyLl6yOpytL
|
|
Km8Fn+y4wkhlqZP5db0rooqy7xdL4wxzFVTX+6HaxuQJK5E5B1neSSovZ9ALB8091dDbbjVxhWNY
|
|
Ve5hn1VnI9OF0wpvaRm7SZuC1IRczwC7GnkhPt3muHV1YxUJfo+uh1sYnJy+vI0ZwuPV2uqWJYUH
|
|
bmBsi1zmFSxHrqwA+WIzLrHkwW4r+bad7xbOzJCnKIa3S3YvrzEBK1Dc0emzJW+SqysQfdEDorQG
|
|
9ZJlbQzEHQV8naPaF440YXzJk/7vHGK2xwuP+Gc5xITxyiP+WQ4x18oXHjFzCBy9kir1EFTAm0Zq
|
|
LYwS8MpiGhtfxiBRDXpxDWxk9g9Q2fzPPAhS6VFDAc/aiNGatUkPtZIStZFQ1qD0IlJa/5ZPAi5J
|
|
ySp1ETDomZMnvgiysZSBfMikrSDte/K5lqV6iwC5q7YN9I1dBZXUytDJNqU74MJsUyNNLAPopWK3
|
|
tzmLkCiDyl7WQnj9sm7Kd5kzgpoccdNeMw/6zPVB3pUwMgi4C7hj4AMFAf4G27oXH8NNT9zll/sK
|
|
S6wVlQwazjxWKWy20ZzXb9ne8ngGalPBWSUSj9xkc1drsXkZ8oOyvYT3e0rnYsGwx85xZB9wKeKg
|
|
cJKZnamYwiaMymZvzk6wtDUkxmdUg0mPad0YHtvzpjEfp2iMxvORhnx0kCVLf5Qa43WJsVoyfEyI
|
|
pzmf8ruM6xBr7dnBgzyxpqXuUPYaKahOaz1LrxNkS/Q3Ae5AC+xl6NbxAqXXlzghZBZHmOrM6Y6Y
|
|
ctAkltwlF7SKEsShjVh7QHuxMU0a08/eiu3x3M+07OijMcKFFltByXrpk8w+JNnZpnp3CfgjV1Ax
|
|
gUYCnWwYow42I5wHCcTzLXK0hMZN2DrPM/zCSqe9jRSlJnr70BPE4+zrwbk/xVIDHy2FAQyHoomT
|
|
Tt5jiM68nBQut35Y0qLclLiQrutxt/c0OlSqXAC8VrxW97lGoRWzhOnifE2zbF05W4xuyhg7JTUL
|
|
aqJ7SWDywhjlal0b+NLTpERBgnPW0+Nw99X2Ws72gOL27iER9jgzj7Uu09JaZ3n+hmCjjvZpjNst
|
|
vOWWTbuLrg+/1ltX8WpPauEDEvcunIgTxuMEHweWKCx2KQ9DU/UKdO/3za4Szm2iHYL+ss9AAttm
|
|
gZHq2pkUXFbV+FiJCKrpBms18zH75vax5jSo7FNunrVWY3Chvd8KKnHdaTt/6ealwaA1x17yTlft
|
|
8VBle3nAE+7R0MScC3MJofNCCkA9PGKBgGMYEwfB2QO5j8zUqa8F/EkWKCzGQJ5EZ05HTly1B01E
|
|
z813G5BY++RZ2sxbQS8ZveGPJNabp5kXAeoign6Tlt5+L8i5ZquY9+S+KEUHkmYMRFBxRrHnbl2X
|
|
rVemKnG+oB1yd9+zT+4c43jQ0wWmQRR6mTCkY1q3VG05Y120ZzKOMBe6Vy7I5Vz4ygPB3yY4G0FP
|
|
8RxiMx985YJPXsgRU58EuHj75gygTzejP+W/zKGe78UQN3yOJ1aMQV9hFH+GAfLRsza84WlPLAI/
|
|
9G/5JdcHftEfH+Y3/fHUG7/o8bv98dzzy3e8S+XCvgqB+VUf7sH0yDHpONdbRE8tAg9NWOzcTJ7q
|
|
TuAxe/AJ07c1Rs9okJvl1/0G60qvbdDzz5zO0FuPFQIHNp9y9Bd1CufYVx7dB26mAxwa8GMNrN/U
|
|
oGbNZ3EQ7inLzHy5tRg9AXJrN8cB59cCUBeCiVO7zKM0jU0MamhnRThkg/NMmBOGb6StNeD9tDfA
|
|
7czsAWopDdnGoXUHtA+s/k0vNPkBcxEI13jVd/axp85va3LpwGggXXWw12Gwr/JGAH0b8CPboiZd
|
|
QO1l0mk/UHukud4C+w5uRoNzpCmoW6GbgbMyaQNkga2pQINB18lOXOCJzSWPFOhZcwzdgrsQnne7
|
|
nvjBi+7cP2BbtBeDOW5uOLGf3z94FasKIguOqJl+8ss/6Kumns4cuWbqq5592TN/RNIbn5Qo6qbi
|
|
O4F0P9txxPAwagqPlftztO8cWBzdN/jz3b7GD6JHYP/Zp4ToAMaA74M+EGSft3hEGMuf8EwjnTk/
|
|
nz/P7SLipB/ogQ6xNX0fDqNncMCfHqGLCMM0ZzFa+6lPJYQ5p81vW4HkCvidYf6kb+P/oB965g8K
|
|
C6uR0rdjX1DNKc5pOSTquI8uQ6KXxYaKBn+30/09tK4kMpJPgUIQkbENEPbuezNPPje2Um83SgyX
|
|
GTCJb6MnGVIpgncdQg1qz2bvPfxYD9fewCXDomx9S+HQJuX6W3VAL+v5WZMudRQZk9ZdOk6GIUtC
|
|
PqEb/uwSIrtR7/edzqgEdtpEwq7p2J5OQV+RLrmtTvFwFpf03M/VrRyTZ73qVod7v7Jh2Dwe5J25
|
|
JqFOU2qEu1sP+CRotklediycKfLjeIZzjJQsvKmiGSNQhxuJpKa+hoWUizaE1PuIRGzJqropwgVB
|
|
oo1hr870MZLgnXF5ZIpr6mF0L8aSy2gVnTAuoB4WEd4d5NPVC9TMotYXERKlTcwQ2KiB/C48AEfH
|
|
Qbyq4CN8xTFnTvf/ebOc3isnjD95s0QF0nx9s+y+zMmz782xL0SgEmRpA3x1w1Ff9/74xcxKEPdS
|
|
IEFTz6GgU0+BK/UZ5Gwbl4gZwycxEw+Kqa5QmMkh4OzgzEVPnDAiAOGBFaBW4wkDmj1G4RyElKgj
|
|
NlLCq8zsp085MNh/+R4t1Q8yxoSv8PUpTt7izZwf2BTHZZ3pIZpUIpuLkL1nNL6sYcHqcKm237wp
|
|
T2+RCjgXweXd2Zp7ZM8W6dG5bZsqo0nrJBTx8EC0+CQQdzEGnabTnkzofu1pYkWl4E7XSniECdxy
|
|
vLYavPMcL9LW5SToJFNnos+uqweOHriUZ1ntIYZUonc7ltEQ6oTRtwOHNwez2sVREskHN+bqG3ua
|
|
eaEbJ8XpyO8CeD9QJc8nbLP2C2R3A437ISUNyt5Yd0TbDNcl11/DSsOzdbi/VhCC0KE6v1vqVNkq
|
|
45ZnG6fiV2NwzInxCNth3BwL0+8814jE6+1W1EeWtpWbSZJOJNYXmWRXa7vLnAljE692eHjZ4y5u
|
|
y1u63De0IzKca7As48Z3XshVF+3XiLNz0JIMh/JOpbiNLlMi672uO0wYzOCZjRxcxj3D+gVenGIE
|
|
MvFUGGXuRps2RzMcgWIRolHXpGUP6sMsQt1hspUBnVKUn/WQj2u6j3SXd9Xz0QtEzoM7qTu5y7gR
|
|
q9gNNsrlEMLdikBt9bFvBnfbUIh6voTw7eDsyTmPKUvF0bHqWLbHe3VRHyRZnNeSGKsB73q66Vsk
|
|
taxWYmwz1tYVFG/vOQhlM0gUkyvIab3nv2caJ1udU1F3pDMty7stubTE4OJqm0i0ECfrJIkLtraC
|
|
HwRWKzlqpfhEIqYH09eT9WrOhQyt8YEoyBlnXtAT37WHIQ03TIuEHbnRxZDdLun0iok9PUC79prU
|
|
m5beZzfQUelEXnhzb/pIROKx3F7qCttYIFGh5dXNzFzID7u8vKykA8Uejf7XXz//S4nKvW//ofS/
|
|
QastYw==
|
|
""")
|
|
|
|
##file distutils-init.py
|
|
DISTUTILS_INIT = convert("""
|
|
eJytV1uL4zYUfvevOE0ottuMW9q3gVDa3aUMXXbLMlDKMBiNrSTqOJKRlMxkf33PkXyRbGe7Dw2E
|
|
UXTu37lpxLFV2oIyifAncxmOL0xLIfcG+gv80x9VW6maw7o/CANSWWBwFtqeWMPlGY6qPjV8A0bB
|
|
C4eKSTgZ5LRgFeyErMEeOBhbN+Ipgeizhjtnhkn7DdyjuNLPoCS0l/ayQTG0djwZC08cLXozeMss
|
|
aG5EzQ0IScpnWtHSTXuxByV/QCmxE7y+eS0uxWeoheaVVfqSJHiU7Mhhi6gULbOHorshkrEnKxpT
|
|
0n3A8Y8SMpuwZx6aoix3ouFlmW8gHRSkeSJ2g7hU+kiHLDaQw3bmRDaTGfTnty7gPm0FHbIBg9U9
|
|
oh1kZzAFLaue2R6htPCtAda2nGlDSUJ4PZBgCJBGVcwKTAMz/vJiLD+Oin5Z5QlvDPdulC6EsiyE
|
|
NFzb7McNTKJzbJqzphx92VKRFY1idenzmq3K0emRcbWBD0ryqc4NZGmKOOOX9Pz5x+/l27tP797c
|
|
f/z0d+4NruGNai8uAM0bfsYaw8itFk8ny41jsfpyO+BWlpqfhcG4yxLdi/0tQqoT4a8Vby382mt8
|
|
p7XSo7aWGdPBc+b6utaBmCQ7rQKQoWtAuthQCiold2KfJIPTT8xwg9blPumc+YDZC/wYGdAyHpJk
|
|
vUbHbHWAp5No6pK/WhhLEWrFjUwtPEv1Agf8YmnsuXUQYkeZoHm8ogP16gt2uHoxcEMdf2C6pmbw
|
|
hUMsWGhanboh4IzzmsIpWs134jVPqD/c74bZHdY69UKKSn/+KfVhxLgUlToemayLMYQOqfEC61bh
|
|
cbhwaqoGUzIyZRFHPmau5juaWqwRn3mpWmoEA5nhzS5gog/5jbcFQqOZvmBasZtwYlG93k5GEiyw
|
|
buHhMWLjDarEGpMGB2LFs5nIJkhp/nUmZneFaRth++lieJtHepIvKgx6PJqIlD9X2j6pG1i9x3pZ
|
|
5bHuCPFiirGHeO7McvoXkz786GaKVzC9DSpnOxJdc4xm6NSVq7lNEnKdVlnpu9BNYoKX2Iq3wvgh
|
|
gGEUM66kK6j4NiyoneuPLSwaCWDxczgaolEWpiMyDVDb7dNuLAbriL8ig8mmeju31oNvQdpnvEPC
|
|
1vAXbWacGRVrGt/uXN/gU0CDDwgooKRrHfTBb1/s9lYZ8ZqOBU0yLvpuP6+K9hLFsvIjeNhBi0KL
|
|
MlOuWRn3FRwx5oHXjl0YImUx0+gLzjGchrgzca026ETmYJzPD+IpuKzNi8AFn048Thd63OdD86M6
|
|
84zE8yQm0VqXdbbgvub2pKVnS76icBGdeTHHXTKspUmr4NYo/furFLKiMdQzFjHJNcdAnMhltBJK
|
|
0/IKX3DVFqvPJ2dLE7bDBkH0l/PJ29074+F0CsGYOxsb7U3myTUncYfXqnLLfa6sJybX4g+hmcjO
|
|
kMRBfA1JellfRRKJcyRpxdS4rIl6FdmQCWjo/o9Qz7yKffoP4JHjOvABcRn4CZIT2RH4jnxmfpVG
|
|
qgLaAvQBNfuO6X0/Ux02nb4FKx3vgP+XnkX0QW9pLy/NsXgdN24dD3LxO2Nwil7Zlc1dqtP3d7/h
|
|
kzp1/+7hGBuY4pk0XD/0Ao/oTe/XGrfyM773aB7iUhgkpy+dwAMalxMP0DrBcsVw/6p25+/hobP9
|
|
GBknrWExDhLJ1bwt1NcCNblaFbMKCyvmX0PeRaQ=
|
|
""")
|
|
|
|
##file distutils.cfg
|
|
DISTUTILS_CFG = convert("""
|
|
eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH
|
|
xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg
|
|
9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q=
|
|
""")
|
|
|
|
##file activate_this.py
|
|
ACTIVATE_THIS = convert("""
|
|
eJyNU01v2zAMvetXEB4K21jmDOstQA4dMGCHbeihlyEIDMWmG62yJEiKE//7kXKdpN2KzYBt8euR
|
|
fKSyLPs8wiEo8wh4wqZTGou4V6Hm0wJa1cSiTkJdr8+GsoTRHuCotBayiWqQEYGtMCgfD1KjGYBe
|
|
5a3p0cRKiAe2NtLADikftnDco0ko/SFEVgEZ8aRC5GLux7i3BpSJ6J1H+i7A2CjiHq9z7JRZuuQq
|
|
siwTIvpxJYCeuWaBpwZdhB+yxy/eWz+ZvVSU8C4E9FFZkyxFsvCT/ZzL8gcz9aXVE14Yyp2M+2W0
|
|
y7n5mp0qN+avKXvbsyyzUqjeWR8hjGE+2iCE1W1tQ82hsCZN9UzlJr+/e/iab8WfqsmPI6pWeUPd
|
|
FrMsd4H/55poeO9n54COhUs+sZNEzNtg/wanpjpuqHJaxs76HtZryI/K3H7KJ/KDIhqcbJ7kI4ar
|
|
XL+sMgXnX0D+Te2Iy5xdP8yueSlQB/x/ED2BTAtyE3K4SYUN6AMNfbO63f4lBW3bUJPbTL+mjSxS
|
|
PyRfJkZRgj+VbFv+EzHFi5pKwUEepa4JslMnwkowSRCXI+m5XvEOvtuBrxHdhLalG0JofYBok6qj
|
|
YdN2dEngUlbC4PG60M1WEN0piu7Nq7on0mgyyUw3iV1etLo6r/81biWdQ9MWHFaePWZYaq+nmp+t
|
|
s3az+sj7eA0jfgPfeoN1
|
|
""")
|
|
|
|
MH_MAGIC = 0xfeedface
|
|
MH_CIGAM = 0xcefaedfe
|
|
MH_MAGIC_64 = 0xfeedfacf
|
|
MH_CIGAM_64 = 0xcffaedfe
|
|
FAT_MAGIC = 0xcafebabe
|
|
BIG_ENDIAN = '>'
|
|
LITTLE_ENDIAN = '<'
|
|
LC_LOAD_DYLIB = 0xc
|
|
maxint = majver == 3 and getattr(sys, 'maxsize') or getattr(sys, 'maxint')
|
|
|
|
|
|
class fileview(object):
|
|
"""
|
|
A proxy for file-like objects that exposes a given view of a file.
|
|
Modified from macholib.
|
|
"""
|
|
|
|
def __init__(self, fileobj, start=0, size=maxint):
|
|
if isinstance(fileobj, fileview):
|
|
self._fileobj = fileobj._fileobj
|
|
else:
|
|
self._fileobj = fileobj
|
|
self._start = start
|
|
self._end = start + size
|
|
self._pos = 0
|
|
|
|
def __repr__(self):
|
|
return '<fileview [%d, %d] %r>' % (
|
|
self._start, self._end, self._fileobj)
|
|
|
|
def tell(self):
|
|
return self._pos
|
|
|
|
def _checkwindow(self, seekto, op):
|
|
if not (self._start <= seekto <= self._end):
|
|
raise IOError("%s to offset %d is outside window [%d, %d]" % (
|
|
op, seekto, self._start, self._end))
|
|
|
|
def seek(self, offset, whence=0):
|
|
seekto = offset
|
|
if whence == os.SEEK_SET:
|
|
seekto += self._start
|
|
elif whence == os.SEEK_CUR:
|
|
seekto += self._start + self._pos
|
|
elif whence == os.SEEK_END:
|
|
seekto += self._end
|
|
else:
|
|
raise IOError("Invalid whence argument to seek: %r" % (whence,))
|
|
self._checkwindow(seekto, 'seek')
|
|
self._fileobj.seek(seekto)
|
|
self._pos = seekto - self._start
|
|
|
|
def write(self, bytes):
|
|
here = self._start + self._pos
|
|
self._checkwindow(here, 'write')
|
|
self._checkwindow(here + len(bytes), 'write')
|
|
self._fileobj.seek(here, os.SEEK_SET)
|
|
self._fileobj.write(bytes)
|
|
self._pos += len(bytes)
|
|
|
|
def read(self, size=maxint):
|
|
assert size >= 0
|
|
here = self._start + self._pos
|
|
self._checkwindow(here, 'read')
|
|
size = min(size, self._end - here)
|
|
self._fileobj.seek(here, os.SEEK_SET)
|
|
bytes = self._fileobj.read(size)
|
|
self._pos += len(bytes)
|
|
return bytes
|
|
|
|
|
|
def read_data(file, endian, num=1):
|
|
"""
|
|
Read a given number of 32-bits unsigned integers from the given file
|
|
with the given endianness.
|
|
"""
|
|
res = struct.unpack(endian + 'L' * num, file.read(num * 4))
|
|
if len(res) == 1:
|
|
return res[0]
|
|
return res
|
|
|
|
|
|
def mach_o_change(path, what, value):
|
|
"""
|
|
Replace a given name (what) in any LC_LOAD_DYLIB command found in
|
|
the given binary with a new name (value), provided it's shorter.
|
|
"""
|
|
|
|
def do_macho(file, bits, endian):
|
|
# Read Mach-O header (the magic number is assumed read by the caller)
|
|
cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = read_data(file, endian, 6)
|
|
# 64-bits header has one more field.
|
|
if bits == 64:
|
|
read_data(file, endian)
|
|
# The header is followed by ncmds commands
|
|
for n in range(ncmds):
|
|
where = file.tell()
|
|
# Read command header
|
|
cmd, cmdsize = read_data(file, endian, 2)
|
|
if cmd == LC_LOAD_DYLIB:
|
|
# The first data field in LC_LOAD_DYLIB commands is the
|
|
# offset of the name, starting from the beginning of the
|
|
# command.
|
|
name_offset = read_data(file, endian)
|
|
file.seek(where + name_offset, os.SEEK_SET)
|
|
# Read the NUL terminated string
|
|
load = file.read(cmdsize - name_offset).decode()
|
|
load = load[:load.index('\0')]
|
|
# If the string is what is being replaced, overwrite it.
|
|
if load == what:
|
|
file.seek(where + name_offset, os.SEEK_SET)
|
|
file.write(value.encode() + '\0'.encode())
|
|
# Seek to the next command
|
|
file.seek(where + cmdsize, os.SEEK_SET)
|
|
|
|
def do_file(file, offset=0, size=maxint):
|
|
file = fileview(file, offset, size)
|
|
# Read magic number
|
|
magic = read_data(file, BIG_ENDIAN)
|
|
if magic == FAT_MAGIC:
|
|
# Fat binaries contain nfat_arch Mach-O binaries
|
|
nfat_arch = read_data(file, BIG_ENDIAN)
|
|
for n in range(nfat_arch):
|
|
# Read arch header
|
|
cputype, cpusubtype, offset, size, align = read_data(file, BIG_ENDIAN, 5)
|
|
do_file(file, offset, size)
|
|
elif magic == MH_MAGIC:
|
|
do_macho(file, 32, BIG_ENDIAN)
|
|
elif magic == MH_CIGAM:
|
|
do_macho(file, 32, LITTLE_ENDIAN)
|
|
elif magic == MH_MAGIC_64:
|
|
do_macho(file, 64, BIG_ENDIAN)
|
|
elif magic == MH_CIGAM_64:
|
|
do_macho(file, 64, LITTLE_ENDIAN)
|
|
|
|
assert(len(what) >= len(value))
|
|
do_file(open(path, 'r+b'))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|
|
## TODO:
|
|
## Copy python.exe.manifest
|
|
## Monkeypatch distutils.sysconfig
|