simplify virtualenv creation script
authorymh <ymh.work@gmail.com>
Tue, 29 Mar 2011 13:33:43 +0200
changeset 50 0d59e0522d36
parent 49 8ccbd2f3820e
child 51 28d35ffe83ab
simplify virtualenv creation script
virtualenv/res/lib/lib_create_env.py
virtualenv/res/lib/patch.py
virtualenv/res/src/Django-1.2.4.tar.gz
virtualenv/res/src/Imaging-1.1.7.tar.gz
virtualenv/res/src/JCC-2.6-py2.6-win32.egg
virtualenv/res/src/MySQL-python-1.2.3.tar.gz
virtualenv/res/src/PIL-1.1.7.win32-py2.6.exe
virtualenv/res/src/distribute-0.6.14.tar.gz
virtualenv/res/src/django-extensions-0.6.tar.gz
virtualenv/res/src/django-oauth-plus.tar.gz
virtualenv/res/src/django-piston-0.2.2-modified.tar.gz
virtualenv/res/src/django-registration.tar.gz
virtualenv/res/src/django-tagging-0.3.1.tar.gz
virtualenv/res/src/httplib2-0.6.0.tar.gz
virtualenv/res/src/jogging-0.2.2.tar.gz
virtualenv/res/src/lucene-3.0.2-py2.6-win32.egg
virtualenv/res/src/lxml-2.2.8-py2.6-win32.egg
virtualenv/res/src/lxml-2.2.8.tar.gz
virtualenv/res/src/mercurial-1.7.5.tar.gz
virtualenv/res/src/psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip
virtualenv/res/src/psycopg2-2.3.2.tar.gz
virtualenv/res/src/pylucene-3.0.3-1-src.tar.gz
virtualenv/res/src/python-oauth2-1.2.1-modified.tar.gz
virtualenv/res/src/setuptools_hg-0.2.tar.gz
virtualenv/setup/create_python_env.py
virtualenv/setup/res/res_create_env.py
virtualenv/setup/res/src/distribute-0.6.14.tar.gz
virtualenv/setup/res/src/mercurial-1.7.5.tar.gz
virtualenv/setup/res/src/setuptools_hg-0.2.tar.gz
virtualenv/web/create_python_env.py
virtualenv/web/res/lib/patch.py
virtualenv/web/res/res_create_env.py
virtualenv/web/res/src/Django-1.2.4.tar.gz
virtualenv/web/res/src/Imaging-1.1.7.tar.gz
virtualenv/web/res/src/JCC-2.6-py2.6-win32.egg
virtualenv/web/res/src/MySQL-python-1.2.3.tar.gz
virtualenv/web/res/src/PIL-1.1.7.win32-py2.6.exe
virtualenv/web/res/src/distribute-0.6.14.tar.gz
virtualenv/web/res/src/django-extensions-0.6.tar.gz
virtualenv/web/res/src/django-oauth-plus.tar.gz
virtualenv/web/res/src/django-piston-0.2.2-modified.tar.gz
virtualenv/web/res/src/django-registration.tar.gz
virtualenv/web/res/src/django-tagging-0.3.1.tar.gz
virtualenv/web/res/src/httplib2-0.6.0.tar.gz
virtualenv/web/res/src/jogging-0.2.2.tar.gz
virtualenv/web/res/src/lucene-3.0.2-py2.6-win32.egg
virtualenv/web/res/src/lxml-2.2.8-py2.6-win32.egg
virtualenv/web/res/src/lxml-2.2.8.tar.gz
virtualenv/web/res/src/psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip
virtualenv/web/res/src/psycopg2-2.3.2.tar.gz
virtualenv/web/res/src/pylucene-3.0.3-1-src.tar.gz
virtualenv/web/res/src/python-oauth2-1.2.1-modified.tar.gz
virtualenv/web/res/src/setuptools_hg-0.2.tar.gz
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/virtualenv/res/lib/lib_create_env.py	Tue Mar 29 13:33:43 2011 +0200
@@ -0,0 +1,292 @@
+import sys
+import os
+import os.path
+import shutil
+import tarfile
+import zipfile
+import urllib
+import platform
+import patch
+
+join = os.path.join
+system_str = platform.system()
+
+
+URLS = {
+    'DISTRIBUTE': {'setup': 'distribute', 'url':'http://pypi.python.org/packages/source/d/distribute/distribute-0.6.14.tar.gz', 'local':"distribute-0.6.14.tar.gz"},
+    'DJANGO': {'setup': 'django', 'url': 'http://www.djangoproject.com/download/1.2.4/tarball/', 'local':"Django-1.2.4.tar.gz"},
+    'JOGGING': {'setup': 'jogging', 'url': 'http://github.com/zain/jogging/tarball/v0.2.2', 'local':"jogging-0.2.2.tar.gz"},
+    'DJANGO-EXTENSIONS': { 'setup': 'django-extensions', 'url':'https://github.com/django-extensions/django-extensions/tarball/0.6', 'local':"django-extensions-0.6.tar.gz"},
+    'DJANGO-REGISTRATION': { 'setup': 'django-registration', 'url':'http://bitbucket.org/ubernostrum/django-registration/get/tip.tar.gz', 'local':"django-registration.tar.gz"},
+    'DJANGO-TAGGING': { 'setup': 'django-tagging', 'url':'http://django-tagging.googlecode.com/files/django-tagging-0.3.1.tar.gz', 'local':"django-tagging-0.3.1.tar.gz"},
+    'DJANGO-PISTON': { 'setup': 'django-piston', 'url':"django-piston-0.2.2-modified.tar.gz", 'local':"django-piston-0.2.2-modified.tar.gz"},
+    'OAUTH2': { 'setup': 'python-oauth2', 'url':"python-oauth2-1.2.1-modified.tar.gz", 'local':"python-oauth2-1.2.1-modified.tar.gz"},
+    'HTTPLIB2': { 'setup': 'python-oauth2', 'url':'http://httplib2.googlecode.com/files/httplib2-0.6.0.tar.gz', 'local':"httplib2-0.6.0.tar.gz"},
+    'DJANGO-OAUTH-PLUS': { 'setup': 'django-oauth-plus', 'url':'http://bitbucket.org/david/django-oauth-plus/get/f314f018e473.gz', 'local':"django-oauth-plus.tar.gz"},
+    'MYSQL': { 'setup': 'mysql-python', 'url': 'http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.3/MySQL-python-1.2.3.tar.gz/download', 'local':"MySQL-python-1.2.3.tar.gz"},
+    'SETUPTOOLS-HG': { 'setup':'mercurial_hg', 'url':'http://pypi.python.org/packages/source/s/setuptools_hg/setuptools_hg-0.2.tar.gz', 'local':"setuptools_hg-0.2.tar.gz"},
+    'MERCURIAL': {'setup':'mercurial', 'url':'http://pypi.python.org/packages/source/d/mercurial/mercurial-1.7.5.tar.gz', 'local':"mercurial-1.7.5.tar.gz"}
+}
+
+if system_str == 'Windows':
+    URLS.update({
+        'PSYCOPG2': {'setup': 'psycopg2','url': 'psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip', 'local':"psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip"},
+        'JCC': {'setup': 'http://pylucene-win32-binary.googlecode.com/files/JCC-2.6-py2.6-win32.egg', 'local':"JCC-2.6-py2.6-win32.egg"},
+        'PYLUCENE': {'setup': 'http://pylucene-win32-binary.googlecode.com/files/lucene-3.0.2-py2.6-win32.egg', 'local':"lucene-3.0.2-py2.6-win32.egg"},
+        'PIL': {'setup': 'pil', 'url': 'http://effbot.org/media/downloads/PIL-1.1.7.win32-py2.6.exe', 'local':"PIL-1.1.7.win32-py2.6.exe"},
+        'LXML': {'setup': 'lxml', 'url': 'http://pypi.python.org/packages/2.6/l/lxml/lxml-2.2.8-py2.6-win32.egg', 'local':"lxml-2.2.8-py2.6-win32.egg"}
+    })
+else:
+    URLS.update({
+        'PSYCOPG2': {'setup': 'psycopg2','url': 'http://initd.org/psycopg/tarballs/PSYCOPG-2-3/psycopg2-2.3.2.tar.gz', 'local':"psycopg2-2.3.2.tar.gz"},
+        'PYLUCENE': {'setup': 'http://apache.crihan.fr/dist/lucene/pylucene/pylucene-3.0.3-1-src.tar.gz', 'url': 'http://apache.crihan.fr/dist/lucene/pylucene/pylucene-3.0.3-1-src.tar.gz', 'local':"pylucene-3.0.3-1-src.tar.gz"},
+        'PIL': {'setup': 'pil', 'url': 'http://effbot.org/downloads/Imaging-1.1.7.tar.gz', 'local':"Imaging-1.1.7.tar.gz"},
+        'LXML': {'setup': 'lxml', 'url':"lxml_2.2.8.tar.gz", 'local':"lxml-2.2.8.tar.gz"}
+    })
+
+
+
+class ResourcesEnv(object):
+
+    def __init__(self, src_base, urls, normal_installs):
+        self.src_base = src_base
+        self.URLS = {}
+        self.__init_url(urls)
+        self.NORMAL_INSTALL = normal_installs
+
+    def get_src_base_path(self, fpath):
+        return os.path.abspath(os.path.join(self.src_base, fpath)).replace("\\","/")
+    
+    def __add_package_def(self, key, setup, url, local):
+        self.URLS[key] = {'setup':setup, 'url':url, 'local':self.get_src_base_path(local)}
+
+    def __init_url(self, urls):
+        for key, url_dict in urls.items():
+            url = url_dict['url']
+            if not url.startswith("http://"):
+                url = self.get_src_base_path(url)
+            self.__add_package_def(key, url_dict["setup"], url, url_dict["local"])
+
+def ensure_dir(dir, logger):
+    if not os.path.exists(dir):
+        logger.notify('Creating directory %s' % dir)
+        os.makedirs(dir)
+
+def extend_parser(parser):    
+    parser.add_option(
+        '--index-url',
+        metavar='INDEX_URL',
+        dest='index_url',
+        default='http://pypi.python.org/simple/',
+        help='base URL of Python Package Index')
+    parser.add_option(
+        '--type-install',
+        metavar='type_install',
+        dest='type_install',
+        default='local',
+        help='type install : local, url, setup')
+    parser.add_option(
+        '--ignore-packages',
+        metavar='ignore_packages',
+        dest='ignore_packages',
+        default=None,
+        help='list of comma separated keys for package to ignore')
+
+def adjust_options(options, args):
+    pass
+
+
+def install_pylucene(option_str, extra_env, res_source_key, home_dir, tmp_dir, src_dir, res_env, logger, call_subprocess, filter_python_develop):
+    
+    logger.notify("Get Pylucene from %s " % res_env.URLS['PYLUCENE'][res_source_key])
+    pylucene_src = os.path.join(src_dir,"pylucene.tar.gz")
+    if res_source_key == 'local':
+        shutil.copy(res_env.URLS['PYLUCENE'][res_source_key], pylucene_src)
+    else:
+        urllib.urlretrieve(res_env.URLS['PYLUCENE'][res_source_key], pylucene_src)
+    tf = tarfile.open(pylucene_src,'r:gz')
+    pylucene_base_path = os.path.join(src_dir,"pylucene") 
+    logger.notify("Extract Pylucene to %s " % pylucene_base_path)
+    tf.extractall(pylucene_base_path)
+    tf.close()
+    
+    pylucene_src_path = os.path.join(pylucene_base_path, os.listdir(pylucene_base_path)[0])
+    jcc_src_path = os.path.abspath(os.path.join(pylucene_src_path,"jcc"))
+    
+    #install jcc
+
+    #patch for linux
+    if system_str == 'Linux' :
+        olddir = os.getcwd()
+        patch_dest_path = os.path.join(lib_dir,'site-packages','setuptools-0.6c11-py'+'%s.%s' % (sys.version_info[0], sys.version_info[1])+'.egg')
+        if os.path.isfile(patch_dest_path):
+            # must unzip egg
+            # rename file and etract all
+            shutil.move(patch_dest_path, patch_dest_path + ".zip")
+            zf = zipfile.ZipFile(patch_dest_path + ".zip",'r')
+            zf.extractall(patch_dest_path)
+            os.remove(patch_dest_path + ".zip")
+        logger.notify("Patch jcc : %s " % (patch_dest_path))
+        os.chdir(patch_dest_path)
+        p = patch.fromfile(os.path.join(jcc_src_path,"jcc","patches","patch.43.0.6c11"))
+        p.apply()
+        os.chdir(olddir)
+
+    logger.notify("Install jcc")
+    call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'python')), 'setup.py', 'install'],
+                    cwd=jcc_src_path,
+                    filter_stdout=filter_python_develop,
+                    show_stdout=True)
+    #install pylucene
+    
+    logger.notify("Install pylucene")
+    #modify makefile
+    makefile_path = os.path.join(pylucene_src_path,"Makefile")
+    logger.notify("Modify makefile %s " % makefile_path)
+    shutil.move( makefile_path, makefile_path+"~" )
+
+    destination= open( makefile_path, "w" )
+    source= open( makefile_path+"~", "r" )
+    destination.write("PREFIX_PYTHON="+os.path.abspath(home_dir)+"\n")
+    destination.write("ANT=ant\n")
+    destination.write("PYTHON=$(PREFIX_PYTHON)/bin/python\n")
+    
+    if system_str == "Darwin":
+        if sys.version_info >= (2,6):
+            destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\n")
+        else:
+            destination.write("JCC=$(PYTHON) -m jcc --shared --arch x86_64 --arch i386\n")
+        destination.write("NUM_FILES=2\n")
+    elif system_str == "Windows":
+        destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\n")
+        destination.write("NUM_FILES=2\n")
+    else:
+        if sys.version_info >= (2,6) and sys.version_info <= (2,7):
+            destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared\n")
+        else:
+            destination.write("JCC=$(PYTHON) -m jcc --shared\n")
+        destination.write("NUM_FILES=2\n")
+    for line in source:
+        destination.write( line )
+    source.close()
+    destination.close()
+    os.remove(makefile_path+"~" )
+
+    logger.notify("pylucene make")
+    call_subprocess(['make'],
+                    cwd=os.path.abspath(pylucene_src_path),
+                    filter_stdout=filter_python_develop,
+                    show_stdout=True)
+
+    logger.notify("pylucene make install")
+    call_subprocess(['make', 'install'],
+                    cwd=os.path.abspath(pylucene_src_path),
+                    filter_stdout=filter_python_develop,
+                    show_stdout=True)
+    
+
+def install_psycopg2(option_str, extra_env, res_source_key, home_dir, tmp_dir, src_dir, res_env, logger, call_subprocess, filter_python_develop):
+    psycopg2_src = os.path.join(src_dir,"psycopg2.zip")
+    shutil.copy(res_env.URLS['PSYCOPG2'][res_source_key], psycopg2_src)
+    #extract psycopg2
+    zf = zipfile.ZipFile(psycopg2_src)
+    psycopg2_base_path = os.path.join(src_dir,"psycopg2")
+    zf.extractall(psycopg2_base_path)
+    zf.close()
+    
+    psycopg2_src_path = os.path.join(psycopg2_base_path, os.listdir(psycopg2_base_path)[0])
+    shutil.copytree(os.path.join(psycopg2_src_path, 'psycopg2'), os.path.abspath(os.path.join(home_dir, 'Lib', 'psycopg2')))
+    shutil.copy(os.path.join(psycopg2_src_path, 'psycopg2-2.0.10-py2.6.egg-info'), os.path.abspath(os.path.join(home_dir, 'Lib', 'site-packages')))
+
+
+
+def lib_generate_install_methods(path_locations, src_base, Logger, call_subprocess, normal_installs, urls=None):
+    
+    all_urls = URLS.copy()
+    if urls is not None:
+        all_urls.update(urls)
+        
+    res_env = ResourcesEnv(src_base, all_urls, normal_installs)
+
+    def filter_python_develop(line):
+        if not line.strip():
+            return Logger.DEBUG
+        for prefix in ['Searching for', 'Reading ', 'Best match: ', 'Processing ',
+                       'Moving ', 'Adding ', 'running ', 'writing ', 'Creating ',
+                       'creating ', 'Copying ']:
+            if line.startswith(prefix):
+                return Logger.DEBUG
+        return Logger.NOTIFY
+    
+    
+    def normal_install(key, method, option_str, extra_env, res_source_key, home_dir, tmp_dir, res_env, logger, call_subprocess):
+        logger.notify("Install %s from %s with %s" % (key,res_env.URLS[key][res_source_key],method))
+        if method == 'pip':
+            if sys.platform == 'win32':
+                args = [os.path.abspath(os.path.join(home_dir, 'Scripts', 'pip')), 'install', '-E', os.path.abspath(home_dir), res_env.URLS[key][res_source_key]]
+            else:
+                args = [os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), res_env.URLS[key][res_source_key]]
+            if option_str :
+                args.insert(4,option_str)
+            call_subprocess(args,
+                    cwd=os.path.abspath(tmp_dir),
+                    filter_stdout=filter_python_develop,
+                    show_stdout=True,
+                    extra_env=extra_env)
+        else:
+            if sys.platform == 'win32':
+                args = [os.path.abspath(os.path.join(home_dir, 'Scripts', 'easy_install')), res_env.URLS[key][res_source_key]]
+            else:
+                args = [os.path.abspath(os.path.join(home_dir, 'bin', 'easy_install')), res_env.URLS[key][res_source_key]]
+            if option_str :
+                args.insert(1,option_str)
+            call_subprocess(args,
+                    cwd=os.path.abspath(tmp_dir),
+                    filter_stdout=filter_python_develop,
+                    show_stdout=True,
+                    extra_env=extra_env)            
+ 
+    
+    def after_install(options, home_dir):
+        
+        global logger
+        
+        verbosity = options.verbose - options.quiet
+        logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)])
+
+        
+        home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
+        base_dir = os.path.dirname(home_dir)
+        src_dir = os.path.join(home_dir, 'src')
+        tmp_dir = os.path.join(home_dir, 'tmp')
+        ensure_dir(src_dir, logger)
+        ensure_dir(tmp_dir, logger)
+        system_str = platform.system()
+        
+        res_source_key = options.type_install
+        
+        ignore_packages = []
+        
+        if options.ignore_packages :
+            ignore_packages = options.ignore_packages.split(",")
+        
+        logger.indent += 2
+        try:    
+            for key, method, option_str, extra_env in res_env.NORMAL_INSTALL:
+                if key not in ignore_packages:
+                    if callable(method):
+                        method(option_str, extra_env, res_source_key, home_dir, tmp_dir, src_dir, res_env, logger, call_subprocess, filter_python_develop)
+                    else:
+                        normal_install(key, method, option_str, extra_env, res_source_key, home_dir, tmp_dir, res_env, logger, call_subprocess)
+                            
+            logger.notify("Clear source dir")
+            shutil.rmtree(src_dir)
+    
+        finally:
+            logger.indent -= 2
+        script_dir = join(base_dir, bin_dir)
+        logger.notify('Run "%s Package" to install new packages that provide builds'
+                      % join(script_dir, 'easy_install'))
+    
+
+    return adjust_options, extend_parser, after_install
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/virtualenv/res/lib/patch.py	Tue Mar 29 13:33:43 2011 +0200
@@ -0,0 +1,589 @@
+""" Patch utility to apply unified diffs
+
+    Brute-force line-by-line non-recursive parsing 
+
+    Copyright (c) 2008-2010 anatoly techtonik
+    Available under the terms of MIT license
+
+    Project home: http://code.google.com/p/python-patch/
+
+
+    $Id: patch.py 76 2010-04-08 19:10:21Z techtonik $
+    $HeadURL: https://python-patch.googlecode.com/svn/trunk/patch.py $
+"""
+
+__author__ = "techtonik.rainforce.org"
+__version__ = "10.04"
+
+import copy
+import logging
+import re
+# cStringIO doesn't support unicode in 2.5
+from StringIO import StringIO
+from logging import debug, info, warning
+
+from os.path import exists, isfile, abspath
+from os import unlink
+
+
+#------------------------------------------------
+# Logging is controlled by "python_patch" logger
+
+debugmode = False
+
+logger = logging.getLogger("python_patch")
+loghandler = logging.StreamHandler()
+logger.addHandler(loghandler)
+
+debug = logger.debug
+info = logger.info
+warning = logger.warning
+
+#: disable library logging by default
+logger.setLevel(logging.CRITICAL)
+
+#------------------------------------------------
+
+
+def fromfile(filename):
+  """ Parse patch file and return Patch() object
+  """
+
+  info("reading patch from file %s" % filename)
+  fp = open(filename, "rb")
+  patch = Patch(fp)
+  fp.close()
+  return patch
+
+
+def fromstring(s):
+  """ Parse text string and return Patch() object
+  """
+
+  return Patch(
+           StringIO.StringIO(s)    
+         )
+
+
+
+class HunkInfo(object):
+  """ Parsed hunk data container (hunk starts with @@ -R +R @@) """
+
+  def __init__(self):
+    self.startsrc=None #: line count starts with 1
+    self.linessrc=None
+    self.starttgt=None
+    self.linestgt=None
+    self.invalid=False
+    self.text=[]
+
+  def copy(self):
+    return copy.copy(self)
+
+#  def apply(self, estream):
+#    """ write hunk data into enumerable stream
+#        return strings one by one until hunk is
+#        over
+#
+#        enumerable stream are tuples (lineno, line)
+#        where lineno starts with 0
+#    """
+#    pass
+
+
+
+class Patch(object):
+
+  def __init__(self, stream=None):
+
+    # define Patch data members
+    # table with a row for every source file
+
+    #: list of source filenames
+    self.source=None
+    self.target=None
+    #: list of lists of hunks
+    self.hunks=None
+    #: file endings statistics for every hunk
+    self.hunkends=None
+
+    if stream:
+      self.parse(stream)
+
+  def copy(self):
+    return copy.copy(self)
+
+  def parse(self, stream):
+    """ parse unified diff """
+    self.source = []
+    self.target = []
+    self.hunks = []
+    self.hunkends = []
+
+    # define possible file regions that will direct the parser flow
+    header = False    # comments before the patch body
+    filenames = False # lines starting with --- and +++
+
+    hunkhead = False  # @@ -R +R @@ sequence
+    hunkbody = False  #
+    hunkskip = False  # skipping invalid hunk mode
+
+    header = True
+    lineends = dict(lf=0, crlf=0, cr=0)
+    nextfileno = 0
+    nexthunkno = 0    #: even if index starts with 0 user messages number hunks from 1
+
+    # hunkinfo holds parsed values, hunkactual - calculated
+    hunkinfo = HunkInfo()
+    hunkactual = dict(linessrc=None, linestgt=None)
+
+    fe = enumerate(stream)
+    for lineno, line in fe:
+
+      # analyze state
+      if header and line.startswith("--- "):
+        header = False
+        # switch to filenames state
+        filenames = True
+      #: skip hunkskip and hunkbody code until you read definition of hunkhead
+      if hunkbody:
+        # process line first
+        if re.match(r"^[- \+\\]", line):
+            # gather stats about line endings
+            if line.endswith("\r\n"):
+              self.hunkends[nextfileno-1]["crlf"] += 1
+            elif line.endswith("\n"):
+              self.hunkends[nextfileno-1]["lf"] += 1
+            elif line.endswith("\r"):
+              self.hunkends[nextfileno-1]["cr"] += 1
+              
+            if line.startswith("-"):
+              hunkactual["linessrc"] += 1
+            elif line.startswith("+"):
+              hunkactual["linestgt"] += 1
+            elif not line.startswith("\\"):
+              hunkactual["linessrc"] += 1
+              hunkactual["linestgt"] += 1
+            hunkinfo.text.append(line)
+            # todo: handle \ No newline cases
+        else:
+            warning("invalid hunk no.%d at %d for target file %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
+            # add hunk status node
+            self.hunks[nextfileno-1].append(hunkinfo.copy())
+            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
+            # switch to hunkskip state
+            hunkbody = False
+            hunkskip = True
+
+        # check exit conditions
+        if hunkactual["linessrc"] > hunkinfo.linessrc or hunkactual["linestgt"] > hunkinfo.linestgt:
+            warning("extra hunk no.%d lines at %d for target %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
+            # add hunk status node
+            self.hunks[nextfileno-1].append(hunkinfo.copy())
+            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
+            # switch to hunkskip state
+            hunkbody = False
+            hunkskip = True
+        elif hunkinfo.linessrc == hunkactual["linessrc"] and hunkinfo.linestgt == hunkactual["linestgt"]:
+            self.hunks[nextfileno-1].append(hunkinfo.copy())
+            # switch to hunkskip state
+            hunkbody = False
+            hunkskip = True
+
+            # detect mixed window/unix line ends
+            ends = self.hunkends[nextfileno-1]
+            if ((ends["cr"]!=0) + (ends["crlf"]!=0) + (ends["lf"]!=0)) > 1:
+              warning("inconsistent line ends in patch hunks for %s" % self.source[nextfileno-1])
+            if debugmode:
+              debuglines = dict(ends)
+              debuglines.update(file=self.target[nextfileno-1], hunk=nexthunkno)
+              debug("crlf: %(crlf)d  lf: %(lf)d  cr: %(cr)d\t - file: %(file)s hunk: %(hunk)d" % debuglines)
+
+      if hunkskip:
+        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
+        if match:
+          # switch to hunkhead state
+          hunkskip = False
+          hunkhead = True
+        elif line.startswith("--- "):
+          # switch to filenames state
+          hunkskip = False
+          filenames = True
+          if debugmode and len(self.source) > 0:
+            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
+
+      if filenames:
+        if line.startswith("--- "):
+          if nextfileno in self.source:
+            warning("skipping invalid patch for %s" % self.source[nextfileno])
+            del self.source[nextfileno]
+            # double source filename line is encountered
+            # attempt to restart from this second line
+          re_filename = "^--- ([^\t]+)"
+          match = re.match(re_filename, line)
+          # todo: support spaces in filenames
+          if match:
+            self.source.append(match.group(1).strip())
+          else:
+            warning("skipping invalid filename at line %d" % lineno)
+            # switch back to header state
+            filenames = False
+            header = True
+        elif not line.startswith("+++ "):
+          if nextfileno in self.source:
+            warning("skipping invalid patch with no target for %s" % self.source[nextfileno])
+            del self.source[nextfileno]
+          else:
+            # this should be unreachable
+            warning("skipping invalid target patch")
+          filenames = False
+          header = True
+        else:
+          if nextfileno in self.target:
+            warning("skipping invalid patch - double target at line %d" % lineno)
+            del self.source[nextfileno]
+            del self.target[nextfileno]
+            nextfileno -= 1
+            # double target filename line is encountered
+            # switch back to header state
+            filenames = False
+            header = True
+          else:
+            re_filename = "^\+\+\+ ([^\t]+)"
+            match = re.match(re_filename, line)
+            if not match:
+              warning("skipping invalid patch - no target filename at line %d" % lineno)
+              # switch back to header state
+              filenames = False
+              header = True
+            else:
+              self.target.append(match.group(1).strip())
+              nextfileno += 1
+              # switch to hunkhead state
+              filenames = False
+              hunkhead = True
+              nexthunkno = 0
+              self.hunks.append([])
+              self.hunkends.append(lineends.copy())
+              continue
+
+      if hunkhead:
+        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
+        if not match:
+          if nextfileno-1 not in self.hunks:
+            warning("skipping invalid patch with no hunks for file %s" % self.target[nextfileno-1])
+            # switch to header state
+            hunkhead = False
+            header = True
+            continue
+          else:
+            # switch to header state
+            hunkhead = False
+            header = True
+        else:
+          hunkinfo.startsrc = int(match.group(1))
+          hunkinfo.linessrc = 1
+          if match.group(3): hunkinfo.linessrc = int(match.group(3))
+          hunkinfo.starttgt = int(match.group(4))
+          hunkinfo.linestgt = 1
+          if match.group(6): hunkinfo.linestgt = int(match.group(6))
+          hunkinfo.invalid = False
+          hunkinfo.text = []
+
+          hunkactual["linessrc"] = hunkactual["linestgt"] = 0
+
+          # switch to hunkbody state
+          hunkhead = False
+          hunkbody = True
+          nexthunkno += 1
+          continue
+    else:
+      if not hunkskip:
+        warning("patch file incomplete - %s" % filename)
+        # sys.exit(?)
+      else:
+        # duplicated message when an eof is reached
+        if debugmode and len(self.source) > 0:
+            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
+
+    info("total files: %d  total hunks: %d" % (len(self.source), sum(len(hset) for hset in self.hunks)))
+
+
+  def apply(self):
+    """ apply parsed patch """
+
+    total = len(self.source)
+    for fileno, filename in enumerate(self.source):
+
+      f2patch = filename
+      if not exists(f2patch):
+        f2patch = self.target[fileno]
+        if not exists(f2patch):
+          warning("source/target file does not exist\n--- %s\n+++ %s" % (filename, f2patch))
+          continue
+      if not isfile(f2patch):
+        warning("not a file - %s" % f2patch)
+        continue
+      filename = f2patch
+
+      info("processing %d/%d:\t %s" % (fileno+1, total, filename))
+
+      # validate before patching
+      f2fp = open(filename)
+      hunkno = 0
+      hunk = self.hunks[fileno][hunkno]
+      hunkfind = []
+      hunkreplace = []
+      validhunks = 0
+      canpatch = False
+      for lineno, line in enumerate(f2fp):
+        if lineno+1 < hunk.startsrc:
+          continue
+        elif lineno+1 == hunk.startsrc:
+          hunkfind = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " -"]
+          hunkreplace = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " +"]
+          #pprint(hunkreplace)
+          hunklineno = 0
+
+          # todo \ No newline at end of file
+
+        # check hunks in source file
+        if lineno+1 < hunk.startsrc+len(hunkfind)-1:
+          if line.rstrip("\r\n") == hunkfind[hunklineno]:
+            hunklineno+=1
+          else:
+            debug("hunk no.%d doesn't match source file %s" % (hunkno+1, filename))
+            # file may be already patched, but we will check other hunks anyway
+            hunkno += 1
+            if hunkno < len(self.hunks[fileno]):
+              hunk = self.hunks[fileno][hunkno]
+              continue
+            else:
+              break
+
+        # check if processed line is the last line
+        if lineno+1 == hunk.startsrc+len(hunkfind)-1:
+          debug("file %s hunk no.%d -- is ready to be patched" % (filename, hunkno+1))
+          hunkno+=1
+          validhunks+=1
+          if hunkno < len(self.hunks[fileno]):
+            hunk = self.hunks[fileno][hunkno]
+          else:
+            if validhunks == len(self.hunks[fileno]):
+              # patch file
+              canpatch = True
+              break
+      else:
+        if hunkno < len(self.hunks[fileno]):
+          warning("premature end of source file %s at hunk %d" % (filename, hunkno+1))
+
+      f2fp.close()
+
+      if validhunks < len(self.hunks[fileno]):
+        if self._match_file_hunks(filename, self.hunks[fileno]):
+          warning("already patched  %s" % filename)
+        else:
+          warning("source file is different - %s" % filename)
+      if canpatch:
+        backupname = filename+".orig"
+        if exists(backupname):
+          warning("can't backup original file to %s - aborting" % backupname)
+        else:
+          import shutil
+          shutil.move(filename, backupname)
+          if self.write_hunks(backupname, filename, self.hunks[fileno]):
+            warning("successfully patched %s" % filename)
+            unlink(backupname)
+          else:
+            warning("error patching file %s" % filename)
+            shutil.copy(filename, filename+".invalid")
+            warning("invalid version is saved to %s" % filename+".invalid")
+            # todo: proper rejects
+            shutil.move(backupname, filename)
+
+    # todo: check for premature eof
+
+
+  def can_patch(self, filename):
+    """ Check if specified filename can be patched. Returns None if file can
+    not be found among source filenames. False if patch can not be applied
+    clearly. True otherwise.
+
+    :returns: True, False or None
+    """
+    idx = self._get_file_idx(filename, source=True)
+    if idx == None:
+      return None
+    return self._match_file_hunks(filename, self.hunks[idx])
+    
+
+  def _match_file_hunks(self, filepath, hunks):
+    matched = True
+    fp = open(abspath(filepath))
+
+    class NoMatch(Exception):
+      pass
+
+    lineno = 1
+    line = fp.readline()
+    hno = None
+    try:
+      for hno, h in enumerate(hunks):
+        # skip to first line of the hunk
+        while lineno < h.starttgt:
+          if not len(line): # eof
+            debug("check failed - premature eof before hunk: %d" % (hno+1))
+            raise NoMatch
+          line = fp.readline()
+          lineno += 1
+        for hline in h.text:
+          if hline.startswith("-"):
+            continue
+          if not len(line):
+            debug("check failed - premature eof on hunk: %d" % (hno+1))
+            # todo: \ No newline at the end of file
+            raise NoMatch
+          if line.rstrip("\r\n") != hline[1:].rstrip("\r\n"):
+            debug("file is not patched - failed hunk: %d" % (hno+1))
+            raise NoMatch
+          line = fp.readline()
+          lineno += 1
+
+    except NoMatch:
+      matched = False
+      # todo: display failed hunk, i.e. expected/found
+
+    fp.close()
+    return matched
+
+
+  def patch_stream(self, instream, hunks):
+    """ Generator that yields stream patched with hunks iterable
+    
+        Converts lineends in hunk lines to the best suitable format
+        autodetected from input
+    """
+
+    # todo: At the moment substituted lineends may not be the same
+    #       at the start and at the end of patching. Also issue a
+    #       warning/throw about mixed lineends (is it really needed?)
+
+    hunks = iter(hunks)
+
+    srclineno = 1
+
+    lineends = {'\n':0, '\r\n':0, '\r':0}
+    def get_line():
+      """
+      local utility function - return line from source stream
+      collecting line end statistics on the way
+      """
+      line = instream.readline()
+        # 'U' mode works only with text files
+      if line.endswith("\r\n"):
+        lineends["\r\n"] += 1
+      elif line.endswith("\n"):
+        lineends["\n"] += 1
+      elif line.endswith("\r"):
+        lineends["\r"] += 1
+      return line
+
+    for hno, h in enumerate(hunks):
+      debug("hunk %d" % (hno+1))
+      # skip to line just before hunk starts
+      while srclineno < h.startsrc:
+        yield get_line()
+        srclineno += 1
+
+      for hline in h.text:
+        # todo: check \ No newline at the end of file
+        if hline.startswith("-") or hline.startswith("\\"):
+          get_line()
+          srclineno += 1
+          continue
+        else:
+          if not hline.startswith("+"):
+            get_line()
+            srclineno += 1
+          line2write = hline[1:]
+          # detect if line ends are consistent in source file
+          if sum([bool(lineends[x]) for x in lineends]) == 1:
+            newline = [x for x in lineends if lineends[x] != 0][0]
+            yield line2write.rstrip("\r\n")+newline
+          else: # newlines are mixed
+            yield line2write
+     
+    for line in instream:
+      yield line
+
+
+  def write_hunks(self, srcname, tgtname, hunks):
+    src = open(srcname, "rb")
+    tgt = open(tgtname, "wb")
+
+    debug("processing target file %s" % tgtname)
+
+    tgt.writelines(self.patch_stream(src, hunks))
+
+    tgt.close()
+    src.close()
+    return True
+  
+
+  def _get_file_idx(self, filename, source=None):
+    """ Detect index of given filename within patch.
+
+        :param filename:
+        :param source: search filename among sources (True),
+                       targets (False), or both (None)
+        :returns: int or None
+    """
+    filename = abspath(filename)
+    if source == True or source == None:
+      for i,fnm in enumerate(self.source):
+        if filename == abspath(fnm):
+          return i  
+    if source == False or source == None:
+      for i,fnm in enumerate(self.target):
+        if filename == abspath(fnm):
+          return i  
+
+
+
+
+from optparse import OptionParser
+from os.path import exists
+import sys
+
+if __name__ == "__main__":
+  opt = OptionParser(usage="%prog [options] unipatch-file", version="python-patch %s" % __version__)
+  opt.add_option("--debug", action="store_true", dest="debugmode", help="debug mode")
+  (options, args) = opt.parse_args()
+
+  if not args:
+    opt.print_version()
+    opt.print_help()
+    sys.exit()
+  debugmode = options.debugmode
+  patchfile = args[0]
+  if not exists(patchfile) or not isfile(patchfile):
+    sys.exit("patch file does not exist - %s" % patchfile)
+
+
+  if debugmode:
+    loglevel = logging.DEBUG
+    logformat = "%(levelname)8s %(message)s"
+  else:
+    loglevel = logging.INFO
+    logformat = "%(message)s"
+  logger.setLevel(loglevel)
+  loghandler.setFormatter(logging.Formatter(logformat))
+
+
+
+  patch = fromfile(patchfile)
+  #pprint(patch)
+  patch.apply()
+
+  # todo: document and test line ends handling logic - patch.py detects proper line-endings
+  #       for inserted hunks and issues a warning if patched file has incosistent line ends
Binary file virtualenv/res/src/Django-1.2.4.tar.gz has changed
Binary file virtualenv/res/src/Imaging-1.1.7.tar.gz has changed
Binary file virtualenv/res/src/JCC-2.6-py2.6-win32.egg has changed
Binary file virtualenv/res/src/MySQL-python-1.2.3.tar.gz has changed
Binary file virtualenv/res/src/PIL-1.1.7.win32-py2.6.exe has changed
Binary file virtualenv/res/src/distribute-0.6.14.tar.gz has changed
Binary file virtualenv/res/src/django-extensions-0.6.tar.gz has changed
Binary file virtualenv/res/src/django-oauth-plus.tar.gz has changed
Binary file virtualenv/res/src/django-piston-0.2.2-modified.tar.gz has changed
Binary file virtualenv/res/src/django-registration.tar.gz has changed
Binary file virtualenv/res/src/django-tagging-0.3.1.tar.gz has changed
Binary file virtualenv/res/src/httplib2-0.6.0.tar.gz has changed
Binary file virtualenv/res/src/jogging-0.2.2.tar.gz has changed
Binary file virtualenv/res/src/lucene-3.0.2-py2.6-win32.egg has changed
Binary file virtualenv/res/src/lxml-2.2.8-py2.6-win32.egg has changed
Binary file virtualenv/res/src/lxml-2.2.8.tar.gz has changed
Binary file virtualenv/res/src/mercurial-1.7.5.tar.gz has changed
Binary file virtualenv/res/src/psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip has changed
Binary file virtualenv/res/src/psycopg2-2.3.2.tar.gz has changed
Binary file virtualenv/res/src/pylucene-3.0.3-1-src.tar.gz has changed
Binary file virtualenv/res/src/python-oauth2-1.2.1-modified.tar.gz has changed
Binary file virtualenv/res/src/setuptools_hg-0.2.tar.gz has changed
--- a/virtualenv/setup/create_python_env.py	Fri Mar 25 21:39:53 2011 +0100
+++ b/virtualenv/setup/create_python_env.py	Tue Mar 29 13:33:43 2011 +0200
@@ -29,15 +29,16 @@
 # - 4Suite-xml - easy_install ftp://ftp.4suite.org/pub/4Suite/4Suite-XML-1.0.2.tar.bz2
 # - pylucene  - script
 
