Source code for rebasehelper.helpers.macro_helper

# -*- coding: utf-8 -*-
# This tool helps you rebase your package to the latest version
# Copyright (C) 2013-2019 Red Hat, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# Authors: Petr Hráček <>
#          Tomáš Hozza <>
#          Nikola Forró <>
#          František Nečas <>

import re
from typing import List

import rpm  # type: ignore
from pkg_resources import parse_version

from rebasehelper.helpers.console_helper import ConsoleHelper

[docs]class MacroHelper: """Class for working with RPM macros""" MACROS_WHITELIST: List[str] = [ '_bindir', '_datadir', '_includedir', '_infodir', '_initdir', '_libdir', '_libexecdir', '_localstatedir', '_mandir', '_sbindir', '_sharedstatedir', '_sysconfdir', 'python2_sitelib', 'python3_sitelib', ]
[docs] @staticmethod def expand(s, default=None, suppress_errors=False): try: if not suppress_errors: return rpm.expandMacro(s) with ConsoleHelper.Capturer(stderr=True): return rpm.expandMacro(s) except rpm.error: return default
[docs] @classmethod def purge_macro(cls, macro: str) -> None: m = '%{{{}}}'.format(macro) while cls.expand(m, m) != m: with ConsoleHelper.Capturer(stderr=True): rpm.delMacro(macro)
[docs] @classmethod def expand_macros(cls, macros): """Expands values of multiple macros. Args: macros (list): List of macros to be expanded, macros are represented as dicts. Returns: list: List of macros with expanded values. """ for macro in macros: macro['value'] = cls.expand(macro['value']) return macros
[docs] @staticmethod def substitute_path_with_macros(path, macros): """Substitutes parts of a path with macros. Args: path (str): Path to be changed. macros (list): Macros which can be used as a substitution. Returns: str: Path expressed using macros. """ for m in macros: if m['value'] and m['value'] in path: path = path.replace(m['value'], '%{{{}}}'.format(m['name'])) return path
[docs] @staticmethod def dump(): """Gets list of all defined macros. Returns: list: All defined macros. """ macro_re = re.compile( r''' ^\s* (?P<level>-?\d+) (?P<used>=|:) [ ] (?P<name>\w+) (?P<options>\(.+?\))? [\t] (?P<value>.*) $ ''', re.VERBOSE) with ConsoleHelper.Capturer(stderr=True) as capturer: rpm.expandMacro('%dump') macros = [] def add_macro(properties): macro = dict(properties) macro['used'] = macro['used'] == '=' macro['level'] = int(macro['level']) if parse_version(rpm.__version__) < parse_version('4.13.90'): # in RPM < 4.13.90 level of some macros is decreased by 1 if macro['level'] == -1: # this could be macro with level -1 or level 0, we can not be sure # so just duplicate the macro for both levels macros.append(macro) macro = dict(macro) macro['level'] = 0 macros.append(macro) elif macro['level'] in (-14, -16): macro['level'] += 1 macros.append(macro) else: macros.append(macro) else: macros.append(macro) lines = capturer.stderr.strip().split('\n') # last line contains only summary lines.pop() while lines: line = lines.pop(0) # squash lines ending with \ while line.endswith('\\'): line = line[:-1] + lines.pop(0) match = macro_re.match(line) if match: add_macro(match.groupdict()) elif macros: # part of the value of the last parsed macro macros[-1]['value'] += '\n' + line return macros
[docs] @staticmethod def filter(macros, **kwargs): """Finds all macros satisfying certain conditions. Args: macros (list): Macros to be filtered. **kwargs: Filters to be used. Returns: list: Macros satisfying the conditions. """ def _test(macro): return all(macro.get(k[4:]) >= v if k.startswith('min_') else macro.get(k[4:]) <= v if k.startswith('max_') else macro.get(k) == v for k, v in kwargs.items()) return [m for m in macros if _test(m)]