blob: a7d595429f7d28eceed17da8d09af5a6ac220965 [file] [log] [blame]
# Copyright 2015 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
from __future__ import unicode_literals
import fnmatch
from itertools import chain
import os
import re
from portage.util import shlex_split
class SonameDepsProcessor(object):
"""
Processes NEEDED.ELF.2 entries for one package, in order to generate
REQUIRES and PROVIDES metadata.
Any sonames provided by the package will automatically be filtered
from the generated REQUIRES values.
"""
def __init__(self, provides_exclude, requires_exclude):
"""
@param provides_exclude: PROVIDES_EXCLUDE value
@type provides_exclude: str
@param requires_exclude: REQUIRES_EXCLUDE value
@type requires_exclude: str
"""
self._provides_exclude = self._exclude_pattern(provides_exclude)
self._requires_exclude = self._exclude_pattern(requires_exclude)
self._requires_map = {}
self._provides_map = {}
self._provides_unfiltered = {}
self._provides = None
self._requires = None
self._intersected = False
@staticmethod
def _exclude_pattern(s):
# shlex_split enables quoted whitespace inside patterns
if s:
pat = re.compile("|".join(
fnmatch.translate(x.lstrip(os.sep))
for x in shlex_split(s)))
else:
pat = None
return pat
def add(self, entry):
"""
Add one NEEDED.ELF.2 entry, for inclusion in the generated
REQUIRES and PROVIDES values.
@param entry: NEEDED.ELF.2 entry
@type entry: NeededEntry
"""
multilib_cat = entry.multilib_category
if multilib_cat is None:
# This usage is invalid. The caller must ensure that
# the multilib category data is supplied here.
raise AssertionError(
"Missing multilib category data: %s" % entry.filename)
if entry.needed and (
self._requires_exclude is None or
self._requires_exclude.match(
entry.filename.lstrip(os.sep)) is None):
for x in entry.needed:
if (self._requires_exclude is None or
self._requires_exclude.match(x) is None):
self._requires_map.setdefault(
multilib_cat, set()).add(x)
if entry.soname:
self._provides_unfiltered.setdefault(
multilib_cat, set()).add(entry.soname)
if entry.soname and (
self._provides_exclude is None or
(self._provides_exclude.match(
entry.filename.lstrip(os.sep)) is None and
self._provides_exclude.match(entry.soname) is None)):
self._provides_map.setdefault(
multilib_cat, set()).add(entry.soname)
def _intersect(self):
requires_map = self._requires_map
provides_map = self._provides_map
provides_unfiltered = self._provides_unfiltered
for multilib_cat in set(chain(requires_map, provides_map)):
requires_map.setdefault(multilib_cat, set())
provides_map.setdefault(multilib_cat, set())
provides_unfiltered.setdefault(multilib_cat, set())
for soname in list(requires_map[multilib_cat]):
if soname in provides_unfiltered[multilib_cat]:
requires_map[multilib_cat].remove(soname)
provides_data = []
for multilib_cat in sorted(provides_map):
if provides_map[multilib_cat]:
provides_data.append(multilib_cat + ":")
provides_data.extend(sorted(provides_map[multilib_cat]))
if provides_data:
self._provides = " ".join(provides_data) + "\n"
requires_data = []
for multilib_cat in sorted(requires_map):
if requires_map[multilib_cat]:
requires_data.append(multilib_cat + ":")
requires_data.extend(sorted(requires_map[multilib_cat]))
if requires_data:
self._requires = " ".join(requires_data) + "\n"
self._intersected = True
@property
def provides(self):
"""
@rtype: str
@return: PROVIDES value generated from NEEDED.ELF.2 entries
"""
if not self._intersected:
self._intersect()
return self._provides
@property
def requires(self):
"""
@rtype: str
@return: REQUIRES value generated from NEEDED.ELF.2 entries
"""
if not self._intersected:
self._intersect()
return self._requires