# vim: set sw=4 sts=4 et : # Copyright: 2008 Gentoo Foundation # Author(s): Nirbheek Chauhan # License: GPL-2 # # Immortal lh! # # XXX: This is purely yay-something-works code # This will change radically with time import re, subprocess, sys, os import os.path as osp from .. import config, daemon class Jobuild(object): """A jobuild""" def __init__(self, jobtagedir, atom): """ @param jobtagedir: Jobtage directory @type jobtagdir: string @param atom: Atom for finding the corresponding jobuild @type atom: string """ self.atom = atom self.jobtagedir = jobtagedir self.which = self._best_jobuild(atom) def __str__(self): return '%s jobuild object' % self.atom def _split_atom(self, atom): regex = re.compile(r'^([<>]?=)?([a-zA-Z0-9_+-]+)/([a-zA-Z0-9_+-]+)(-|$)([0-9]+\.[0-9]+)?') # Fancy regex aye? # >= bheekling / test-beagle - 1.0 # bheekling / build-brasero parts = regex.findall(atom) if not parts: # FIXME: Custom exceptions raise Exception('Invalid atom %s' % atom) parts = parts[0] if parts[3] and not parts[4]: # FIXME: Custom exceptions raise Exception('Invalid atom %s' % atom) parts = (parts[0], parts[1], parts[2], parts[4],) if (parts[0] and not parts[3]) or (parts[3] and not parts[0]): # FIXME: Custom exceptions raise Exception('Invalid atom %s' % atom) return parts def _get_all_pv_sorted(self, data): files = os.listdir('%(jobtage)s/%(maint)s/%(pn)s' % data) for file in files[:]: if not file.endswith('.jobuild'): files.remove(file) else: files[files.index(file)] = '=%s/%s' % (data['maint'], osp.basename(file)[:-8],) # .jobuild is 8 characters # =maint/pn-pv pv = [] for atom in files: pv.append(self._split_atom(atom)[3]) pv.sort() return pv def _best_jobuild(self, atom): parts = self._split_atom(atom) data = {'op': parts[0], 'maint': parts[1], 'pn': parts[2], 'pv': parts[3], 'jobtage': self.jobtagedir} if data['op'] == '=': pass # Nothing to be done elif not data['op']: pv = self._get_all_pv_sorted(data) data['pv'] = pv[-1] elif data['op'] == '>=': pv = self._get_all_pv_sorted(data) if pv[-1] >= data['pv']: data['pv'] = pv[-1] else: raise Exception('No matching jobuild found for atom \"%s\"' % atom) elif data['op'] == '<=': pv = self._get_all_pv_sorted(data) pv.reverse() for i in pv: if i <= data['pv']: data['pv'] = i break if i == pv[-1]: # If it's the last pv.. raise Exception('No matching jobuild found for atom \"%s\"' % atom) return '%(maint)s/%(pn)s/%(pn)s-%(pv)s.jobuild' % data class Processor(object): """Generic Jobuild processor""" def __init__(self, jobuild, chroot): """ @param jobuild: Jobuild to process. Can be changed anytime @type jobuild: L{autotua.jobuild.Jobuild} @param chroot: Chroot to use for processing the jobuild @type chroot: L{autotua.chroot.WorkChroot} """ self.jobuild = jobuild self.chroot = chroot def run_phase(self, phase): """ Run the specified phase of the jobuild all: Run all the phases unpack: src_unpack() work: do_work() cleanup: cleanup() """ args = {'phase': phase, 'jobuild': self.jobuild.which,} self._msg('RUN_PHASE "%(phase)s" "%(jobuild)s"' % args) def get_var(self, var): """ Parse jobuild and get a variable (yay-something-works function) """ args = {'var': var, 'jobuild': self.jobuild.which,} return self._msg('GET_VAR "%(var)s" "%(jobuild)s"' % args) def _msg(self, msg): chroot = None workdir = config.AUTOTUA_DIR jobtagedir = self.jobuild.jobtagedir if self.chroot: chroot = self.chroot.chrootdir workdir = config.CHAUTOTUA_DIR jobtagedir = config.CHAUTOTUA_DIR+'/jobtage' proc = daemon.Spawn(chroot, jobtagedir, workdir, '\"%s\"/bin/jobuild.sh %s' % (workdir, msg)) # XXX: This waits for process to close stderr. # Replace with a loop during daemonisation response = proc.process.stderr.read()[:-1] # Remove trailing newline returncode = proc.process.returncode if returncode and returncode < 0: # FIXME: Custom exceptions raise Exception('Daemon died with exit code \'%s\'' % -returncode) return response class Resolver(object): """Jobuild dependency resolver""" def __init__(self, jobuild): """ @param jobuild: Jobuild to resolve dependencies of @type jobuild: L{autotua.jobuild.Jobuild} """ self.jobuild = jobuild self.processor = Processor(self.jobuild, None) def _get_deplist(self, deplist): retlist = deplist index = 0 while index < len(deplist): if not isinstance(deplist[index], list): jobj = Jobuild(self.jobuild.jobtagedir, deplist[index]) self.processor.jobuild = jobj deps = self.processor.get_var('DEPEND').split() if deps: retlist.append(self._get_deplist(deps)) index += 1 return retlist def _flatten(self, deplist): newlist = [] for item in deplist: if isinstance(item, list): newlist.extend(self._flatten(item)) else: newlist.append(item) return newlist def _unique(self, deplist): """ Makes deplist unique and reverses order This gives us the required merge order """ newlist = [] index = len(deplist) while index >= 0: index -= 1 if deplist[index] not in newlist: newlist.append(deplist[index]) return newlist def resolve(self): """ Resolve all the dependencies of the Jobuild and return a list of jobs in running order """ deps = [self.jobuild.atom] deps = self._get_deplist(deps) deps = self._flatten(deps) return self._unique(deps)