remove the dependency of rpmUtils

The rpmUtils module is provided yum-utils package, which is only
available for Python 2. There is no replacement for the functionality in
DNF.

There is a proposal to add this functionality to rpm itself, but it's
not really moving forward very much:
https://bugzilla.redhat.com/show_bug.cgi?id=1072972

As a short term solution let's copy the needed parts of rpmUtils.arch
module directly to pungi code base.

Fixes: https://pagure.io/pungi/issue/533
Signed-off-by: Qixiang Wan <qwan@redhat.com>
This commit is contained in:
Qixiang Wan 2017-03-21 12:32:53 +08:00 committed by Lubomír Sedlář
parent d92390b80b
commit 0a3e5b27bf
7 changed files with 390 additions and 20 deletions

View File

@ -46,7 +46,7 @@ PyPI or from tarball. You will still need to install all of the non-Python
packages above as they are used by calling an executable. :: packages above as they are used by calling an executable. ::
$ mkvirtualenv pungienv $ mkvirtualenv pungienv
$ for pkg in koji rpm rpmUtils pykickstart selinux createrepo yum urlgrabber; do ln -vs "$(deactivate && python -c 'import os, '$pkg'; print os.path.dirname('$pkg'.__file__)')" "$(virtualenvwrapper_get_site_packages_dir)"; done $ for pkg in koji rpm pykickstart selinux createrepo yum urlgrabber; do ln -vs "$(deactivate && python -c 'import os, '$pkg'; print os.path.dirname('$pkg'.__file__)')" "$(virtualenvwrapper_get_site_packages_dir)"; done
$ for pkg in _selinux deltarpm _deltarpm krbV sqlitecachec _sqlitecache; do ln -vs "$(deactivate && python -c 'import os, '$pkg'; print '$pkg'.__file__')" "$(virtualenvwrapper_get_site_packages_dir)"; done $ for pkg in _selinux deltarpm _deltarpm krbV sqlitecachec _sqlitecache; do ln -vs "$(deactivate && python -c 'import os, '$pkg'; print '$pkg'.__file__')" "$(virtualenvwrapper_get_site_packages_dir)"; done
$ PYCURL_SSL_LIBRARY=nss pip install pycurl --no-binary :all: $ PYCURL_SSL_LIBRARY=nss pip install pycurl --no-binary :all:
$ pip install lxml pyopenssl mock sphinx setuptools nose nose-cov productmd jsonschema requests lockfile python-multilib kobo $ pip install lxml pyopenssl mock sphinx setuptools nose nose-cov productmd jsonschema requests lockfile python-multilib kobo

View File

