linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Luis R. Rodriguez" <mcgrof@kernel.org>
To: Julia.Lawall@lip6.fr, Gilles.Muller@lip6.fr,
	nicolas.palix@imag.fr, mmarek@suse.com
Cc: linux-kernel@vger.kernel.org, cocci@systeme.lip6.fr,
	"Luis R. Rodriguez" <mcgrof@kernel.org>
Subject: [PATCH 2/4] scripts: add reqs python library
Date: Tue, 14 Jun 2016 15:10:15 -0700	[thread overview]
Message-ID: <1465942217-14452-3-git-send-email-mcgrof@kernel.org> (raw)
In-Reply-To: <1465942217-14452-1-git-send-email-mcgrof@kernel.org>

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

  parent reply	other threads:[~2016-06-14 22:10 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-14 22:10 [PATCH 0/4] scripts: add basic python version library and use it Luis R. Rodriguez
2016-06-14 22:10 ` [PATCH 1/4] coccicheck: propagate error and stop processing after first error Luis R. Rodriguez
2016-06-14 22:10 ` Luis R. Rodriguez [this message]
2016-06-15  6:06   ` [PATCH 2/4] scripts: add reqs python library Julia Lawall
2016-06-15 16:04     ` Luis R. Rodriguez
2016-06-15  7:50   ` Michal Marek
2016-06-15 16:02     ` Luis R. Rodriguez
2016-06-15 19:11       ` Michal Marek
2016-06-15 20:26         ` Luis R. Rodriguez
2016-06-15 20:31           ` Julia Lawall
2016-06-15 12:01   ` Aw: [Cocci] " SF Markus Elfring
2016-06-15 15:51     ` Luis R. Rodriguez
2016-06-14 22:10 ` [PATCH 3/4] coccicheck: enable use of the kernel's " Luis R. Rodriguez
2016-06-15  7:51   ` Michal Marek
2016-06-15 15:43     ` Luis R. Rodriguez
2016-06-14 22:10 ` [PATCH 4/4] scripts/coccinelle: require coccinelle >= 1.0.4 on device_node_continue.cocci Luis R. Rodriguez
2016-06-15  6:08   ` Julia Lawall
2016-06-15 15:45     ` Luis R. Rodriguez
2016-06-15  8:43   ` Julia Lawall
2016-06-15 15:49     ` Luis R. Rodriguez
2016-06-15 15:55       ` Julia Lawall
2016-06-15 16:06         ` SF Markus Elfring
2016-06-15 16:08         ` [PATCH 4/4] " Luis R. Rodriguez
2016-06-15 16:11           ` Julia Lawall
2016-06-15 16:46             ` Luis R. Rodriguez
2016-06-15 16:52               ` Julia Lawall
2016-06-15 19:08                 ` Luis R. Rodriguez

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1465942217-14452-3-git-send-email-mcgrof@kernel.org \
    --to=mcgrof@kernel.org \
    --cc=Gilles.Muller@lip6.fr \
    --cc=Julia.Lawall@lip6.fr \
    --cc=cocci@systeme.lip6.fr \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mmarek@suse.com \
    --cc=nicolas.palix@imag.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).