From 03288d32635b4400c3981024e669b602a580ba8d Mon Sep 17 00:00:00 2001 From: Matthew Thode Date: Mon, 18 Apr 2016 16:16:00 -0500 Subject: [PATCH] Add Gentoo support to bindep Equery is used to query Gentoo's package database. An exit code of 3 means the package is not installed or it is an invalid package atom (doesn't exist for example). We are able to rely on the exit code for package install status and just output the package version, if it's installed. We do not output revision information as it is not needed here. We also add support for gentoo categories in the package name by allowing a '/' in the package name. This is needed for package names that exist in multiple categories. For instance, curl could map to 'dev-haskell/curl' or 'net-misc/curl'. Without this change we would not be able to specify which to install. I've also included testing for '/' in the package name. Change-Id: Id87038a5599824befae627d67a03f59b70e19257 --- bindep/depends.py | 29 ++++++++++++++++++++++- bindep/tests/test_depends.py | 46 +++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/bindep/depends.py b/bindep/depends.py index 4b22531..067408c 100644 --- a/bindep/depends.py +++ b/bindep/depends.py @@ -45,7 +45,7 @@ rule = :name selector?:selector version?:version ('\n'|comment) -> ( name, selector or [], version or []) lowercase = ('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p' |'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z') -name = letterOrDigit:start (letterOrDigit|'.'|'+'|'-')+:rest +name = letterOrDigit:start (letterOrDigit|'.'|'+'|'-'|'/')+:rest ws = ' '+ profile = ('!'?:neg <(lowercase|digit|':'|'-')+>:name) -> (neg!='!', name) selector = ws '[' profile:p1 (ws profile)*:p2 ']' -> [p1] + p2 @@ -143,6 +143,9 @@ class Depends(object): elif distro in ["centos", "fedora"]: atoms.add("rpm") self.platform = Rpm() + elif distro in ["gentoo"]: + atoms.add("emerge") + self.platform = Emerge() return ["platform:%s" % (atom,) for atom in sorted(atoms)] @@ -208,6 +211,30 @@ class Rpm(Platform): return elements[1] +class Emerge(Platform): + """emerge specific implementation. + + This currently shells out to equery, it could be changed to eix to be + faster but that would add another dependency and eix's cache would need to + be updated before this is run. + """ + + def get_pkg_version(self, pkg_name): + try: + output = subprocess.check_output( + ['equery', 'l', '--format=\'$version\'', pkg_name], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + if e.returncode == 3: + return None + raise + # output looks like + # version + output = output.strip() + elements = output.split(' ') + return elements[0] + + def _eval_diff(operator, diff): """Return the boolean result for operator given diff. diff --git a/bindep/tests/test_depends.py b/bindep/tests/test_depends.py index d5f6a93..3e9f5f8 100644 --- a/bindep/tests/test_depends.py +++ b/bindep/tests/test_depends.py @@ -28,6 +28,7 @@ from testtools import TestCase from bindep.depends import _eval from bindep.depends import Depends from bindep.depends import Dpkg +from bindep.depends import Emerge from bindep.depends import Platform from bindep.depends import Rpm @@ -189,6 +190,7 @@ class TestDepends(TestCase): depends = Depends(dedent("""\ foo bar [something] + category/packagename # for gentoo baz [platform:this platform:that-those] quux [anotherthing !nothing] <=12 womp # and a comment @@ -196,7 +198,7 @@ class TestDepends(TestCase): # all's ok? good then """)) - self.assertEqual(len(depends.active_rules(['default'])), 2) + self.assertEqual(len(depends.active_rules(['default'])), 3) def test_parser_invalid(self): self.assertRaises(ometa.runtime.ParseError, @@ -249,6 +251,48 @@ class TestDpkg(TestCase): self.assertEqual("4.0.0-0ubuntu1", platform.get_pkg_version("foo")) +class TestEmerge(TestCase): + + def test_not_installed(self): + platform = Emerge() + mocker = mox.Mox() + mocker.StubOutWithMock(subprocess, "check_output") + subprocess.check_output( + ['equery', 'l', '--format=\'$version\'', 'foo'], + stderr=subprocess.STDOUT).AndRaise( + subprocess.CalledProcessError(3, [], '')) + mocker.ReplayAll() + self.addCleanup(mocker.VerifyAll) + self.addCleanup(mocker.UnsetStubs) + self.assertEqual(None, platform.get_pkg_version("foo")) + + def test_unknown_package(self): + platform = Emerge() + mocker = mox.Mox() + mocker.StubOutWithMock(subprocess, "check_output") + subprocess.check_output( + ['equery', 'l', '--format=\'$version\'', 'foo'], + stderr=subprocess.STDOUT).AndRaise( + subprocess.CalledProcessError(3, [], '')) + mocker.ReplayAll() + self.addCleanup(mocker.VerifyAll) + self.addCleanup(mocker.UnsetStubs) + self.assertEqual(None, platform.get_pkg_version("foo")) + + def test_installed_version(self): + platform = Emerge() + mocker = mox.Mox() + mocker.StubOutWithMock(subprocess, "check_output") + subprocess.check_output( + ['equery', 'l', '--format=\'$version\'', 'foo'], + stderr=subprocess.STDOUT).AndReturn( + "4.0.0\n") + mocker.ReplayAll() + self.addCleanup(mocker.VerifyAll) + self.addCleanup(mocker.UnsetStubs) + self.assertEqual("4.0.0", platform.get_pkg_version("foo")) + + class TestRpm(TestCase): # NOTE: test_not_installed is not implemented as rpm seems to only be aware