-src_base = os.path.join(here,"res","src").replace("\\","/")
-lib_path = os.path.abspath(os.path.join(here,"res","lib")).replace("\\","/")
+src_base = os.path.abspath(os.path.join(here,"..","res","src")).replace("\\","/")
+lib_path = os.path.abspath(os.path.join(here,"..","res","lib")).replace("\\","/")
+patch_path = os.path.abspath(os.path.join(here,"res","patch")).replace("\\","/")
+
 
 EXTRA_TEXT  = "import sys\n"
 EXTRA_TEXT += "sys.path.append('%s')\n" % (lib_path)
-f = open(os.path.join(os.path. os.path.join(os.path.dirname(os.path.abspath(__file__)),"res"),'res_create_env.py'), 'r')
-EXTRA_TEXT += f.read()
-EXTRA_TEXT += "\n"
-EXTRA_TEXT += "RES_ENV = ResourcesEnv('%s')\n" % (src_base)
+EXTRA_TEXT += "sys.path.append('%s')\n" % (os.path.abspath(os.path.join(here,"res")).replace("\\","/"))
+EXTRA_TEXT += "from res_create_env import generate_install_methods\n"
+EXTRA_TEXT += "adjust_options, extend_parser, after_install = generate_install_methods(path_locations, '%s', Logger, call_subprocess)\n" % (src_base)
 
 
 def main():
