[PATCH 2/4] scripts: add reqs python library

Luis R. Rodriguez
2016-06-14T22:10:30Z

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 <mcgrof@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-janitors@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"
+ 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