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
This commit is contained in:
Matthew Thode 2016-04-18 16:16:00 -05:00
parent a0f517c435
commit 03288d3263
No known key found for this signature in database
GPG Key ID: 64A37BEAAE19A4E8
2 changed files with 73 additions and 2 deletions

View File

@ -45,7 +45,7 @@ rule = <name>: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.

View File

@ -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