--- a/virtualenv/setup/res/res_create_env.py	Fri Mar 25 21:39:53 2011 +0100
+++ b/virtualenv/setup/res/res_create_env.py	Tue Mar 29 13:33:43 2011 +0200
@@ -1,249 +1,15 @@
-import sys
-import os
-import os.path
-import shutil
-import tarfile
-import zipfile
-import urllib
 import platform
-import patch
-import logging
-
-class ResourcesEnv(object):
-
-    def __init__(self, src_base):
-        self.src_base = src_base
-        self.URLS = {}
-        self.__init_url()
-        self.NORMAL_INSTALL = []
-        self.__init_normal_install()
-
-    def get_src_base_path(self, fpath):
-        return os.path.abspath(os.path.join(self.src_base, fpath)).replace("\\","/")
-    
-    def __add_package_def(self, key, setup, url, local):
-        self.URLS[key] = {'setup':setup, 'url':url, 'local':self.get_src_base_path(local)}
-
-    def __init_url(self):
-        self.__add_package_def('DISTRIBUTE', setup='distribute', url='http://pypi.python.org/packages/source/d/distribute/distribute-0.6.14.tar.gz', local="distribute-0.6.14.tar.gz")
-        self.__add_package_def('MERCURIAL', setup='mercurial', url='http://pypi.python.org/packages/source/d/mercurial/mercurial-1.7.5.tar.gz', local="mercurial-1.7.5.tar.gz")
-        self.__add_package_def('SETUPTOOLS-HG', setup='mercurial_hg', url='http://pypi.python.org/packages/source/s/setuptools_hg/setuptools_hg-0.2.tar.gz', local="setuptools_hg-0.2.tar.gz")
-
 
