aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'g-octave')
-rw-r--r--g-octave/config.py86
-rw-r--r--g-octave/description.py144
-rw-r--r--g-octave/description_tree.py105
-rw-r--r--g-octave/ebuild.py190
-rw-r--r--g-octave/fetch.py134
-rw-r--r--g-octave/overlay.py49
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)