aboutsummaryrefslogtreecommitdiff
blob: 54d3ca0aac62ca6d34b2b6669fa41b9f3c8b6ab2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# vim: set sw=4 sts=4 et :
# Copyright: 2008 Gentoo Foundation
# Author(s): Nirbheek Chauhan <nirbheek.chauhan@gmail.com>
# 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 'Invalid atom %s' % atom
        parts = parts[0]
        if parts[3] and not parts[4]:
            # FIXME: Custom exceptions
            raise '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 '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 '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 '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
        if self.chroot:
            chroot = self.chroot.chrootdir
            workdir = config.CHAUTOTUA_DIR
        proc = daemon.Spawn(chroot, self.jobuild.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 '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)