-    def __init_normal_install(self):
-        system_str = platform.system()
-        self.NORMAL_INSTALL = [ #(key,method, option_str, extra_env)
-            ('DISTRIBUTE', 'pip', None, None),
-            ('MERCURIAL', 'pip', None, None),
-            ('SETUPTOOLS-HG', 'pip', None, None), 
-        ]
+from lib_create_env import lib_generate_install_methods, install_pylucene, install_psycopg2
 
-def extend_parser(parser):
-    
-    parser.add_option(
-        '--index-url',
-        metavar='INDEX_URL',
-        dest='index_url',
-        default='http://pypi.python.org/simple/',
-        help='base URL of Python Package Index')
-    parser.add_option(
-        '--type-install',
-        metavar='type_install',
-        dest='type_install',
-        default='local',
-        help='type install : local, url, setup')
-    parser.add_option(
-        '--ignore-packages',
-        metavar='ignore_packages',
-        dest='ignore_packages',
-        default=None,
-        help='list of comma separated keys for package to ignore')
-
-
-def adjust_options(options, args):
-    pass
+system_str = platform.system()
 
 
-def after_install(options, home_dir):
-    global RES_ENV
-    #global path_locations
-    home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
-    base_dir = os.path.dirname(home_dir)
-    src_dir = os.path.join(home_dir, 'src')
-    tmp_dir = os.path.join(home_dir, 'tmp')
-    ensure_dir(src_dir)
-    ensure_dir(tmp_dir)
-    system_str = platform.system()
-    
-    res_source_key = options.type_install
-    
-    ignore_packages = []
-    
-    if options.ignore_packages :
-        ignore_packages = options.ignore_packages.split(",")
-    
-    logger.indent += 2
-    try:
-        
-        if 'PYLUCENE' not in ignore_packages and system_str != "Windows":
-            #get pylucene
-            logger.notify("Get Pylucene from %s " % RES_ENV.URLS['PYLUCENE'][res_source_key])
-            pylucene_src = os.path.join(src_dir,"pylucene.tar.gz")
-            if res_source_key == 'local':
-                shutil.copy(RES_ENV.URLS['PYLUCENE'][res_source_key], pylucene_src)
-            else:
-                urllib.urlretrieve(RES_ENV.URLS['PYLUCENE'][res_source_key], pylucene_src)
-            tf = tarfile.open(pylucene_src,'r:gz')
-            pylucene_base_path = os.path.join(src_dir,"pylucene") 
-            logger.notify("Extract Pylucene to %s " % pylucene_base_path)
-            tf.extractall(pylucene_base_path)
-            tf.close()
-            
-            pylucene_src_path = os.path.join(pylucene_base_path, os.listdir(pylucene_base_path)[0])
-            jcc_src_path = os.path.abspath(os.path.join(pylucene_src_path,"jcc"))
-            
-            #install jcc
-    
-            #patch for linux
-            if system_str == 'Linux' :
-                olddir = os.getcwd()
-                patch_dest_path = os.path.join(lib_dir,'site-packages','setuptools-0.6c11-py'+'%s.%s' % (sys.version_info[0], sys.version_info[1])+'.egg')
-                if os.path.isfile(patch_dest_path):
-                    # must unzip egg
-                    # rename file and etract all
-                    shutil.move(patch_dest_path, patch_dest_path + ".zip")
-                    zf = zipfile.ZipFile(patch_dest_path + ".zip",'r')
-                    zf.extractall(patch_dest_path)
-                    os.remove(patch_dest_path + ".zip")
-                logger.notify("Patch jcc : %s " % (patch_dest_path))
-                os.chdir(patch_dest_path)
-                p = patch.fromfile(os.path.join(jcc_src_path,"jcc","patches","patch.43.0.6c11"))
-                p.apply()
-                os.chdir(olddir)
-    
-            logger.notify("Install jcc")
-            call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'python')), 'setup.py', 'install'],
-                            cwd=jcc_src_path,
-                            filter_stdout=filter_python_develop,
-                            show_stdout=True)
-            #install pylucene
-            
-            logger.notify("Install pylucene")
-            #modify makefile
-            makefile_path = os.path.join(pylucene_src_path,"Makefile")
-            logger.notify("Modify makefile %s " % makefile_path)
-            shutil.move( makefile_path, makefile_path+"~" )
-    
-            destination= open( makefile_path, "w" )
-            source= open( makefile_path+"~", "r" )
-            destination.write("PREFIX_PYTHON="+os.path.abspath(home_dir)+"\\n")
-            destination.write("ANT=ant\\n")
-            destination.write("PYTHON=$(PREFIX_PYTHON)/bin/python\\n")
-            
-            if system_str == "Darwin":
-                if sys.version_info >= (2,6):
-                    destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\\n")
-                else:
-                    destination.write("JCC=$(PYTHON) -m jcc --shared --arch x86_64 --arch i386\\n")
-                destination.write("NUM_FILES=2\\n")
-            elif system_str == "Windows":
-                destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\\n")
-                destination.write("NUM_FILES=2\\n")
-            else:
-                if sys.version_info >= (2,6) and sys.version_info <= (2,7):
-                    destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared\\n")
-                else:
-                    destination.write("JCC=$(PYTHON) -m jcc --shared\\n")
-                destination.write("NUM_FILES=2\\n")
-            for line in source:
-                destination.write( line )
-            source.close()
-            destination.close()
-            os.remove(makefile_path+"~" )
-    
-            logger.notify("pylucene make")
-            call_subprocess(['make'],
-                            cwd=os.path.abspath(pylucene_src_path),
-                            filter_stdout=filter_python_develop,
-                            show_stdout=True)
-    
-            logger.notify("pylucene make install")
-            call_subprocess(['make', 'install'],
-                            cwd=os.path.abspath(pylucene_src_path),
-                            filter_stdout=filter_python_develop,
-                            show_stdout=True)
+INSTALLS = [#(key,method, option_str, dict_extra_env)
+    ('DISTRIBUTE', 'pip', None, None),
+    ('MERCURIAL', 'pip', None, None),
+    ('SETUPTOOLS-HG', 'pip', None, None), 
+]
 
