diff options
Diffstat (limited to 'g-octave')
-rw-r--r-- | g-octave/config.py | 86 | ||||
-rw-r--r-- | g-octave/description.py | 144 | ||||
-rw-r--r-- | g-octave/description_tree.py | 105 | ||||
-rw-r--r-- | g-octave/ebuild.py | 190 | ||||
-rw-r--r-- | g-octave/fetch.py | 134 | ||||
-rw-r--r-- | g-octave/overlay.py | 49 |
6 files changed, 708 insertions, 0 deletions
diff --git a/g-octave/config.py b/g-octave/config.py new file mode 100644 index 0000000..f802918 --- /dev/null +++ b/g-octave/config.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +__all__ = [ + 'Config', + 'ConfigException', +] + +import ConfigParser +import simplejson +import os + +class ConfigException(Exception): + pass + + +class Config(object): + + __defaults = { + 'db': '/var/cache/octave-forge', + 'overlay': '/usr/local/g-octave', + 'categories': 'main,extra,language', + 'db_mirror': 'http://files.rafaelmartins.eng.br/octave-forge', + 'pm': '/usr/bin/emerge', + 'pm_options': '--ask --verbose', + } + + __section_name = 'main' + + + def __init__(self, fetch_phase=False): + + # Config Parser + self.__config = ConfigParser.ConfigParser(self.__defaults) + + if os.path.exists('/etc/g-octave.cfg'): + self.__config_file = '/etc/g-octave.cfg' + else: + self.__config_file = '../etc/g-octave.cfg.devel' + + self.__config.read(self.__config_file) + + if not fetch_phase: + + # Cache (JSON) + cache_file = os.path.join(self.__getattr__('db'), 'cache.json') + fp = open(cache_file) + self.__cache = simplejson.load(fp) + fp.close() + + # JSON + json_file = os.path.join(self.__getattr__('db'), self.__cache['files']['info.json']) + fp = open(json_file) + self.__info = simplejson.load(fp) + fp.close() + + self.__check_dirs() + + + def __getattr__(self, attr): + + if self.__defaults.has_key(attr): + return self.__config.get(self.__section_name, attr) + elif self.__info.has_key(attr): + return self.__info[attr] + elif attr == 'cache': + return self.__cache['files'] + else: + raise ConfigException('Invalid option: %s' % attr) + + + def __check_dirs(self): + + dirs = [ + self.__config.get(self.__section_name, 'db'), + self.__config.get(self.__section_name, 'overlay') + ] + + for dir in dirs: + if not os.path.isdir(dir): + os.makedirs(dir, 0755) + + +if __name__ == '__main__': + conf = Config() + print conf.pm_options diff --git a/g-octave/description.py b/g-octave/description.py new file mode 100644 index 0000000..599dede --- /dev/null +++ b/g-octave/description.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +__all__ = [ + 'Description', + 'DescriptionException', + 're_depends', + 're_atom', + 're_pkg_atom' +] + +from config import Config +conf = Config() + +import re, os + +# octave-forge DESCRIPTION's dependencies atoms +re_depends = re.compile(r'([a-zA-Z]+) *(\( *([><=]?=?) *([0-9.]+) *\))?') + +# gentoo-like atoms, to use with emerge/whatever package manager +re_atom = re.compile(r'^([><]?=?)([a-zA-Z\-/]+)(-(.*))?$') + +# we'll use atoms like 'control-1.0.11' to g-octave packages +re_pkg_atom = re.compile(r'^(.+)-([0-9.]+)$') + + +class DescriptionException(Exception): + pass + + +class Description(object): + + def __init__(self, file): + + if not os.path.exists(file): + raise DescriptionException('File not found: %s' % file) + + fp = open(file) + myfile = fp.readlines() + fp.close() + + kw = '' + + self.__desc = {} + + for i in myfile: + line = i.split(':') + if len(line) < 2: + if i[0].isspace() and kw != '': + self.__desc[kw] += ' ' + i.strip() + else: + kw = line[0].strip().lower() + value = ':'.join(line[1:]).strip() + if self.__desc.has_key(kw): + if kw == 'depends' or kw == 'systemrequirements' or kw == 'buildrequires': + self.__desc[kw] += ', ' + value + else: + self.__desc[kw] += ' ' + value + else: + self.__desc[kw] = value + + self_depends = [] + + for i in self.__desc: + if i == 'depends': + depends = self.__desc[i] + self.__desc[i] = self.__depends(depends) + self_depends = self.__self_depends(depends) + if i == 'systemrequirements' or i == 'buildrequires': + self.__desc[i] = self.__requirements(self.__desc[i]) + + self.__desc['self_depends'] = self_depends + + + def __depends(self, long_atom): + + tmp = [] + + for atom in long_atom.split(','): + + r = re_depends.match(atom.strip()) + + if r != None: + + myatom = '' + + if r.group(3) != None: + myatom += str(r.group(3)) == '==' and '=' or str(r.group(3)) + + if r.group(1).lower() == 'octave': + myatom += 'sci-mathematics/octave' + else: + myatom += 'g-octave/%s' % r.group(1) + + if r.group(4) != None: + myatom += '-%s' % r.group(4) + + tmp.append(myatom) + + return tmp + + + def __self_depends(self, long_atom): + + tmp = [] + + for atom in long_atom.split(','): + + r = re_depends.match(atom.strip()) + + if r != None: + if r.group(1).lower() != 'octave': + tmp.append((r.group(1), r.group(3), r.group(4))) + + return tmp + + + def __requirements(self, long_atom): + + tmp = [] + + for atom in long_atom.split(','): + atom = atom.strip() + + if conf.dependencies.has_key(atom): + dep = conf.dependencies[atom] + + if dep != '': + tmp.append(dep) + + return tmp + + + def __getattr__(self, name): + + if self.__desc.has_key(name): + return self.__desc[name] + + return None + + +if __name__ == '__main__': + a = Description('/development/contrib/octave-forge-20090607/main/zenity-0.5.7/DESCRIPTION') + print a.depends diff --git a/g-octave/description_tree.py b/g-octave/description_tree.py new file mode 100644 index 0000000..7d33fa4 --- /dev/null +++ b/g-octave/description_tree.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +__all__ = [ + 'DescriptionTree', + 'DescriptionTreeException', +] + +from config import Config +conf = Config() + +from description import * + +import os + +class DescriptionTreeException(Exception): + pass + + +class DescriptionTree(object): + + def __init__(self): + + self.pkg_list = {} + + self.__db_path = conf.db+'/octave-forge' + + if not os.path.isdir(self.__db_path): + raise DescriptionTreeException('Invalid db: %s' % self.__db_path) + + for cat in [i.strip() for i in conf.categories.split(',')]: + catdir = os.path.join(self.__db_path, cat) + if os.path.isdir(catdir): + self.pkg_list[cat] = [] + pkgs = os.listdir(catdir) + for pkg in pkgs: + mypkg = re_pkg_atom.match(pkg) + if mypkg == None: + raise DescriptionTreeException('Invalid Atom: %s' % mypkg) + if mypkg.group(1) not in conf.blacklist: + self.pkg_list[cat].append({ + 'name': mypkg.group(1), + 'version': mypkg.group(2), + }) + + + def __getitem__(self, key): + + mykey = re_pkg_atom.match(key) + if mykey == None: + return None + + name = mykey.group(1) + version = mykey.group(2) + + for cat in self.pkg_list: + for pkg in self.pkg_list[cat]: + if pkg['name'] == name and pkg['version'] == version: + pkgfile = os.path.join( + self.__db_path, + cat, + '%s-%s' % (pkg['name'], pkg['version']), + 'DESCRIPTION' + ) + return Description(pkgfile) + + return None + + + def package_versions(self, pkgname): + + tmp = [] + + for cat in self.pkg_list: + for pkg in self.pkg_list[cat]: + if pkg['name'] == pkgname: + tmp.append(pkg['version']) + + return tmp + + + def latest_version(self, pkgname): + + tmp = self.package_versions(pkgname) + return self.version_compare(tmp) + + + def version_compare(self, versions): + + max = ('0',) + maxstr = None + + for version in versions: + tmp = tuple(version.split('.')) + if tmp > max: + max = tmp + maxstr = version + + return maxstr + + + +if __name__ == '__main__': + a = DescriptionTree() + print a.latest_version('parallel') diff --git a/g-octave/ebuild.py b/g-octave/ebuild.py new file mode 100644 index 0000000..d504cdc --- /dev/null +++ b/g-octave/ebuild.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +__all__ = [ + 'Ebuild', + 'EbuildException', +] + +from config import Config +conf = Config() + +from description import * +from description_tree import * + +import os +import portage +import re +import shutil + +class EbuildException(Exception): + pass + + +class Ebuild: + + def __init__(self, pkg_atom, force=False): + + self.__force = force + self.__dbtree = DescriptionTree() + + atom = re_pkg_atom.match(pkg_atom) + if atom == None: + self.pkgname = pkg_atom + self.version = self.__dbtree.latest_version(self.pkgname) + else: + self.pkgname = atom.group(1) + self.version = atom.group(2) + + self.__desc = self.__dbtree['%s-%s' % (self.pkgname, self.version)] + + if self.__desc == None: + raise EbuildException('Package not found: %s' % pkg_atom) + + + def create(self): + + ebuild_path = os.path.join(conf.overlay, 'g-octave', self.pkgname) + ebuild_file = os.path.join(ebuild_path, '%s-%s.ebuild' % (self.pkgname, self.version)) + + if os.path.exists(ebuild_file) and not self.__force: + return + + if not os.path.exists(ebuild_path): + os.makedirs(ebuild_path, 0755) + + ebuild = """\ +# Copyright 1999-2009 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# This ebuild was generated by g-octave + +EAPI="2" + +inherit octave-forge%(eutils)s + +DESCRIPTION="%(description)s" +HOMEPAGE="%(url)s" +SRC_URI="mirror://sourceforge/octave/${OCT_P}.tar.gz" + +LICENSE="|| ( GPL-2 GPL-3 LGPL BSD GFDL )" +SLOT="0" +KEYWORDS="%(keywords)s" +IUSE="" + +DEPEND="%(depend)s" +RDEPEND="${DEPEND} +\t%(rdepend)s" +""" + + description = self.__desc.description > 70 and \ + self.__desc.description[:70]+'...' or self.__desc.description + + vars = { + 'eutils': '', + 'description': description, + 'url': self.__desc.url, + 'keywords': portage.settings['ACCEPT_KEYWORDS'], + 'depend': '', + 'rdepend': '', + } + + vars['depend'] = self.depends(self.__desc.buildrequires) + + systemrequirements = self.depends(self.__desc.systemrequirements) + if systemrequirements != '': + vars['depend'] += "\n\t"+systemrequirements + + vars['rdepend'] = self.depends(self.__desc.depends) + + patches = self.__search_patches() + + if len(patches) > 0: + + # WOW, we have patches :( + + patchesdir = os.path.join(conf.db, 'patches') + filesdir = os.path.join(conf.overlay, 'g-octave', self.pkgname, 'files') + if not os.path.exists(filesdir): + os.makedirs(filesdir, 0755) + + patch_string = '' + for patch in patches: + patch_string += "\n\tepatch \"${FILESDIR}/%s\"" % patch + shutil.copy2(os.path.join(patchesdir, patch), filesdir) + + ebuild += "\nsrc_prepare() {%s\n}\n" % patch_string + vars['eutils'] = ' eutils' + + fp = open(ebuild_file, 'w', 0644) + fp.write(ebuild % vars) + fp.close() + + portage.doebuild( + ebuild_file, + "manifest", + portage.root, + portage.config(clone=portage.settings), + tree="porttree" + ) + + self.__resolve_dependencies() + + + def depends(self, mylist): + + if mylist != None: + return "\n\t".join(mylist) + + return '' + + + def __search_patches(self): + + tmp = [] + + for patch in os.listdir(conf.db+'/patches'): + if re.match(r'^([0-9]{3})_%s-%s' % (self.pkgname, self.version), patch): + tmp.append(patch) + + tmp.sort() + + return tmp + + + + def __resolve_dependencies(self): + + to_install = [] + + for pkg, comp, version in self.__desc.self_depends: + + # no version required, get the latest available + if version == None: + to_install.append('%s-%s' % (pkg, self.__dbtree.latest_version(pkg))) + continue + + # here we need to calculate the better version to install + versions = self.__dbtree.package_versions(pkg) + + allowed_versions = [] + + for _version in versions: + _tp_version = tuple(_version.split('.')) + tp_version = tuple(version.split('.')) + + if eval('%s %s %s' % (_tp_version, comp, tp_version)): + allowed_versions.append(_version) + + to_install.append('%s-%s' % (pkg, self.__dbtree.version_compare(allowed_versions))) + + if len(to_install) == 0: + raise EbuildException('Can\'t resolve a dependency: %s' % pkg) + + # creating the ebuilds for the dependencies, recursivelly + for ebuild in to_install: + Ebuild(ebuild).create() + + +if __name__ == '__main__': + a = Ebuild('vrml', True) + a.create() diff --git a/g-octave/fetch.py b/g-octave/fetch.py new file mode 100644 index 0000000..668b136 --- /dev/null +++ b/g-octave/fetch.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +__all__ = [ + 'FetchException', + 'check_updates', + 'download_files', + 'check_db_cache', +] + +from config import Config +conf = Config(True) # fetch phase + +import urllib2 +import os +import simplejson +import subprocess +import re +import shutil +import tarfile + +re_files = { + 'info.json': re.compile(r'info-([0-9]{8})-([0-9]+)\.json'), + 'octave-forge.eclass': re.compile(r'octave-forge-([0-9]+)\.eclass'), + 'octave-forge.db.tar.gz': re.compile(r'octave-forge-([0-9]{8})\.db\.tar\.gz'), + 'patches.tar.gz': re.compile(r'patches-([0-9]{8})-([0-9]+)\.tar\.gz'), +} + +class FetchException(Exception): + pass + + +def check_updates(): + + try: + # we'll use urlopen to do all silensiously and save a subprocess + fp = urllib2.urlopen(conf.db_mirror+'/update.json') + my_json = fp.read() + fp.close() + + except: + # if we already have a file, that's ok + if not os.path.exists(conf.db+'/update.json'): + raise FetchException('Unable to get file list from the mirror: %s' % conf.db_mirror) + + else: + fp = open(conf.db+'/update.json', 'w', 0664) + fp.write(my_json) + fp.close() + + +def download_files(): + + fp = open(conf.db+'/update.json') + files = simplejson.load(fp) + fp.close() + + for _file in files['files']: + if not os.path.exists(conf.db+'/'+_file): + download_with_wget(conf.db_mirror+'/'+_file, conf.db+'/'+_file) + add_file_to_db_cache(_file) + + +def download_with_wget(url, dest): + + # TODO: let the user chooses how to fetch the files + + ret = subprocess.call([ + '/usr/bin/wget', + '-t', '5', + '-T', '60', + '--passive-ftp', + '-O', dest+'.part', + url, + ]) + + if ret != 0: + raise FetchException('Failed to fetch the file: %s' % url) + + shutil.move(dest+'.part', dest) + + +def add_file_to_db_cache(_file): + + try: + fp = open(conf.db+'/cache.json') + files = simplejson.load(fp) + fp.close() + except: + files = {'files': {}} + + for f in re_files: + if re_files[f].match(_file) != None: + files['files'][f] = _file + + fp = open(conf.db+'/cache.json', 'w', 0644) + simplejson.dump(files, fp) + fp.close() + + +def check_db_cache(): + + try: + fp = open(conf.db+'/cache.json') + cache = simplejson.load(fp) + fp.close() + except: + cache = {'files': []} + + fp = open(conf.db+'/update.json') + update = simplejson.load(fp) + fp.close() + + for _file in update['files']: + if _file not in cache['files']: + if not os.path.exists(conf.db+'/'+_file): + download_with_wget(conf.db_mirror+'/'+_file, conf.db+'/'+_file) + add_file_to_db_cache(_file) + extract(_file) + + +def extract(_file): + + my_file = conf.db+'/'+_file + + if tarfile.is_tarfile(my_file): + fp = tarfile.open(my_file, 'r:gz') + fp.extractall(conf.db) + + +if __name__ == '__main__': + check_updates() + download_files() + check_db_cache() diff --git a/g-octave/overlay.py b/g-octave/overlay.py new file mode 100644 index 0000000..f4f133f --- /dev/null +++ b/g-octave/overlay.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +__all__ = ['create_overlay'] + +from config import Config +conf = Config() + +import os +import shutil + +def create_overlay(force=False): + + if force: + if os.path.exists(conf.overlay): + shutil.rmtree(conf.overlay) + + # creating dirs + for _dir in ['profiles', 'eclass']: + dir = os.path.join(conf.overlay, _dir) + if not os.path.exists(dir) or force: + os.makedirs(dir, 0755) + + # creating files + files = { + os.path.join(conf.overlay, 'profiles', 'repo_name'): 'g-octave', + os.path.join(conf.overlay, 'profiles', 'categories'): 'g-octave', + os.path.join(conf.overlay, 'eclass', 'octave-forge.eclass'): + open(os.path.join(conf.db, conf.cache['octave-forge.eclass'])), + } + for _file in files: + if not os.path.exists(_file) or force: + __create_file(_file, files[_file]) + + +def __create_file(_file, content): + + if type(content) == file: + content = content.read() + + fp = open(_file, 'w', 0644) + fp.write(content) + fp.close() + + type(content) == file and content.close() + + +if __name__ == '__main__': + create_overlay(True) |