On Tue, 14 Jun 2016, Luis R. Rodriguez wrote:
> This library can be used in other python scripts to require > specific binary version requirements. It will be used first > with coccinelle's python bindings to enable coccinelle SmPL > files to specify version requirements per cocci file if it > has any. > > Signed-off-by: Luis R. Rodriguez <mcg...@kernel.org> > --- > MAINTAINERS | 1 + > scripts/lib/__init__.py | 1 + > scripts/lib/reqs.py | 211 > ++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 213 insertions(+) > create mode 100644 scripts/lib/__init__.py > create mode 100644 scripts/lib/reqs.py > > diff --git a/MAINTAINERS b/MAINTAINERS > index f83e19a2dd97..fdebbb513c1b 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -6521,6 +6521,7 @@ F: scripts/Makefile.* > F: scripts/basic/ > F: scripts/mk* > F: scripts/package/ > +F: scripts/lib/ > > KERNEL JANITORS > L: kernel-janit...@vger.kernel.org > diff --git a/scripts/lib/__init__.py b/scripts/lib/__init__.py > new file mode 100644 > index 000000000000..1bb8bf6d7fd4 > --- /dev/null > +++ b/scripts/lib/__init__.py > @@ -0,0 +1 @@ > +# empty > diff --git a/scripts/lib/reqs.py b/scripts/lib/reqs.py > new file mode 100644 > index 000000000000..1325fd21a87a > --- /dev/null > +++ b/scripts/lib/reqs.py > @@ -0,0 +1,211 @@ > +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" binay -> binary > + 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] > + > + MAX_RC = 25 > + 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) - (Req.MAX_RC + 1) > + 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) > -- > 2.8.2 > >