-
-        if 'PSYCOPG2' not in ignore_packages and system_str == "Windows":
-            #get psycopg2
-            psycopg2_src = os.path.join(src_dir,"psycopg2.zip")
-            shutil.copy(RES_ENV.URLS['PSYCOPG2'][res_source_key], psycopg2_src)
-            #extract psycopg2
-            zf = zipfile.ZipFile(psycopg2_src)
-            psycopg2_base_path = os.path.join(src_dir,"psycopg2")
-            zf.extractall(psycopg2_base_path)
-            zf.close()
-            
-            psycopg2_src_path = os.path.join(psycopg2_base_path, os.listdir(psycopg2_base_path)[0])
-            shutil.copytree(os.path.join(psycopg2_src_path, 'psycopg2'), os.path.abspath(os.path.join(home_dir, 'Lib', 'psycopg2')))
-            shutil.copy(os.path.join(psycopg2_src_path, 'psycopg2-2.0.10-py2.6.egg-info'), os.path.abspath(os.path.join(home_dir, 'Lib', 'site-packages')))
-
-        for key, method, option_str, extra_env in RES_ENV.NORMAL_INSTALL:
-            if key not in ignore_packages:
-                normal_install(key, method, option_str, extra_env, res_source_key, home_dir, tmp_dir)
-                        
-        logger.notify("Clear source dir")
-        shutil.rmtree(src_dir)
-
-    finally:
-        logger.indent -= 2
-    script_dir = join(base_dir, bin_dir)
-    logger.notify('Run "%s Package" to install new packages that provide builds'
-                  % join(script_dir, 'easy_install'))
-
-
-def normal_install(key, method, option_str, extra_env, res_source_key, home_dir, tmp_dir):
-    global logger
-    logger.notify("Install %s from %s with %s" % (key,RES_ENV.URLS[key][res_source_key],method))
-    if method == 'pip':
-        if sys.platform == 'win32':
-            args = [os.path.abspath(os.path.join(home_dir, 'Scripts', 'pip')), 'install', '-E', os.path.abspath(home_dir), RES_ENV.URLS[key][res_source_key]]
-        else:
-            args = [os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), RES_ENV.URLS[key][res_source_key]]
-        if option_str :
-            args.insert(4,option_str)
-        call_subprocess(args,
-                cwd=os.path.abspath(tmp_dir),
-                filter_stdout=filter_python_develop,
-                show_stdout=True,
-                extra_env=extra_env)
-    else:
-        if sys.platform == 'win32':
-            args = [os.path.abspath(os.path.join(home_dir, 'Scripts', 'easy_install')), RES_ENV.URLS[key][res_source_key]]
-        else:
-            args = [os.path.abspath(os.path.join(home_dir, 'bin', 'easy_install')), RES_ENV.URLS[key][res_source_key]]
-        if option_str :
-            args.insert(1,option_str)
-        call_subprocess(args,
-                cwd=os.path.abspath(tmp_dir),
-                filter_stdout=filter_python_develop,
-                show_stdout=True,
-                extra_env=extra_env)
-    
-
-def ensure_dir(dir):
-    global logger
-    if not os.path.exists(dir):
-        logger.notify('Creating directory %s' % dir)
-        os.makedirs(dir)
-
-def filter_python_develop(line):
-    global logger
-    if not line.strip():
-        return Logger.DEBUG
-    for prefix in ['Searching for', 'Reading ', 'Best match: ', 'Processing ',
-                   'Moving ', 'Adding ', 'running ', 'writing ', 'Creating ',
-                   'creating ', 'Copying ']:
-        if line.startswith(prefix):
-            return Logger.DEBUG
-    return Logger.NOTIFY
+def generate_install_methods(path_locations, src_base, Logger, call_subprocess):    
+    return lib_generate_install_methods(path_locations, src_base, Logger, call_subprocess, INSTALLS)
Binary file virtualenv/setup/res/src/distribute-0.6.14.tar.gz has changed
Binary file virtualenv/setup/res/src/mercurial-1.7.5.tar.gz has changed
Binary file virtualenv/setup/res/src/setuptools_hg-0.2.tar.gz has changed
--- a/virtualenv/web/create_python_env.py	Fri Mar 25 21:39:53 2011 +0100
+++ b/virtualenv/web/create_python_env.py	Tue Mar 29 13:33:43 2011 +0200
@@ -34,8 +34,8 @@
 # - 4Suite-xml - easy_install ftp://ftp.4suite.org/pub/4Suite/4Suite-XML-1.0.2.tar.bz2
 # - pylucene  - script
 
-src_base = os.path.join(here,"res","src").replace("\\","/")
-lib_path = os.path.abspath(os.path.join(here,"res","lib")).replace("\\","/")
+src_base = os.path.abspath(os.path.join(here,"..","res","src")).replace("\\","/")
+lib_path = os.path.abspath(os.path.join(here,"..","res","lib")).replace("\\","/")
 patch_path = os.path.abspath(os.path.join(here,"res","patch")).replace("\\","/")
 
 
