Personal tools
     DOCUMENTATION

Conary:cvc make

From rPath Wiki

Jump to: navigation, search
  Conary Developer Community Go to the community main page  
 
#!/usr/bin/python
#
# Copyright 2005 Tim Gerla
# Copyright 2006 Stu Gott
#
# This program is distributed under the terms of the Common Public License,
# version 1.0. A copy of this license should have been distributed with this
# source file in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/cpl.php.
#
# This program is distributed in the hope that it will be useful, but
# without any waranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the Common Public License for
# full details.
#
 
#
# Original code by Tim Gerla
# Extensively modifed by Stu Gott
#
 
import os
import tempfile
import re
import subprocess
import sys
import urllib2
from urlparse import urlparse
 
from conary.lib import util
sys.excepthook = util.genExcepthook()
 
unpackerMap = {
    '.tar.gz' : 'gunzip',
    '.tgz'    : 'gunzip',
    '.tar.bz2': 'bunzip2',
    '.zip'    : 'unzip'
    }
 
recipeTemplates = {
    'auto': """class %(capname)s(AutoPackageRecipe):
    name = '%(name)s'
    version = '%(version)s'
 
    def unpack(r):
        r.addArchive('%(url)s')""",
 
    'distutils': """class %(capname)s(PackageRecipe):
    name = '%(name)s'
    version = '%(version)s'
 
    def setup(r):
        r.addArchive('%(url)s')
 
        r.Run('python setup.py build')
        r.Run('python setup.py install --root=%%(destdir)s')""",
 
    'Makefile' : """class %(capname)s(PackageRecipe):
    name = '%(name)s'
    version = '%(version)s'
 
    def setup(r):
        r.addArchive('%(url)s')
 
        r.Make()
        r.MakeInstall()"""
}
 
configMap = {
    'configure'  : ('autoconf configure script', 'auto'),
    'setup.py'   : ('Python disutils script', 'distutils'),
    'install'    : ('custom install script', 'auto'),
    'install.sh' : ('custom install script', 'auto'),
    'Makefile'   : ('Makefile', 'Makefile')
}
 
# number of args past command line switch to be assigned to given local var.
cmdlnArgs = {'context' : 1, 'archive' : 1, 'help' : 0, 'error': 0}
cmdlnAlias = {'c' : 'context', 'a' : 'archive', 'h' : 'help'}
 
def parseCmdLine():
    options = dict([(x, None) for x in cmdlnArgs.keys()])
    index = 1
    while index < len(sys.argv):
        token = sys.argv[index]
        if token.startswith('-') and (len(token) > 1) and token[1] != '-':
            if token[1:] in cmdlnAlias:
                token = '--' + cmdlnAlias[token[1:]]
            else:
                options['error'] = True
                index += 1
                continue
        if token.startswith('--'):
            stripTok = token[2:]
            if stripTok not in cmdlnArgs:
                options['error'] = True
                index += 1
            else:
                if not cmdlnArgs[stripTok]:
                    options[stripTok] = True
                elif cmdlnArgs[stripTok] == 1:
                    if index + 1 == len(sys.argv) or \
                           sys.argv[index + 1].startswith('-'):
                        options['error'] = True
                    else:
                        options[stripTok] = sys.argv[index + 1]
                else:
                    if index + 1 + cmdlnArgs[stripTok] >= len(sys.argv) or \
                           [x for x in sys.argv[index + 1: index + 1 + \
                                                cmdlnArgs[stripTok]] if \
                            x.startswith('-')]:
                        options['error'] = True
                    else:
                        options[stripTok] = sys.argv[index + 1: index + 1 + \
                                                     cmdlnArgs[stripTok]]
                index += cmdlnArgs[stripTok] + 1
        else:
            options['archive'] = token
            index += 1
    options['error'] = options['error'] or not options['archive']
    # replace any hyphenated arg with studly-caps form
    for key in options:
        if '-' in key:
            newKey = key[0] + \
                     ''.join([x.capitalize() for x in key.split('-')])[1:]
            options[newKey] = options[key]
            del options[key]
    return options
 
