from os import listdir from os.path import abspath, isfile, dirname from portage.versions import cpv_sort_key from configparser import ConfigParser import xml.etree.ElementTree as ET class ebuild: def __init__(self, package, full_name): self.package = package self.path = package.path+'/'+full_name+'.ebuild' self.name = '-'.join(full_name.split('-')[0:-1]) self.version = full_name.split('-')[-1] self.live = self.version[:4] == '9999' self.inherits = [] self.vars = {} self.loaded = False def load(self): # TODO: Read ebuilds properly with open(self.path, 'r') as fp: for line in fp.readlines(): line = line[:-1].split('#', maxsplit=1)[0] if len(line) > 0 and '=' in line: s = [p.strip() for p in line.split('=', maxsplit=1)] if len(s[1]) > 1 and s[1][0] == s[1][-1] == '"': self.vars[s[0]] = s[1][1:-1] if line.startswith('inherit'): self.inherits.extend(line.split(' ')[1:]) if len(line) > 0 and line[-1] == '{': break self.loaded = True class manifest: def __init__(self, path): self.path = path self.loaded = False def load(self): with open(self.path, 'r') as fp: self.entries = [n[:-1].split(' ') for n in fp.readlines()] self.loaded = True class metadata: def __init__(self, path): self.path = path self.maintainers = [] self.remotes = [] self.loaded = False def load(self): self.tree = ET.parse(self.path) self.root = self.tree.getroot() for maintainer in self.root.findall('./maintainer'): self.maintainers.append(maintainer.find('email').text) for upstream in self.root.findall('./upstream'): remote = upstream.find('remote-id') if remote is not None: self.remotes.append([remote.get('type'), remote.text]) self.loaded = True class package: def __init__(self, category, name): self.category = category self.name = name self.path = category.path+'/'+name self.ebuilds = [] self.loaded = False @classmethod def from_path(self, path): path = abspath(path) if path[-1] == '/': path = path[:-1] cat = category.from_path(dirname(path)) return package(cat, path.split('/')[-1]) def load(self): self.full_name = self.category.name+'/'+self.name+'::'+self.category.repo.name ls = listdir(self.path) if 'Manifest' in ls: mf = manifest(self.path+'/Manifest') mf.load() self.manifest = mf if 'metadata.xml' in ls: md = metadata(self.path+'/metadata.xml') md.load() self.metadata = md ebuild_names = [n[:-7] for n in filter(lambda name: name[-7:] == '.ebuild', ls)] ebuild_names = sorted(ebuild_names, key=cpv_sort_key())[::-1] for name in ebuild_names: eb = ebuild(self, name) eb.load() self.ebuilds.append(eb) self.loaded = True def version_latest(self): for eb in self.ebuilds: if not eb.live: return eb.version return None class category: def __init__(self, repo, name): self.repo = repo self.name = name self.path = repo.path+'/'+name self.packages = {} self.loaded = False @classmethod def from_path(self, path): path = abspath(path) if path[-1] == '/': path = path[:-1] repo = repository(dirname(path)) return category(repo, path.split('/')[-1]) def load(self): for name in listdir(self.path): if isfile(self.path+'/'+name): continue self.packages[name] = package(self, name) self.loaded = True class repository: def __init__(self, path): self.path = abspath(path) self.categories = {} with open(self.path+'/profiles/repo_name', 'r') as fp: self.name = fp.read().replace("\n", '') self.loaded = False def load(self): for name in listdir(self.path): if '-' not in name or isfile(self.path+'/'+name): continue self.categories[name] = category(self, name) self.loaded = True class repos: def __init__(self): self.from_path('/etc/portage/repos.conf') self.loaded = False @classmethod def from_path(self, path): self.path = abspath(path) self.repos = {} def load(self): conf_paths = [] if isfile(self.path): conf_paths.append(self.path) else: for name in listdir(self.path): if name[-5:] == '.conf': conf_paths.append(self.path+'/'+name) conf = ConfigParser() conf.read(conf_paths) for section in conf.sections(): self.repos[section] = repository(conf[section]['location']) self.loaded = True