--- a/virtualenv/web/res/lib/patch.py	Fri Mar 25 21:39:53 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,589 +0,0 @@
-""" Patch utility to apply unified diffs
-
-    Brute-force line-by-line non-recursive parsing 
-
-    Copyright (c) 2008-2010 anatoly techtonik
-    Available under the terms of MIT license
-
-    Project home: http://code.google.com/p/python-patch/
-
-
-    $Id: patch.py 76 2010-04-08 19:10:21Z techtonik $
-    $HeadURL: https://python-patch.googlecode.com/svn/trunk/patch.py $
-"""
-
-__author__ = "techtonik.rainforce.org"
-__version__ = "10.04"
-
-import copy
-import logging
-import re
-# cStringIO doesn't support unicode in 2.5
-from StringIO import StringIO
-from logging import debug, info, warning
-
-from os.path import exists, isfile, abspath
-from os import unlink
-
-
-#------------------------------------------------
-# Logging is controlled by "python_patch" logger
-
-debugmode = False
-
-logger = logging.getLogger("python_patch")
-loghandler = logging.StreamHandler()
-logger.addHandler(loghandler)
-
-debug = logger.debug
-info = logger.info
-warning = logger.warning
-
-#: disable library logging by default
-logger.setLevel(logging.CRITICAL)
-
-#------------------------------------------------
-
-
-def fromfile(filename):
-  """ Parse patch file and return Patch() object
-  """
-
-  info("reading patch from file %s" % filename)
-  fp = open(filename, "rb")
-  patch = Patch(fp)
-  fp.close()
-  return patch
-
-
-def fromstring(s):
-  """ Parse text string and return Patch() object
-  """
-
-  return Patch(
-           StringIO.StringIO(s)    
-         )
-
-
-
-class HunkInfo(object):
-  """ Parsed hunk data container (hunk starts with @@ -R +R @@) """
-
-  def __init__(self):
-    self.startsrc=None #: line count starts with 1
-    self.linessrc=None
-    self.starttgt=None
-    self.linestgt=None
-    self.invalid=False
-    self.text=[]
-
-  def copy(self):
-    return copy.copy(self)
-
-#  def apply(self, estream):
-#    """ write hunk data into enumerable stream
-#        return strings one by one until hunk is
-#        over
-#
-#        enumerable stream are tuples (lineno, line)
-#        where lineno starts with 0
-#    """
-#    pass
-
-
-
-class Patch(object):
-
-  def __init__(self, stream=None):
-
-    # define Patch data members
-    # table with a row for every source file
-
-    #: list of source filenames
-    self.source=None
-    self.target=None
-    #: list of lists of hunks
-    self.hunks=None
-    #: file endings statistics for every hunk
-    self.hunkends=None
-
-    if stream:
-      self.parse(stream)
-
-  def copy(self):
-    return copy.copy(self)
-
-  def parse(self, stream):
-    """ parse unified diff """
-    self.source = []
-    self.target = []
-    self.hunks = []
-    self.hunkends = []
-
-    # define possible file regions that will direct the parser flow
-    header = False    # comments before the patch body
-    filenames = False # lines starting with --- and +++
-
-    hunkhead = False  # @@ -R +R @@ sequence
-    hunkbody = False  #
-    hunkskip = False  # skipping invalid hunk mode
-
-    header = True
-    lineends = dict(lf=0, crlf=0, cr=0)
-    nextfileno = 0
-    nexthunkno = 0    #: even if index starts with 0 user messages number hunks from 1
-
-    # hunkinfo holds parsed values, hunkactual - calculated
-    hunkinfo = HunkInfo()
-    hunkactual = dict(linessrc=None, linestgt=None)
-
-    fe = enumerate(stream)
-    for lineno, line in fe:
-
-      # analyze state
-      if header and line.startswith("--- "):
-        header = False
-        # switch to filenames state
-        filenames = True
-      #: skip hunkskip and hunkbody code until you read definition of hunkhead
-      if hunkbody:
-        # process line first
-        if re.match(r"^[- \+\\]", line):
-            # gather stats about line endings
-            if line.endswith("\r\n"):
-              self.hunkends[nextfileno-1]["crlf"] += 1
-            elif line.endswith("\n"):
-              self.hunkends[nextfileno-1]["lf"] += 1
-            elif line.endswith("\r"):
-              self.hunkends[nextfileno-1]["cr"] += 1
-              
-            if line.startswith("-"):
-              hunkactual["linessrc"] += 1
-            elif line.startswith("+"):
-              hunkactual["linestgt"] += 1
-            elif not line.startswith("\\"):
-              hunkactual["linessrc"] += 1
-              hunkactual["linestgt"] += 1
-            hunkinfo.text.append(line)
-            # todo: handle \ No newline cases
-        else:
-            warning("invalid hunk no.%d at %d for target file %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
-            # add hunk status node
-            self.hunks[nextfileno-1].append(hunkinfo.copy())
-            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
-            # switch to hunkskip state
-            hunkbody = False
-            hunkskip = True
-
-        # check exit conditions
-        if hunkactual["linessrc"] > hunkinfo.linessrc or hunkactual["linestgt"] > hunkinfo.linestgt:
-            warning("extra hunk no.%d lines at %d for target %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
-            # add hunk status node
-            self.hunks[nextfileno-1].append(hunkinfo.copy())
-            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
-            # switch to hunkskip state
-            hunkbody = False
-            hunkskip = True
-        elif hunkinfo.linessrc == hunkactual["linessrc"] and hunkinfo.linestgt == hunkactual["linestgt"]:
-            self.hunks[nextfileno-1].append(hunkinfo.copy())
-            # switch to hunkskip state
-            hunkbody = False
-            hunkskip = True
-
-            # detect mixed window/unix line ends
-            ends = self.hunkends[nextfileno-1]
-            if ((ends["cr"]!=0) + (ends["crlf"]!=0) + (ends["lf"]!=0)) > 1:
-              warning("inconsistent line ends in patch hunks for %s" % self.source[nextfileno-1])
-            if debugmode:
-              debuglines = dict(ends)
-              debuglines.update(file=self.target[nextfileno-1], hunk=nexthunkno)
-              debug("crlf: %(crlf)d  lf: %(lf)d  cr: %(cr)d\t - file: %(file)s hunk: %(hunk)d" % debuglines)
-
-      if hunkskip:
-        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
-        if match:
-          # switch to hunkhead state
-          hunkskip = False
-          hunkhead = True
-        elif line.startswith("--- "):
-          # switch to filenames state
-          hunkskip = False
-          filenames = True
-          if debugmode and len(self.source) > 0:
-            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
-
-      if filenames:
-        if line.startswith("--- "):
-          if nextfileno in self.source:
-            warning("skipping invalid patch for %s" % self.source[nextfileno])
-            del self.source[nextfileno]
-            # double source filename line is encountered
-            # attempt to restart from this second line
-          re_filename = "^--- ([^\t]+)"
-          match = re.match(re_filename, line)
-          # todo: support spaces in filenames
-          if match:
-            self.source.append(match.group(1).strip())
-          else:
-            warning("skipping invalid filename at line %d" % lineno)
-            # switch back to header state
-            filenames = False
-            header = True
-        elif not line.startswith("+++ "):
-          if nextfileno in self.source:
-            warning("skipping invalid patch with no target for %s" % self.source[nextfileno])
-            del self.source[nextfileno]
-          else:
-            # this should be unreachable
-            warning("skipping invalid target patch")
-          filenames = False
-          header = True
-        else:
-          if nextfileno in self.target:
-            warning("skipping invalid patch - double target at line %d" % lineno)
-            del self.source[nextfileno]
-            del self.target[nextfileno]
-            nextfileno -= 1
-            # double target filename line is encountered
-            # switch back to header state
-            filenames = False
-            header = True
-          else:
-            re_filename = "^\+\+\+ ([^\t]+)"
-            match = re.match(re_filename, line)
-            if not match:
-              warning("skipping invalid patch - no target filename at line %d" % lineno)
-              # switch back to header state
-              filenames = False
-              header = True
-            else:
-              self.target.append(match.group(1).strip())
-              nextfileno += 1
-              # switch to hunkhead state
-              filenames = False
-              hunkhead = True
-              nexthunkno = 0
-              self.hunks.append([])
-              self.hunkends.append(lineends.copy())
-              continue
-
-      if hunkhead:
-        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
-        if not match:
-          if nextfileno-1 not in self.hunks:
-            warning("skipping invalid patch with no hunks for file %s" % self.target[nextfileno-1])
-            # switch to header state
-            hunkhead = False
-            header = True
-            continue
-          else:
-            # switch to header state
-            hunkhead = False
-            header = True
-        else:
-          hunkinfo.startsrc = int(match.group(1))
-          hunkinfo.linessrc = 1
-          if match.group(3): hunkinfo.linessrc = int(match.group(3))
-          hunkinfo.starttgt = int(match.group(4))
-          hunkinfo.linestgt = 1
-          if match.group(6): hunkinfo.linestgt = int(match.group(6))
-          hunkinfo.invalid = False
-          hunkinfo.text = []
-
-          hunkactual["linessrc"] = hunkactual["linestgt"] = 0
-
-          # switch to hunkbody state
-          hunkhead = False
-          hunkbody = True
-          nexthunkno += 1
-          continue
-    else:
-      if not hunkskip:
-        warning("patch file incomplete - %s" % filename)
-        # sys.exit(?)
-      else:
-        # duplicated message when an eof is reached
-        if debugmode and len(self.source) > 0:
-            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
-
-    info("total files: %d  total hunks: %d" % (len(self.source), sum(len(hset) for hset in self.hunks)))
-
-
-  def apply(self):
-    """ apply parsed patch """
-
-    total = len(self.source)
-    for fileno, filename in enumerate(self.source):
-
-      f2patch = filename
-      if not exists(f2patch):
-        f2patch = self.target[fileno]
-        if not exists(f2patch):
-          warning("source/target file does not exist\n--- %s\n+++ %s" % (filename, f2patch))
-          continue
-      if not isfile(f2patch):
-        warning("not a file - %s" % f2patch)
-        continue
-      filename = f2patch
-
-      info("processing %d/%d:\t %s" % (fileno+1, total, filename))
-
-      # validate before patching
-      f2fp = open(filename)
-      hunkno = 0
-      hunk = self.hunks[fileno][hunkno]
-      hunkfind = []
-      hunkreplace = []
-      validhunks = 0
-      canpatch = False
-      for lineno, line in enumerate(f2fp):
-        if lineno+1 < hunk.startsrc:
-          continue
-        elif lineno+1 == hunk.startsrc:
-          hunkfind = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " -"]
-          hunkreplace = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " +"]
-          #pprint(hunkreplace)
-          hunklineno = 0
-
-          # todo \ No newline at end of file
-
-        # check hunks in source file
-        if lineno+1 < hunk.startsrc+len(hunkfind)-1:
-          if line.rstrip("\r\n") == hunkfind[hunklineno]:
-            hunklineno+=1
-          else:
-            debug("hunk no.%d doesn't match source file %s" % (hunkno+1, filename))
-            # file may be already patched, but we will check other hunks anyway
-            hunkno += 1
-            if hunkno < len(self.hunks[fileno]):
-              hunk = self.hunks[fileno][hunkno]
-              continue
-            else:
-              break
-
-        # check if processed line is the last line
-        if lineno+1 == hunk.startsrc+len(hunkfind)-1:
-          debug("file %s hunk no.%d -- is ready to be patched" % (filename, hunkno+1))
-          hunkno+=1
-          validhunks+=1
-          if hunkno < len(self.hunks[fileno]):
-            hunk = self.hunks[fileno][hunkno]
-          else:
-            if validhunks == len(self.hunks[fileno]):
-              # patch file
-              canpatch = True
-              break
-      else:
-        if hunkno < len(self.hunks[fileno]):
-          warning("premature end of source file %s at hunk %d" % (filename, hunkno+1))
-
-      f2fp.close()
-
-      if validhunks < len(self.hunks[fileno]):
-        if self._match_file_hunks(filename, self.hunks[fileno]):
-          warning("already patched  %s" % filename)
-        else:
-          warning("source file is different - %s" % filename)
-      if canpatch:
-        backupname = filename+".orig"
-        if exists(backupname):
-          warning("can't backup original file to %s - aborting" % backupname)
-        else:
-          import shutil
-          shutil.move(filename, backupname)
-          if self.write_hunks(backupname, filename, self.hunks[fileno]):
-            warning("successfully patched %s" % filename)
-            unlink(backupname)
-          else:
-            warning("error patching file %s" % filename)
-            shutil.copy(filename, filename+".invalid")
-            warning("invalid version is saved to %s" % filename+".invalid")
-            # todo: proper rejects
-            shutil.move(backupname, filename)
-
-    # todo: check for premature eof
-
-
-  def can_patch(self, filename):
-    """ Check if specified filename can be patched. Returns None if file can
-    not be found among source filenames. False if patch can not be applied
-    clearly. True otherwise.
-
-    :returns: True, False or None
-    """
-    idx = self._get_file_idx(filename, source=True)
-    if idx == None:
-      return None
-    return self._match_file_hunks(filename, self.hunks[idx])
-    
-
-  def _match_file_hunks(self, filepath, hunks):
-    matched = True
-    fp = open(abspath(filepath))
-
-    class NoMatch(Exception):
-      pass
-
-    lineno = 1
-    line = fp.readline()
-    hno = None
-    try:
-      for hno, h in enumerate(hunks):
-        # skip to first line of the hunk
-        while lineno < h.starttgt:
-          if not len(line): # eof
-            debug("check failed - premature eof before hunk: %d" % (hno+1))
-            raise NoMatch
-          line = fp.readline()
-          lineno += 1
-        for hline in h.text:
-          if hline.startswith("-"):
-            continue
-          if not len(line):
-            debug("check failed - premature eof on hunk: %d" % (hno+1))
-            # todo: \ No newline at the end of file
-            raise NoMatch
-          if line.rstrip("\r\n") != hline[1:].rstrip("\r\n"):
-            debug("file is not patched - failed hunk: %d" % (hno+1))
-            raise NoMatch
-          line = fp.readline()
-          lineno += 1
-
-    except NoMatch:
-      matched = False
-      # todo: display failed hunk, i.e. expected/found
-
-    fp.close()
-    return matched
-
-
-  def patch_stream(self, instream, hunks):
-    """ Generator that yields stream patched with hunks iterable
-    
-        Converts lineends in hunk lines to the best suitable format
-        autodetected from input
-    """
-
-    # todo: At the moment substituted lineends may not be the same
-    #       at the start and at the end of patching. Also issue a
-    #       warning/throw about mixed lineends (is it really needed?)
-
-    hunks = iter(hunks)
-
-    srclineno = 1
-
-    lineends = {'\n':0, '\r\n':0, '\r':0}
-    def get_line():
-      """
-      local utility function - return line from source stream
-      collecting line end statistics on the way
-      """
-      line = instream.readline()
-        # 'U' mode works only with text files
-      if line.endswith("\r\n"):
-        lineends["\r\n"] += 1
-      elif line.endswith("\n"):
-        lineends["\n"] += 1
-      elif line.endswith("\r"):
-        lineends["\r"] += 1
-      return line
-
-    for hno, h in enumerate(hunks):
-      debug("hunk %d" % (hno+1))
-      # skip to line just before hunk starts
-      while srclineno < h.startsrc:
-        yield get_line()
-        srclineno += 1
-
-      for hline in h.text:
-        # todo: check \ No newline at the end of file
-        if hline.startswith("-") or hline.startswith("\\"):
-          get_line()
-          srclineno += 1
-          continue
-        else:
-          if not hline.startswith("+"):
-            get_line()
-            srclineno += 1
-          line2write = hline[1:]
-          # detect if line ends are consistent in source file
-          if sum([bool(lineends[x]) for x in lineends]) == 1:
-            newline = [x for x in lineends if lineends[x] != 0][0]
-            yield line2write.rstrip("\r\n")+newline
-          else: # newlines are mixed
-            yield line2write
-     
-    for line in instream:
-      yield line
-
-
-  def write_hunks(self, srcname, tgtname, hunks):
-    src = open(srcname, "rb")
-    tgt = open(tgtname, "wb")
-
-    debug("processing target file %s" % tgtname)
-
-    tgt.writelines(self.patch_stream(src, hunks))
-
-    tgt.close()
-    src.close()
-    return True
-  
-
-  def _get_file_idx(self, filename, source=None):
-    """ Detect index of given filename within patch.
-
-        :param filename:
-        :param source: search filename among sources (True),
-                       targets (False), or both (None)
-        :returns: int or None
-    """
-    filename = abspath(filename)
-    if source == True or source == None:
-      for i,fnm in enumerate(self.source):
-        if filename == abspath(fnm):
-          return i  
-    if source == False or source == None:
-      for i,fnm in enumerate(self.target):
-        if filename == abspath(fnm):
-          return i  
-
-
-
-
-from optparse import OptionParser
-from os.path import exists
-import sys
-
-if __name__ == "__main__":
-  opt = OptionParser(usage="%prog [options] unipatch-file", version="python-patch %s" % __version__)
-  opt.add_option("--debug", action="store_true", dest="debugmode", help="debug mode")
-  (options, args) = opt.parse_args()
-
-  if not args:
-    opt.print_version()
-    opt.print_help()
-    sys.exit()
-  debugmode = options.debugmode
-  patchfile = args[0]
-  if not exists(patchfile) or not isfile(patchfile):
-    sys.exit("patch file does not exist - %s" % patchfile)
-
-
-  if debugmode:
-    loglevel = logging.DEBUG
-    logformat = "%(levelname)8s %(message)s"
-  else:
-    loglevel = logging.INFO
-    logformat = "%(message)s"
-  logger.setLevel(loglevel)
-  loghandler.setFormatter(logging.Formatter(logformat))
-
-
-
-  patch = fromfile(patchfile)
-  #pprint(patch)
-  patch.apply()
-
-  # todo: document and test line ends handling logic - patch.py detects proper line-endings
-  #       for inserted hunks and issues a warning if patched file has incosistent line ends
--- a/virtualenv/web/res/res_create_env.py	Fri Mar 25 21:39:53 2011 +0100
+++ b/virtualenv/web/res/res_create_env.py	Tue Mar 29 13:33:43 2011 +0200
@@ -1,303 +1,54 @@
-import sys
-import os
-import os.path
-import shutil
-import tarfile
-import zipfile
-import urllib
 import platform
