virtualenv/res/lib/lib_create_env.py
author ymh <ymh.work@gmail.com>
Fri, 22 Feb 2013 17:48:46 +0100
changeset 6 db873f49afcb
parent 5 8fb31c5f27d5
child 11 bc93fd587618
permissions -rw-r--r--
Added tag V00.01 for changeset 8fb31c5f27d5

import sys
import os
import os.path
import shutil
import tarfile
import zipfile
import urllib
import platform
import patch
import struct
import glob
import re

join = os.path.join
system_str = platform.system()

URLS = {
    #'': {'setup': '', 'url':'', 'local':''},
    'DISTRIBUTE': {'setup': 'distribute', 'url':'http://pypi.python.org/packages/source/d/distribute/distribute-0.6.28.tar.gz', 'local':"distribute-0.6.28.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
    'PYCRYPTO': {'setup': 'pycrypto', 'url':'https://ftp.dlitz.net/pub/dlitz/crypto/pycrypto/pycrypto-2.6.tar.gz', 'local':'pycrypto-2.6.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
    'PARAMIKO' : {'setup': 'paramiko', 'url':'https://github.com/paramiko/paramiko/archive/v1.9.0.tar.gz', 'local':'paramiko-1.9.0.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
    'FABRIC': {'setup': 'fabric', 'url':'https://github.com/fabric/fabric/tarball/1.5.3', 'local':'fabric-1.5.3.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
    'MERCURIAL': {'setup': 'mercurial', 'url':'http://mercurial.selenic.com/release/mercurial-2.5.1.tar.gz', 'local':'mercurial-2.5.1.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
}

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, dict):
        self.URLS[key] = dict
        
    def __init_url(self, urls):
        for key, url_dict in urls.items():
            url_dict_copy = url_dict.copy()
            if not url_dict['url'].startswith("http://"):
                url_dict_copy['url'] = self.get_src_base_path(url_dict['url'])
            url_dict_copy['local'] = self.get_src_base_path(url_dict['local'])            
            
            self.__add_package_def(key, url_dict_copy )

def ensure_dir(dir, logger):
    logger.notify('Check directory %s' % dir)
    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',
        help='type install : local, url, setup - default : local')
    parser.add_option(
        '--ignore-packages',
        metavar='ignore_packages',
        dest='ignore_packages',
        default=None,
        help='list of comma separated keys for package to ignore')    

def install_psycopg2(option_str, extra_env, res_source_key, home_dir, lib_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/site-packages', 'psycopg2')))
    shutil.copy(os.path.join(psycopg2_src_path, 'psycopg2-2.4.5-py2.7.egg-info'), os.path.abspath(os.path.join(home_dir, 'Lib/site-packages', 'site-packages')))
    

def install_mysql(option_str, extra_env, res_source_key, home_dir, lib_dir, tmp_dir, src_dir, res_env, logger, call_subprocess, filter_python_develop):
    
    args = [os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', res_env.URLS['MYSQL'][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)

    mysqlconfig_output = []
    
    call_subprocess(['mysql_config', '--libmysqld-libs'],
        cwd=os.path.abspath(tmp_dir),
        filter_stdout=lambda line: mysqlconfig_output.append(line),
        show_stdout=True)
        
    mysqlconfig_output = "".join(mysqlconfig_output)
    m = re.search("\-L[\'\"]?([\w\/]+)[\'\"]?", mysqlconfig_output)
    if m:
        repdylibpath = m.group(1)
    else:
        repdylibpath = '/usr/local/mysql/lib'
        
    dyliblist = glob.glob(repdylibpath+"/libmysqlclient.*.dylib")
    def key_func(s):
        m = re.match(repdylibpath+"/libmysqlclient\.([\d]+)\.dylib", s)
        if m:
            return int(m.group(1))
        else:
            return sys.maxint
    dyliblist.sort(key=key_func)
    
    if dyliblist:
        dylibpath = dyliblist[0]
    else:
        dylibpath = '/usr/local/mysql/lib/libmysqlclient.18.dylib'
        
    dylibname = os.path.basename(dylibpath)    
    sopath = os.path.join(os.path.abspath(lib_dir), 'site-packages', '_mysql.so')
    
    call_subprocess(['install_name_tool', '-change', dylibname, dylibpath, sopath],
        cwd=os.path.abspath(tmp_dir),
        filter_stdout=filter_python_develop,
        show_stdout=True)


def gen_install_comp_lib(lib_name, lib_key, configure_options=[]):
    
    def install_lib(option_str, extra_env, res_source_key, home_dir, lib_dir, tmp_dir, src_dir, res_env, logger, call_subprocess, filter_python_develop):
        lib_src = os.path.join(src_dir,lib_name+".tar.gz")
        logger.notify("Copy %s to %s " % (res_env.URLS[lib_key][res_source_key],lib_src))
        shutil.copy(res_env.URLS[lib_key][res_source_key], lib_src)
        tf = tarfile.open(lib_src,'r:gz')
        lib_base_path = os.path.join(src_dir, lib_name) 
        logger.notify("Extract %s to %s " % (lib_name,lib_base_path))
        tf.extractall(lib_base_path)
        tf.close()
        
        lib_src_path = os.path.join(lib_base_path, os.listdir(lib_base_path)[0])
    
        logger.notify(lib_name + " configure in " + lib_src_path)
        call_subprocess(['./configure', '--prefix='+os.path.abspath(home_dir)] + configure_options,
                        cwd=os.path.abspath(lib_src_path),
                        filter_stdout=filter_python_develop,
                        show_stdout=True)
        
        logger.notify(lib_name + " make in " + lib_src_path)
        call_subprocess(['make'],
                        cwd=os.path.abspath(lib_src_path),
                        filter_stdout=filter_python_develop,
                        show_stdout=True)
    
        logger.notify(lib_name + "make install in " + lib_src_path)
        call_subprocess(['make', 'install'],
                        cwd=os.path.abspath(lib_src_path),
                        filter_stdout=filter_python_develop,
                        show_stdout=True)
    return install_lib

install_libjpeg = gen_install_comp_lib("libjpeg", "LIBJPEG", ['--enable-shared'])
install_zlib = gen_install_comp_lib("zlib", "ZLIB", [])
    

def lib_generate_install_methods(path_locations, src_base, Logger, call_subprocess, normal_installs, options_to_add=None, 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', res_env.URLS[key][res_source_key]]
            else:
                args = [os.path.abspath(os.path.join(home_dir, 'bin', 'pip')), 'install', 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 = getattr(options, 'type_install') if hasattr(options, 'type_install') else 'local' #.get('type_install', 'local')
        if res_source_key is None:
            res_source_key = local
        
        ignore_packages = []
        
        if system_str == 'Windows':
            default_install_options = {'method': 'easy_install', 'option_str': None, 'dict_extra_env': {}}
        else:
            default_install_options = {'method': 'pip', 'option_str': None, 'dict_extra_env': {}}
            
        if options.ignore_packages :
            ignore_packages = options.ignore_packages.split(",")
        
        logger.indent += 2
        try:    
            for key in res_env.NORMAL_INSTALL:
                if key not in res_env.URLS:
                    logger.notify("%s not found in def : passing" % (key,))
                install_options = res_env.URLS[key].get('install', None)
                if install_options is None:
                    install_options = default_install_options
                method = install_options.get('method', default_install_options['method'])
                option_str = install_options.get('option_str', default_install_options['option_str'])
                extra_env = install_options.get('dict_extra_env', default_install_options['dict_extra_env'])
                if not extra_env:
                    extra_env = {}
                    
                if 'TMPDIR' not in extra_env:
                    extra_env['TMPDIR'] = os.path.abspath(tmp_dir)          
                #isinstance(lst, (list, tuple))
                if key not in ignore_packages:
                    logger.notify("install %s with method %s" % (key, repr(method)))
                    if callable(method):
                        method(option_str, extra_env, res_source_key, home_dir, lib_dir, tmp_dir, src_dir, res_env, logger, call_subprocess, filter_python_develop)
                    elif method in globals() and callable(globals()[method]) and method not in ['pip', 'easy_install']:  
                        globals()[method](option_str, extra_env, res_source_key, home_dir, lib_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'))
    
    def adjust_options(options, args):
        if not options_to_add:
            pass
        for opt in options_to_add:
            test_opt = opt.split('=',1)[0]
            if not hasattr(options,test_opt) or getattr(options, test_opt) is None:                
                setattr(options, test_opt,opt.split('=',1)[1] if "=" in opt else True)

    return adjust_options, extend_parser, after_install