locals().update(parseCmdLine())
 
if help or error:
    if help:
        out = sys.stdout
        retCode = 0
    else:
        out = sys.stderr
        retCode = 1
    print >> out, "Usage:", sys.argv[0], "[--context <context>] <archive>"
    print >> out, "  Make a recipe or conary package in current directory.\n"
    print >> out, "  * archive is a full URL to package.\n"
    print >> out, "  * context is optional."
    print >> out, "    if not defined: a recipe will be created."
    print >> out, "    if defined: context will be used to make a package."
    sys.exit(retCode)
 
ext = None
for suffix in unpackerMap.keys():
    if archive.upper().endswith(suffix.upper()):
        # use case insensitive extension matching
        ext = archive[-1 * len(suffix):]
        unpack = unpackerMap[suffix]
        break
 
if not ext:
    print >> sys.stderr, "Cannot parse archive. Unknown file extension."
    print >> sys.stderr, sys.argv[0], "only knows about", \
          ', '.join(unpackerMap.keys())
    sys.exit(1)
 
try:
    fd, name = tempfile.mkstemp(suffix = ext, prefix = 'cvc_make')
    tmpFn = name
    os.close(fd)
    f = file(name, "w")
 
    print "Fetching %s as %s" % (archive, name)
    url = urllib2.urlopen(sys.argv[1])
 
    f.write(url.read())
    f.close()
 
    outputDir = tempfile.mkdtemp(prefix = 'cvc_make')
    if unpack == 'unzip':
        cmd = "%s %s -d %s" % (unpack, name, outputDir)
    else:
        cmd = "%s -d -c < %s | tar -C %s -xSf -" % (unpack, name, outputDir)
    print "+", cmd
    os.system(cmd)
 
    configureScript = None
    for path in os.listdir(outputDir):
        if os.path.isdir(os.path.join(outputDir, path)):
            for configure in configMap.keys():
                if os.path.exists(os.path.join(outputDir, path, configure)):
                    configureScript = configure
                    break
 
    found = False
    for config in configMap.keys():
        if configureScript == config:
            print "Detected %s." % configMap[config][0]
            recipeTemplate = recipeTemplates[configMap[config][1]]
            found = True
            break
    if not found:
        print "Failed to find a reasonable configure script!"
        sys.exit(1)
 
    pkgname = os.path.basename(urlparse(archive.split(ext)[0])[2])
    m = re.match("(.+)-(\d[^-]*)", pkgname)
    assert m, "Can't parse package name and version."
    name, version = m.groups()
 
    print "Package name: %s, version %s" % (name, version)
    archive = archive.replace(name, "%(name)s")
    archive = archive.replace(version, "%(version)s")
    capname = ''.join([x for x in name.title() if x.isalnum() or x == '_'])
    if capname[0].isdigit():
        capname = '_' + capname
    macros = {'name':       name,
              'version':    version,
              'url':        archive,
              'capname':    capname}
 
    recipeFN = '%s.recipe' % name
    if recipeFN in os.listdir('.'):
        print "removing stale recipe"
        os.unlink(recipeFN)
 
    print "Creating %s.recipe" % name
    recipeFile = file(recipeFN, 'w')
    print >> recipeFile, recipeTemplate % macros
    recipeFile.close()
 
    if context:
        print "Creating %s package" % name
        if name in os.listdir('.'):
            util.rmtree(name)
        os.system('cvc newpkg %s --context %s' % (name, context))
        os.system('mv %s %s' % (recipeFN, name))
        os.chdir(name)
        os.system('cvc context %s' % context)
        os.system('cvc add %s' % recipeFN)
        os.chdir('..')
finally:
    try:
        os.unlink(tmpFn)
    except:
        pass
    try:
        util.rmtree(outputDir)
    except:
        pass