-import patch
-
-join = os.path.join
-
-class ResourcesEnv(object):
-
-    def __init__(self, src_base):
-        self.src_base = src_base
-        self.URLS = {}
-        self.__init_url()
-        self.NORMAL_INSTALL = []
-        self.__init_normal_install()
-
-    def get_src_base_path(self, fpath):
-        return os.path.abspath(os.path.join(self.src_base, fpath)).replace("\\","/")
-    
-    def __add_package_def(self, key, setup, url, local):
-        self.URLS[key] = {'setup':setup, 'url':url, 'local':self.get_src_base_path(local)}
-
-    def __init_url(self):
-        self.__add_package_def('DISTRIBUTE', setup='distribute', url='http://pypi.python.org/packages/source/d/distribute/distribute-0.6.14.tar.gz', local="distribute-0.6.14.tar.gz")
-        self.__add_package_def('DJANGO', setup= 'django', url= 'http://www.djangoproject.com/download/1.2.4/tarball/', local="Django-1.2.4.tar.gz")
-        self.__add_package_def('JOGGING', setup= 'jogging', url= 'http://github.com/zain/jogging/tarball/v0.2.2', local="jogging-0.2.2.tar.gz")
-        self.__add_package_def('DJANGO-EXTENSIONS', setup= 'django-extensions', url='https://github.com/django-extensions/django-extensions/tarball/0.6', local="django-extensions-0.6.tar.gz")
-        self.__add_package_def('DJANGO-REGISTRATION', setup= 'django-registration', url='http://bitbucket.org/ubernostrum/django-registration/get/tip.tar.gz', local="django-registration.tar.gz")
-        self.__add_package_def('DJANGO-TAGGING', setup= 'django-tagging', url='http://django-tagging.googlecode.com/files/django-tagging-0.3.1.tar.gz', local="django-tagging-0.3.1.tar.gz")
-        self.__add_package_def('DJANGO-PISTON', setup= 'django-piston', url=self.get_src_base_path("django-piston-0.2.2-modified.tar.gz"), local="django-piston-0.2.2-modified.tar.gz")
-        self.__add_package_def('OAUTH2', setup= 'python-oauth2', url=self.get_src_base_path("python-oauth2-1.2.1-modified.tar.gz"), local="python-oauth2-1.2.1-modified.tar.gz")
-        self.__add_package_def('HTTPLIB2', setup= 'python-oauth2', url='http://httplib2.googlecode.com/files/httplib2-0.6.0.tar.gz', local="httplib2-0.6.0.tar.gz")
-        self.__add_package_def('DJANGO-OAUTH-PLUS', setup= 'django-oauth-plus', url='http://bitbucket.org/david/django-oauth-plus/get/f314f018e473.gz', local="django-oauth-plus.tar.gz")
-        self.__add_package_def('MYSQL', setup= 'mysql-python', url= 'http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.3/MySQL-python-1.2.3.tar.gz/download', local="MySQL-python-1.2.3.tar.gz")
-        self.__add_package_def('SETUPTOOLS-HG', setup='mercurial_hg', url='http://pypi.python.org/packages/source/s/setuptools_hg/setuptools_hg-0.2.tar.gz', local="setuptools_hg-0.2.tar.gz")
 
-        if sys.platform == 'win32':
-            self.__add_package_def('PSYCOPG2', setup= 'psycopg2',url= self.get_src_base_path('psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip'), local="psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip")
-            self.__add_package_def('JCC', setup= 'http://pylucene-win32-binary.googlecode.com/files/JCC-2.6-py2.6-win32.egg', local="JCC-2.6-py2.6-win32.egg")
-            self.__add_package_def('PYLUCENE', setup= 'http://pylucene-win32-binary.googlecode.com/files/lucene-3.0.2-py2.6-win32.egg', local="lucene-3.0.2-py2.6-win32.egg")
-            self.__add_package_def('PIL', setup= 'pil', url= 'http://effbot.org/media/downloads/PIL-1.1.7.win32-py2.6.exe', local="PIL-1.1.7.win32-py2.6.exe")
-            self.__add_package_def('LXML', setup= 'lxml', url= 'http://pypi.python.org/packages/2.6/l/lxml/lxml-2.2.8-py2.6-win32.egg', local="lxml-2.2.8-py2.6-win32.egg")        
-        else:
-            self.__add_package_def('PSYCOPG2', setup= 'psycopg2',url= 'http://initd.org/psycopg/tarballs/PSYCOPG-2-3/psycopg2-2.3.2.tar.gz', local="psycopg2-2.3.2.tar.gz")
-            self.__add_package_def('PYLUCENE', setup= 'http://apache.crihan.fr/dist/lucene/pylucene/pylucene-3.0.3-1-src.tar.gz', url= 'http://apache.crihan.fr/dist/lucene/pylucene/pylucene-3.0.3-1-src.tar.gz', local="pylucene-3.0.3-1-src.tar.gz")
-            self.__add_package_def('PIL', setup= 'pil', url= 'http://effbot.org/downloads/Imaging-1.1.7.tar.gz', local="Imaging-1.1.7.tar.gz")
-            self.__add_package_def('LXML', setup= 'lxml', url=self.get_src_base_path("lxml_2.2.8.tar.gz"), local="lxml-2.2.8.tar.gz")
+from lib_create_env import lib_generate_install_methods, install_pylucene, install_psycopg2
 
-    def __init_normal_install(self):
-        system_str = platform.system()
-        self.NORMAL_INSTALL = [ #(key,method, option_str, extra_env)
-            ('SETUPTOOLS-HG', 'pip', None, None), 
-            ('MYSQL', 'pip', None, None),
-            ('PIL', 'easy_install', None, None), 
-            ('DJANGO','pip', None, None),
-            ('JOGGING','pip', None, None),
-            ('DJANGO-EXTENSIONS', 'pip', None, None),
-            ('DJANGO-REGISTRATION', 'easy_install', '-Z', None),
-            ('DJANGO-TAGGING', 'pip', None, None),
-            ('DJANGO-PISTON', 'pip', None, None),
-            ('HTTPLIB2', 'pip', None, None),
-            ('OAUTH2', 'easy_install', None, None),
-            ('DJANGO-OAUTH-PLUS', 'pip', None, None),
-        ]
-        if system_str == "Windows":
-            self.NORMAL_INSTALL.append(('JCC','easy_install',None,None))
-            self.NORMAL_INSTALL.append(('PYLUCENE','easy_install',None,None))
-             
-        if system_str == "Darwin":
-            self.NORMAL_INSTALL.append(('LXML', 'pip', None, {'STATIC_DEPS': 'true', 'LIBXML2_VERSION': '2.7.8', 'LIBXSLT_VERSION': '1.1.26'}))
-        else:
-            self.NORMAL_INSTALL.append(('LXML', 'pip', None, None))
-        
-        if system_str != "Windows":
-            self.NORMAL_INSTALL.append(('PSYCOPG2', 'pip', None, None))
-
-        if system_str == 'Linux':
-            self.NORMAL_INSTALL.insert(0, ('DISTRIBUTE', 'pip', None, None))
+system_str = platform.system()
 
 
-def generate_install_methods(path_locations, src_base, Logger, call_subprocess):
+if system_str == 'Linux':
+    INSTALLS = [
+    ('DISTRIBUTE', 'pip', None, None),
+    ]
+else:
+    INSTALLS = []
     
-    res_env = ResourcesEnv(src_base)
 
-    def extend_parser(parser):    
-        parser.add_option(
-            '--index-url',
-            metavar='INDEX_URL',
-            dest='index_url',
-            default='http://pypi.python.org/simple/',
-            help='base URL of Python Package Index')
-        parser.add_option(
-            '--type-install',
-            metavar='type_install',
-            dest='type_install',
-            default='local',
-            help='type install : local, url, setup')
-        parser.add_option(
-            '--ignore-packages',
-            metavar='ignore_packages',
-            dest='ignore_packages',
-            default=None,
-            help='list of comma separated keys for package to ignore')
-    
-    
-    def adjust_options(options, args):
-        pass
-    
-    
-    def after_install(options, home_dir):
-        
-        global logger
-        
-        verbosity = options.verbose - options.quiet
-        logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)])
+INSTALLS.extend([ #(key,method, option_str, dict_extra_env)
+    ('SETUPTOOLS-HG', 'pip', None, None), 
+    ('MYSQL', 'pip', None, None),
+    ('PIL', 'easy_install', None, None), 
+    ('DJANGO','pip', None, None),
+    ('JOGGING','pip', None, None),
+    ('DJANGO-EXTENSIONS', 'pip', None, None),
+    ('DJANGO-REGISTRATION', 'easy_install', '-Z', None),
+    ('DJANGO-TAGGING', 'pip', None, None),
+    ('DJANGO-PISTON', 'pip', None, None),
+    ('HTTPLIB2', 'pip', None, None),
+    ('OAUTH2', 'easy_install', None, None),
+    ('DJANGO-OAUTH-PLUS', 'pip', None, None),
+])
 