@ -13,9 +13,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, see <https://gnu.org/licenses/>. # along with this program; if not, see <https://gnu.org/licenses/>.
from .arch_utils import arches as ALL_ARCHES
import rpmUtils.arch from .arch_utils import getBaseArch, getMultiArchInfo, getArchList
TREE_ARCH_YUM_ARCH_MAP = { TREE_ARCH_YUM_ARCH_MAP = {
"i386": "athlon", "i386": "athlon",
@ -27,13 +26,13 @@ TREE_ARCH_YUM_ARCH_MAP = {
def tree_arch_to_yum_arch(tree_arch): def tree_arch_to_yum_arch(tree_arch):
# this is basically an opposite to rpmUtils.arch.getBaseArch() # this is basically an opposite to pungi.arch_utils.getBaseArch()
yum_arch = TREE_ARCH_YUM_ARCH_MAP.get(tree_arch, tree_arch) yum_arch = TREE_ARCH_YUM_ARCH_MAP.get(tree_arch, tree_arch)
return yum_arch return yum_arch
def get_multilib_arch(yum_arch): def get_multilib_arch(yum_arch):
arch_info = rpmUtils.arch.getMultiArchInfo(yum_arch) arch_info = getMultiArchInfo(yum_arch)
if arch_info is None: if arch_info is None:
return None return None
return arch_info[0] return arch_info[0]
@ -44,14 +43,14 @@ def get_valid_multilib_arches(tree_arch):
multilib_arch = get_multilib_arch(yum_arch) multilib_arch = get_multilib_arch(yum_arch)
if not multilib_arch: if not multilib_arch:
return [] return []
return [i for i in rpmUtils.arch.getArchList(multilib_arch) if i not in ("noarch", "src")] return [i for i in getArchList(multilib_arch) if i not in ("noarch", "src")]
def get_valid_arches(tree_arch, multilib=True, add_noarch=True, add_src=False): def get_valid_arches(tree_arch, multilib=True, add_noarch=True, add_src=False):
result = [] result = []
yum_arch = tree_arch_to_yum_arch(tree_arch) yum_arch = tree_arch_to_yum_arch(tree_arch)
for arch in rpmUtils.arch.getArchList(yum_arch): for arch in getArchList(yum_arch):
if arch not in result: if arch not in result:
result.append(arch) result.append(arch)
@ -70,7 +69,7 @@ def get_valid_arches(tree_arch, multilib=True, add_noarch=True, add_src=False):
def get_compatible_arches(arch, multilib=False): def get_compatible_arches(arch, multilib=False):
tree_arch = rpmUtils.arch.getBaseArch(arch) tree_arch = getBaseArch(arch)
compatible_arches = get_valid_arches(tree_arch, multilib=multilib) compatible_arches = get_valid_arches(tree_arch, multilib=multilib)
return compatible_arches return compatible_arches
@ -78,7 +77,7 @@ def get_compatible_arches(arch, multilib=False):
def is_valid_arch(arch): def is_valid_arch(arch):
if arch in ("noarch", "src", "nosrc"): if arch in ("noarch", "src", "nosrc"):
return True return True
if arch in rpmUtils.arch.arches: if arch in ALL_ARCHES:
return True return True
return False return False

372
pungi/arch_utils.py Normal file
View File

@ -0,0 +1,372 @@
#!/usr/bin/python
# A copy of some necessary parts from yum.rpmUtils.arch, with slightly changes:
# 1. _ppc64_native_is_best changed to True
# 2. code style fixes for flake8 reported errors
import os
import rpm
import ctypes
import struct
# _ppc64_native_is_best is False in yum's source code, but patched with a
# separate patch when built from source rpm, so we set it to True here.
_ppc64_native_is_best = True
# dict mapping arch -> ( multicompat, best personality, biarch personality )
multilibArches = {"x86_64": ("athlon", "x86_64", "athlon"),
"sparc64v": ("sparcv9v", "sparcv9v", "sparc64v"),
"sparc64": ("sparcv9", "sparcv9", "sparc64"),
"ppc64": ("ppc", "ppc", "ppc64"),
"s390x": ("s390", "s390x", "s390"),
}
if _ppc64_native_is_best:
multilibArches["ppc64"] = ("ppc", "ppc64", "ppc64")
arches = {
# ia32
"athlon": "i686",
"i686": "i586",
"geode": "i586",
"i586": "i486",
"i486": "i386",
"i386": "noarch",
# amd64
"x86_64": "athlon",
"amd64": "x86_64",
"ia32e": "x86_64",
# ppc64le
"ppc64le": "noarch",
# ppc
"ppc64p7": "ppc64",
"ppc64pseries": "ppc64",
"ppc64iseries": "ppc64",
"ppc64": "ppc",
"ppc": "noarch",
# s390{,x}
"s390x": "s390",
"s390": "noarch",
# sparc
"sparc64v": "sparcv9v",
"sparc64": "sparcv9",
"sparcv9v": "sparcv9",
"sparcv9": "sparcv8",
"sparcv8": "sparc",
"sparc": "noarch",
# alpha
"alphaev7": "alphaev68",
"alphaev68": "alphaev67",
"alphaev67": "alphaev6",
"alphaev6": "alphapca56",
"alphapca56": "alphaev56",
"alphaev56": "alphaev5",
"alphaev5": "alphaev45",
"alphaev45": "alphaev4",
"alphaev4": "alpha",
"alpha": "noarch",
# arm
"armv7l": "armv6l",
"armv6l": "armv5tejl",
"armv5tejl": "armv5tel",
"armv5tel": "noarch",
# arm hardware floating point
"armv7hnl": "armv7hl",
"armv7hl": "armv6hl",
"armv6hl": "noarch",
# arm64
"arm64": "noarch",
# aarch64
"aarch64": "noarch",
# super-h
"sh4a": "sh4",
"sh4": "noarch",
"sh3": "noarch",
# itanium
"ia64": "noarch",
}
# Will contain information parsed from /proc/self/auxv via _parse_auxv().
# Should move into rpm really.
_aux_vector = {
"platform": "",
"hwcap": 0,
}
def isMultiLibArch(arch=None): # pragma: no cover
"""returns true if arch is a multilib arch, false if not"""
if arch is None:
arch = canonArch
if arch not in arches: # or we could check if it is noarch
return 0
if arch in multilibArches:
return 1
if arches[arch] in multilibArches:
return 1
return 0
def getArchList(thisarch=None): # pragma: no cover
# this returns a list of archs that are compatible with arch given
if not thisarch:
thisarch = canonArch
archlist = [thisarch]
while thisarch in arches:
thisarch = arches[thisarch]
archlist.append(thisarch)
# hack hack hack
# sparc64v is also sparc64 compat
if archlist[0] == "sparc64v":
archlist.insert(1, "sparc64")
# if we're a weirdo arch - add noarch on there.
if len(archlist) == 1 and archlist[0] == thisarch:
archlist.append('noarch')
return archlist
def _try_read_cpuinfo(): # pragma: no cover
""" Try to read /proc/cpuinfo ... if we can't ignore errors (ie. proc not
mounted). """
try:
return open("/proc/cpuinfo", "r")
except:
return []
def _parse_auxv(): # pragma: no cover
""" Read /proc/self/auxv and parse it into global dict for easier access
later on, very similar to what rpm does. """
# In case we can't open and read /proc/self/auxv, just return
try:
data = open("/proc/self/auxv", "rb").read()
except:
return
# Define values from /usr/include/elf.h
AT_PLATFORM = 15
AT_HWCAP = 16
fmtlen = struct.calcsize("LL")
offset = 0
platform = ctypes.c_char_p()
# Parse the data and fill in _aux_vector dict
while offset <= len(data) - fmtlen:
at_type, at_val = struct.unpack_from("LL", data, offset)
if at_type == AT_PLATFORM:
platform.value = at_val
_aux_vector["platform"] = platform.value
if at_type == AT_HWCAP:
_aux_vector["hwcap"] = at_val
offset = offset + fmtlen
def getCanonX86Arch(arch): # pragma: no cover
if arch == "i586":
for line in _try_read_cpuinfo():
if line.startswith("model name"):
if line.find("Geode(TM)") != -1:
return "geode"
break
return arch
# only athlon vs i686 isn't handled with uname currently
if arch != "i686":
return arch
# if we're i686 and AuthenticAMD, then we should be an athlon
for line in _try_read_cpuinfo():
if line.startswith("vendor") and line.find("AuthenticAMD") != -1:
return "athlon"
# i686 doesn't guarantee cmov, but we depend on it
elif line.startswith("flags"):
if line.find("cmov") == -1:
return "i586"
break
return arch
def getCanonARMArch(arch): # pragma: no cover
# the %{_target_arch} macro in rpm will let us know the abi we are using
target = rpm.expandMacro('%{_target_cpu}')
if target.startswith('armv6h'):
return target
if target.startswith('armv7h'):
return target
return arch
def getCanonPPCArch(arch): # pragma: no cover
# FIXME: should I do better handling for mac, etc?
if arch != "ppc64":
return arch
machine = None
for line in _try_read_cpuinfo():
if line.find("machine") != -1:
machine = line.split(':')[1]
break
platform = _aux_vector["platform"]
if machine is None and not platform:
return arch
try:
if platform.startswith("power") and int(platform[5:].rstrip('+')) >= 7:
return "ppc64p7"
except:
pass
if machine is None:
return arch
if machine.find("CHRP IBM") != -1:
return "ppc64pseries"
if machine.find("iSeries") != -1:
return "ppc64iseries"
return arch
def getCanonSPARCArch(arch): # pragma: no cover
# Deal with sun4v, sun4u, sun4m cases
SPARCtype = None
for line in _try_read_cpuinfo():
if line.startswith("type"):
SPARCtype = line.split(':')[1]
break
if SPARCtype is None:
return arch
if SPARCtype.find("sun4v") != -1:
if arch.startswith("sparc64"):
return "sparc64v"
else:
return "sparcv9v"
if SPARCtype.find("sun4u") != -1:
if arch.startswith("sparc64"):
return "sparc64"
else:
return "sparcv9"
if SPARCtype.find("sun4m") != -1:
return "sparcv8"
return arch
def getCanonX86_64Arch(arch): # pragma: no cover
if arch != "x86_64":
return arch
vendor = None
for line in _try_read_cpuinfo():
if line.startswith("vendor_id"):
vendor = line.split(':')[1]
break
if vendor is None:
return arch
if vendor.find("Authentic AMD") != -1 or vendor.find("AuthenticAMD") != -1:
return "amd64"
if vendor.find("GenuineIntel") != -1:
return "ia32e"
return arch
def getCanonArch(skipRpmPlatform=0): # pragma: no cover
if not skipRpmPlatform and os.access("/etc/rpm/platform", os.R_OK):
try:
f = open("/etc/rpm/platform", "r")
line = f.readline()
f.close()
(arch, vendor, opersys) = line.split("-", 2)
return arch
except:
pass
arch = os.uname()[4]
_parse_auxv()
if (len(arch) == 4 and arch[0] == "i" and arch[2:4] == "86"):
return getCanonX86Arch(arch)
if arch.startswith("arm"):
return getCanonARMArch(arch)
if arch.startswith("ppc"):
return getCanonPPCArch(arch)
if arch.startswith("sparc"):
return getCanonSPARCArch(arch)
if arch == "x86_64":
return getCanonX86_64Arch(arch)
return arch
canonArch = getCanonArch()
# this gets you the "compat" arch of a biarch pair
def getMultiArchInfo(arch=canonArch): # pragma: no cover
if arch in multilibArches:
return multilibArches[arch]
if arch in arches and arches[arch] != "noarch":
return getMultiArchInfo(arch=arches[arch])
return None
def getBaseArch(myarch=None): # pragma: no cover
"""returns 'base' arch for myarch, if specified, or canonArch if not.
base arch is the arch before noarch in the arches dict if myarch is not
a key in the multilibArches."""
if not myarch:
myarch = canonArch
if myarch not in arches: # this is dumb, but <shrug>
return myarch
if myarch.startswith("sparc64"):
return "sparc"
elif myarch == "ppc64le":
return "ppc64le"
elif myarch.startswith("ppc64") and not _ppc64_native_is_best:
return "ppc"
elif myarch.startswith("arm64"):
return "arm64"
elif myarch.startswith("armv6h"):
return "armhfp"
elif myarch.startswith("armv7h"):
return "armhfp"
elif myarch.startswith("arm"):
return "arm"
if isMultiLibArch(arch=myarch):
if myarch in multilibArches:
return myarch
else:
return arches[myarch]
if myarch in arches:
basearch = myarch
value = arches[basearch]
while value != 'noarch':
basearch = value
value = arches[basearch]
return basearch

View File

@ -17,11 +17,10 @@
import os import os
import sys import sys
import time import time
import yum
from ConfigParser import SafeConfigParser from ConfigParser import SafeConfigParser
from .arch_utils import getBaseArch
# In development, `here` will point to the bin/ directory with scripts. # In development, `here` will point to the bin/ directory with scripts.
here = sys.path[0] here = sys.path[0]
MULTILIBCONF = (os.path.join(os.path.dirname(__file__), '..', 'share', 'multilib') MULTILIBCONF = (os.path.join(os.path.dirname(__file__), '..', 'share', 'multilib')
@ -47,7 +46,7 @@ class Config(SafeConfigParser):
self.set('pungi', 'product_path', 'Packages') self.set('pungi', 'product_path', 'Packages')
self.set('pungi', 'cachedir', '/var/cache/pungi') self.set('pungi', 'cachedir', '/var/cache/pungi')
self.set('pungi', 'compress_type', 'xz') self.set('pungi', 'compress_type', 'xz')
self.set('pungi', 'arch', yum.rpmUtils.arch.getBaseArch()) self.set('pungi', 'arch', getBaseArch())
self.set('pungi', 'family', 'Fedora') self.set('pungi', 'family', 'Fedora')
self.set('pungi', 'iso_basename', 'Fedora') self.set('pungi', 'iso_basename', 'Fedora')
self.set('pungi', 'version', time.strftime('%Y%m%d', time.localtime())) self.set('pungi', 'version', time.strftime('%Y%m%d', time.localtime()))

View File

@ -18,8 +18,8 @@ import datetime
import json import json
import logging import logging
import os import os
import rpmUtils.arch
from pungi.arch_utils import getBaseArch
from pungi.util import makedirs from pungi.util import makedirs
@ -43,9 +43,9 @@ def get_ref_from_treefile(treefile, arch=None, logger=None):
try: try:
parsed = json.loads(f.read()) parsed = json.loads(f.read())
if arch is None: if arch is None:
basearch = rpmUtils.arch.getBaseArch() basearch = getBaseArch()
else: else:
basearch = rpmUtils.arch.getBaseArch(arch) basearch = getBaseArch(arch)
ref = parsed['ref'].replace('${basearch}', basearch) ref = parsed['ref'].replace('${basearch}', basearch)
except Exception as e: except Exception as e:
logger.error('Unable to get ref from treefile: %s' % e) logger.error('Unable to get ref from treefile: %s' % e)

View File

@ -22,11 +22,11 @@ import threading
import contextlib import contextlib
import koji import koji
import rpmUtils.arch
from kobo.shortcuts import run from kobo.shortcuts import run
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
from .. import util from .. import util
from ..arch_utils import getBaseArch
class KojiWrapper(object): class KojiWrapper(object):
@ -100,7 +100,7 @@ class KojiWrapper(object):
cmd.append(target) cmd.append(target)
# i686 -> i386 etc. # i686 -> i386 etc.
arch = rpmUtils.arch.getBaseArch(arch) arch = getBaseArch(arch)
cmd.append(arch) cmd.append(arch)
if isinstance(command, list): if isinstance(command, list):
@ -280,7 +280,7 @@ class KojiWrapper(object):
cmd.append(target) cmd.append(target)
# i686 -> i386 etc. # i686 -> i386 etc.
arch = rpmUtils.arch.getBaseArch(arch) arch = getBaseArch(arch)
cmd.append(arch) cmd.append(arch)
cmd.append(ks_file) cmd.append(ks_file)

View File

@ -56,7 +56,7 @@ class MockArchModule(object):
return ARCHES[yum_arch] return ARCHES[yum_arch]
@mock.patch('rpmUtils.arch', MockArchModule) @mock.patch('pungi.arch_utils', MockArchModule)
class TestArch(unittest.TestCase): class TestArch(unittest.TestCase):
def test_i386(self): def test_i386(self):