summaryrefslogtreecommitdiff
path: root/lib/bpreqs.py
blob: 6c4667019cf284d8524057c99d796e403b6dcbc1 (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
201
202
203
204
205
206
207
208
209
import subprocess, os, sys, re
"""
Often enough Python code can grow to depend on binaries
on a system, you may also require only specific versions
of these. This small library helps with this. It also has
helpers for packages which we know to handle already.
"""

class ReqError(Exception):
    pass
class ExecutionError(ReqError):
    def __init__(self, errcode):
        self.error_code = errcode

class Req:
    "To be used for verifying binay package dependencies on Python code"
    def __init__(self):
        self.all_reqs_ok = True
        self.debug = False
    def enable_debug(self):
        self.debug = True
    def reqs_match(self):
        if self.all_reqs_ok:
            return True
        sys.stdout.write("You have unfulfilled binary requirements\n")
        return False
    def req_missing(self, program):
        self.all_reqs_ok = False
        sys.stdout.write("You need to have installed: %s\n" % program)
    def req_old_program(self, program, version_req):
        self.all_reqs_ok = False
        sys.stdout.write("You need to have installed: %s >= %s\n" % (program, version_req))
    def which(self, program):
        cmd = ['which', program]
        process = subprocess.Popen(cmd,
                                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                                   close_fds=True, universal_newlines=True)
        stdout = process.communicate()[0]
        process.wait()
        if process.returncode != 0:
            raise ExecutionError(process.returncode)
        return stdout
    def req_exists(self, program):
        cmd = ['which', program]
        process = subprocess.Popen(cmd,
                                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                                   close_fds=True, universal_newlines=True)
        stdout = process.communicate()[0]
        process.wait()
        if process.returncode == 0:
            return True
        return False
    def req_get_prog_version(self, program, version_query, version_pos):
        '''
        Suppose you have a binary that outputs:
        $ spatch --version
        spatch version 1.0.0-rc21 with Python support and with PCRE support

        Every program veries what it wants you to query it for a version string,
        prog_version() is designed so that you pass what the program expects for
        its version query, and the position you expect the version string to be
        on using python list.
        '''
        cmd = [program, version_query]
        process = subprocess.Popen(cmd,
                                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                                   close_fds=True, universal_newlines=True)
        stdout = process.communicate()[0]
        process.wait()
        if process.returncode != 0:
            raise ExecutionError(process.returncode)
        if self.debug:
            sys.stdout.write("Running '%s' got us this break down:\n%s\n" %
                             (
                             ' '.join(cmd),
                             "\n".join(map(str, [[i, x] for i, x in enumerate(stdout.split())])),
                             ))
            sys.stdout.write("You are using for version: %s\n" % stdout.split()[version_pos])
            sys.stdout.write("Specifically your idx, element: %s\n" % ([[i, x] for i, x in enumerate(stdout.split())][version_pos]))
        return stdout.split()[version_pos]
    def __compute_rel_weight(self, rel_specs):
        weight = 0
        extra = 0
        sublevel = 0
        relmod = 0

        if self.debug:
            sys.stdout.write("VERSION       = %s\n" % rel_specs['VERSION'])
            sys.stdout.write("PATCHLEVEL    = %s\n" % rel_specs['PATCHLEVEL'])
            sys.stdout.write("SUBLEVEL      = %s\n" % rel_specs['SUBLEVEL'])
            sys.stdout.write("EXTRAVERSION  = %s\n" % rel_specs['EXTRAVERSION'])
            sys.stdout.write("RELMOD_UPDATE = %s\n" % rel_specs['RELMOD_UPDATE'])

        if rel_specs['EXTRAVERSION'] != '':
            if ("." in rel_specs['EXTRAVERSION'] or
                    "rc" in rel_specs['EXTRAVERSION']):
                rc = rel_specs['EXTRAVERSION'].lstrip("-rc")
                if (rc == ""):
                    rc = 0
                else:
                    rc = int(rc) - 20
                extra = int(rc)
            else:
                extra = int(rel_specs['EXTRAVERSION']) + 10

        if rel_specs['SUBLEVEL'] != '':
            sublevel = int(rel_specs['SUBLEVEL'].lstrip(".")) * 20
        else:
            sublevel = 5

        if rel_specs['RELMOD_UPDATE'] != '':
            mod = rel_specs['RELMOD_UPDATE']
            if (mod == ""):
                mod = 0
            else:
                mod = int(mod)
            relmod = int(mod)

        weight = (int(rel_specs['VERSION'])    << 32) + \
                 (int(rel_specs['PATCHLEVEL']) << 16) + \
                 (sublevel   		       << 8 ) + \
                 (extra * 60) + (relmod * 2)

        return weight
    def req_get_rel_spec(self, rel):
        if "rc" in rel:
            m = re.match(r"v*(?P<VERSION>\d+)\.+"
                         "(?P<PATCHLEVEL>\d+)[.]*"
                         "(?P<SUBLEVEL>\d*)"
                         "(?P<EXTRAVERSION>[-rc]+\w*)\-*"
                         "(?P<RELMOD_UPDATE>\d*)[-]*",
                         rel)
        else:
            m = re.match(r"v*(?P<VERSION>\d+)\.+"
                         "(?P<PATCHLEVEL>\d+)[.]*"
                         "(?P<SUBLEVEL>\d*)[.]*"
                         "(?P<EXTRAVERSION>\w*)\-*"
                         "(?P<RELMOD_UPDATE>\d*)[-]*",
                         rel)
        if not m:
            return m
        rel_specs = m.groupdict()
        return rel_specs
    def compute_rel_weight(self, rel):
        rel_specs = self.req_get_rel_spec(rel)
        if not rel_specs:
            return 0
        return self.__compute_rel_weight(rel_specs)
    def linux_version_cmp(self, version_req, version):
        '''
        If the program follows the linux version style scheme you can
        use this to compare versions.
        '''
        weight_has = self.compute_rel_weight(version)
        weight_req = self.compute_rel_weight(version_req)

        if self.debug:
            sys.stdout.write("You have program weight: %s\n" % weight_has)
            sys.stdout.write("Required program weight: %s\n" % weight_req)

        if weight_has < weight_req:
            return -1
        return 0
    def require_version(self, program, version_query, version_req, version_pos, version_cmp):
        '''
        If you have a program version requirement you can specify it here,
        as for the other flags refer to prog_version.
        '''
        if not self.require(program):
            return False
        version = self.req_get_prog_version(program, version_query, version_pos)
        if self.debug:
            sys.stdout.write("Checking release specs and weight: for: %s\n" % program)
            sys.stdout.write("You have version: %s\n" % version)
            sys.stdout.write("Required version: %s\n" % version_req)
        if version_cmp(version_req, version) != 0:
            self.req_old_program(program, version_req)
            return False
        return True
    def require(self, program):
        if self.req_exists(program):
            return True
        self.req_missing(program)
        return False
    def require_hint(self, program, package_hint):
        if self.require(program):
            return True
        sys.stdout.write("Try installing the package: %s\n" % package_hint)
        return False
    def coccinelle(self, version):
        if self.require_version('spatch', '--version', version, 2, self.linux_version_cmp):
            return True
        sys.stdout.write("Try installing the package: coccinelle\n")
        sys.stdout.write("If that is too old go grab the code from source:\n\n")
        sys.stdout.write("git clone https://github.com/coccinelle/coccinelle.git\n\n")
        sys.stdout.write("To build you will need: ocaml ncurses-devel\n\n")
        sys.stdout.write("If on SUSE / OpenSUSE you will also need: ocaml-ocamldoc\n\n")
        return False
    def kup(self):
        if self.require('kup'):
            return True
        sys.stdout.write("Try installing the package: kup\n")
        sys.stdout.write("If your distribution lacks that go get from source:\n\n")
        sys.stdout.write("git clone git://git.kernel.org/pub/scm/utils/kup/kup.git\n\n")
        return False
    def make(self, version):
        return self.require_version('make', '--version', version, 2, self.linux_version_cmp)
    def gcc(self, version):
        return self.require_version('gcc', '--version', version, 3, self.linux_version_cmp)