-        
-        home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
-        base_dir = os.path.dirname(home_dir)
-        src_dir = os.path.join(home_dir, 'src')
-        tmp_dir = os.path.join(home_dir, 'tmp')
-        ensure_dir(src_dir)
-        ensure_dir(tmp_dir)
-        system_str = platform.system()
-        
-        res_source_key = options.type_install
-        
-        ignore_packages = []
-        
-        if options.ignore_packages :
-            ignore_packages = options.ignore_packages.split(",")
-        
-        logger.indent += 2
-        try:
-            
-            if 'PYLUCENE' not in ignore_packages and system_str != "Windows":
-                #get pylucene
-                logger.notify("Get Pylucene from %s " % res_env.URLS['PYLUCENE'][res_source_key])
-                pylucene_src = os.path.join(src_dir,"pylucene.tar.gz")
-                if res_source_key == 'local':
-                    shutil.copy(res_env.URLS['PYLUCENE'][res_source_key], pylucene_src)
-                else:
-                    urllib.urlretrieve(res_env.URLS['PYLUCENE'][res_source_key], pylucene_src)
-                tf = tarfile.open(pylucene_src,'r:gz')
-                pylucene_base_path = os.path.join(src_dir,"pylucene") 
-                logger.notify("Extract Pylucene to %s " % pylucene_base_path)
-                tf.extractall(pylucene_base_path)
-                tf.close()
-                
-                pylucene_src_path = os.path.join(pylucene_base_path, os.listdir(pylucene_base_path)[0])
-                jcc_src_path = os.path.abspath(os.path.join(pylucene_src_path,"jcc"))
-                
-                #install jcc
-        
-                #patch for linux
-                if system_str == 'Linux' :
-                    olddir = os.getcwd()
-                    patch_dest_path = os.path.join(lib_dir,'site-packages','setuptools-0.6c11-py'+'%s.%s' % (sys.version_info[0], sys.version_info[1])+'.egg')
-                    if os.path.isfile(patch_dest_path):
-                        # must unzip egg
-                        # rename file and etract all
-                        shutil.move(patch_dest_path, patch_dest_path + ".zip")
-                        zf = zipfile.ZipFile(patch_dest_path + ".zip",'r')
-                        zf.extractall(patch_dest_path)
-                        os.remove(patch_dest_path + ".zip")
-                    logger.notify("Patch jcc : %s " % (patch_dest_path))
-                    os.chdir(patch_dest_path)
-                    p = patch.fromfile(os.path.join(jcc_src_path,"jcc","patches","patch.43.0.6c11"))
-                    p.apply()
-                    os.chdir(olddir)
-        
-                logger.notify("Install jcc")
-                call_subprocess([os.path.abspath(os.path.join(home_dir, 'bin', 'python')), 'setup.py', 'install'],
-                                cwd=jcc_src_path,
-                                filter_stdout=filter_python_develop,
-                                show_stdout=True)
-                #install pylucene
-                
-                logger.notify("Install pylucene")
-                #modify makefile
-                makefile_path = os.path.join(pylucene_src_path,"Makefile")
-                logger.notify("Modify makefile %s " % makefile_path)
-                shutil.move( makefile_path, makefile_path+"~" )
-        
-                destination= open( makefile_path, "w" )
-                source= open( makefile_path+"~", "r" )
-                destination.write("PREFIX_PYTHON="+os.path.abspath(home_dir)+"\n")
-                destination.write("ANT=ant\n")
-                destination.write("PYTHON=$(PREFIX_PYTHON)/bin/python\n")
-                
-                if system_str == "Darwin":
-                    if sys.version_info >= (2,6):
-                        destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\n")
-                    else:
-                        destination.write("JCC=$(PYTHON) -m jcc --shared --arch x86_64 --arch i386\n")
-                    destination.write("NUM_FILES=2\n")
-                elif system_str == "Windows":
-                    destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared --arch x86_64 --arch i386\n")
-                    destination.write("NUM_FILES=2\n")
-                else:
-                    if sys.version_info >= (2,6) and sys.version_info <= (2,7):
-                        destination.write("JCC=$(PYTHON) -m jcc.__main__ --shared\n")
-                    else:
-                        destination.write("JCC=$(PYTHON) -m jcc --shared\n")
-                    destination.write("NUM_FILES=2\n")
-                for line in source:
-                    destination.write( line )
-                source.close()
-                destination.close()
-                os.remove(makefile_path+"~" )
-        
-                logger.notify("pylucene make")
-                call_subprocess(['make'],
-                                cwd=os.path.abspath(pylucene_src_path),
-                                filter_stdout=filter_python_develop,
-                                show_stdout=True)
-        
-                logger.notify("pylucene make install")
-                call_subprocess(['make', 'install'],
-                                cwd=os.path.abspath(pylucene_src_path),
-                                filter_stdout=filter_python_develop,
-                                show_stdout=True)
-    
-    
-            if 'PSYCOPG2' not in ignore_packages and system_str == "Windows":
-                #get psycopg2
-                psycopg2_src = os.path.join(src_dir,"psycopg2.zip")
-                shutil.copy(res_env.URLS['PSYCOPG2'][res_source_key], psycopg2_src)
-                #extract psycopg2
-                zf = zipfile.ZipFile(psycopg2_src)
-                psycopg2_base_path = os.path.join(src_dir,"psycopg2")
-                zf.extractall(psycopg2_base_path)
-                zf.close()
-                
-                psycopg2_src_path = os.path.join(psycopg2_base_path, os.listdir(psycopg2_base_path)[0])
-                shutil.copytree(os.path.join(psycopg2_src_path, 'psycopg2'), os.path.abspath(os.path.join(home_dir, 'Lib', 'psycopg2')))
-                shutil.copy(os.path.join(psycopg2_src_path, 'psycopg2-2.0.10-py2.6.egg-info'), os.path.abspath(os.path.join(home_dir, 'Lib', 'site-packages')))
-    
-            for key, method, option_str, extra_env in res_env.NORMAL_INSTALL:
-                if key not in ignore_packages:
-                    normal_install(key, method, option_str, extra_env, res_source_key, home_dir, tmp_dir)
-                            
-            logger.notify("Clear source dir")
-            shutil.rmtree(src_dir)
-    
-        finally:
-            logger.indent -= 2
-        script_dir = join(base_dir, bin_dir)
-        logger.notify('Run "%s Package" to install new packages that provide builds'
-                      % join(script_dir, 'easy_install'))
-    
-    
-    def normal_install(key, method, option_str, extra_env, res_source_key, home_dir, tmp_dir):
-        global logger
-        logger.notify("Install %s from %s with %s" % (key,res_env.URLS[key][res_source_key],method))
-        if method == 'pip':
-            if sys.platform == 'win32':
-                args = [os.path.abspath(os.path.join(home_dir, 'Scripts', 'pip')), 'install', '-E', os.path.abspath(home_dir), res_env.URLS[key][res_source_key]]
-            else:
-                args = [os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', '-E', os.path.abspath(home_dir), res_env.URLS[key][res_source_key]]
-            if option_str :
-                args.insert(4,option_str)
-            call_subprocess(args,
-                    cwd=os.path.abspath(tmp_dir),
-                    filter_stdout=filter_python_develop,
-                    show_stdout=True,
-                    extra_env=extra_env)
-        else:
-            if sys.platform == 'win32':
-                args = [os.path.abspath(os.path.join(home_dir, 'Scripts', 'easy_install')), res_env.URLS[key][res_source_key]]
-            else:
-                args = [os.path.abspath(os.path.join(home_dir, 'bin', 'easy_install')), res_env.URLS[key][res_source_key]]
-            if option_str :
-                args.insert(1,option_str)
-            call_subprocess(args,
-                    cwd=os.path.abspath(tmp_dir),
-                    filter_stdout=filter_python_develop,
-                    show_stdout=True,
-                    extra_env=extra_env)
-        
-    
-    def ensure_dir(dir):
-        global logger
-        if not os.path.exists(dir):
-            logger.notify('Creating directory %s' % dir)
-            os.makedirs(dir)
-    
-    def filter_python_develop(line):
-        if not line.strip():
-            return Logger.DEBUG
-        for prefix in ['Searching for', 'Reading ', 'Best match: ', 'Processing ',
-                       'Moving ', 'Adding ', 'running ', 'writing ', 'Creating ',
-                       'creating ', 'Copying ']:
-            if line.startswith(prefix):
-                return Logger.DEBUG
-        return Logger.NOTIFY
+if system_str == 'Windows':
+    INSTALLS.extend([
+    ('JCC','easy_install',None,None),
+    ('PYLUCENE','easy_install',None,None),
+    ('PSYCOPG2',install_psycopg2,None,None),
+    ])
+else:
+    INSTALLS.extend([
+    ('PYLUCENE',install_pylucene,None,None),
+    ('PSYCOPG2', 'pip', None, None),
+    ])
 
-    return adjust_options, extend_parser, after_install
+if system_str == "Darwin":
+    INSTALLS.extend([
+    ('LXML', 'pip', None, {'STATIC_DEPS': 'true', 'LIBXML2_VERSION': '2.7.8', 'LIBXSLT_VERSION': '1.1.26'}),
+    ])
+else:
+    INSTALLS.extend([
+    ('LXML', 'pip', None, None),
+    ])
+
+
+def generate_install_methods(path_locations, src_base, Logger, call_subprocess):    
+    return lib_generate_install_methods(path_locations, src_base, Logger, call_subprocess, INSTALLS)
Binary file virtualenv/web/res/src/Django-1.2.4.tar.gz has changed
Binary file virtualenv/web/res/src/Imaging-1.1.7.tar.gz has changed
Binary file virtualenv/web/res/src/JCC-2.6-py2.6-win32.egg has changed
Binary file virtualenv/web/res/src/MySQL-python-1.2.3.tar.gz has changed
Binary file virtualenv/web/res/src/PIL-1.1.7.win32-py2.6.exe has changed
Binary file virtualenv/web/res/src/distribute-0.6.14.tar.gz has changed
Binary file virtualenv/web/res/src/django-extensions-0.6.tar.gz has changed
Binary file virtualenv/web/res/src/django-oauth-plus.tar.gz has changed
Binary file virtualenv/web/res/src/django-piston-0.2.2-modified.tar.gz has changed
Binary file virtualenv/web/res/src/django-registration.tar.gz has changed
Binary file virtualenv/web/res/src/django-tagging-0.3.1.tar.gz has changed
Binary file virtualenv/web/res/src/httplib2-0.6.0.tar.gz has changed
Binary file virtualenv/web/res/src/jogging-0.2.2.tar.gz has changed
Binary file virtualenv/web/res/src/lucene-3.0.2-py2.6-win32.egg has changed
Binary file virtualenv/web/res/src/lxml-2.2.8-py2.6-win32.egg has changed
Binary file virtualenv/web/res/src/lxml-2.2.8.tar.gz has changed
Binary file virtualenv/web/res/src/psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.zip has changed
Binary file virtualenv/web/res/src/psycopg2-2.3.2.tar.gz has changed
Binary file virtualenv/web/res/src/pylucene-3.0.3-1-src.tar.gz has changed
Binary file virtualenv/web/res/src/python-oauth2-1.2.1-modified.tar.gz has changed
Binary file virtualenv/web/res/src/setuptools_hg-0.2.tar.gz has changed