# # Copyright 2013, 2018-2020 Free Software Foundation, Inc. # # This file is part of GNU Radio # # SPDX-License-Identifier: GPL-3.0-or-later # # """ Remove blocks module """ from __future__ import print_function from __future__ import absolute_import from __future__ import unicode_literals import os import re import sys import glob import logging from ..tools import remove_pattern_from_file, CMakeFileEditor, CPPFileEditor from .base import ModTool, ModToolException logger = logging.getLogger(__name__) class ModToolRemove(ModTool): """ Remove block (delete files and remove Makefile entries) """ name = 'remove' description = 'Remove a block from a module.' def __init__(self, blockname=None, **kwargs): ModTool.__init__(self, blockname, **kwargs) self.info['pattern'] = blockname def validate(self): """ Validates the arguments """ ModTool._validate(self) if not self.info['pattern'] or self.info['pattern'].isspace(): raise ModToolException("Incorrect blockname (Regex)!") def run(self): """ Go, go, go! """ # This portion will be covered by the CLI if not self.cli: self.validate() else: from ..cli import cli_input def _remove_cc_test_case(filename=None, ed=None): """ Special function that removes the occurrences of a qa*.cc file from the CMakeLists.txt. """ modname_ = self.info['modname'] if filename[:2] != 'qa': return if self.info['version'] == '37': (base, ext) = os.path.splitext(filename) if ext == '.h': remove_pattern_from_file(self._file['qalib'], fr'^#include "{filename}"\s*$') remove_pattern_from_file(self._file['qalib'], fr'^\s*s->addTest\(gr::{modname_}::{base}::suite\(\)\);\s*$' ) self.scm.mark_file_updated(self._file['qalib']) elif ext == '.cc': ed.remove_value('list', r'\$\{CMAKE_CURRENT_SOURCE_DIR\}/%s' % filename, to_ignore_start=f'APPEND test_{modname_}_sources') self.scm.mark_file_updated(ed.filename) elif self.info['version'] == '38': (base, ext) = os.path.splitext(filename) if ext == '.cc': ed.remove_value( 'list', filename, to_ignore_start=f'APPEND test_{modname_}_sources' ) self.scm.mark_file_updated(ed.filename) else: filebase = os.path.splitext(filename)[0] ed.delete_entry('add_executable', filebase) ed.delete_entry('target_link_libraries', filebase) ed.delete_entry('GR_ADD_TEST', filebase) ed.remove_double_newlines() self.scm.mark_file_updated(ed.filename) def _remove_py_test_case(filename=None, ed=None): """ Special function that removes the occurrences of a qa*.{cc,h} file from the CMakeLists.txt and the qa_$modname.cc. """ if filename[:2] != 'qa': return filebase = os.path.splitext(filename)[0] ed.delete_entry('GR_ADD_TEST', filebase) ed.remove_double_newlines() # Go, go, go! if not self.skip_subdirs['lib']: self._run_subdir('lib', ('*.cc', '*.h'), ('add_library', 'list'), cmakeedit_func=_remove_cc_test_case) if not self.skip_subdirs['include']: incl_files_deleted = self._run_subdir(self.info['includedir'], ('*.h',), ('install',)) if not self.skip_subdirs['python']: py_files_deleted = self._run_subdir('python', ('*.py',), ('GR_PYTHON_INSTALL',), cmakeedit_func=_remove_py_test_case) for f in py_files_deleted: remove_pattern_from_file(self._file['pyinit'], fr'.*import\s+{f[:-3]}.*') remove_pattern_from_file(self._file['pyinit'], fr'.*from\s+{f[:-3]}\s+import.*\n') pb_files_deleted = self._run_subdir('python/bindings', ('*.cc',), ('list',)) pbdoc_files_deleted = self._run_subdir('python/bindings/docstrings', ('*.h',), ('',)) # Update python_bindings.cc ed = CPPFileEditor(self._file['ccpybind']) ed.remove_value('// BINDING_FUNCTION_PROTOTYPES(', '// ) END BINDING_FUNCTION_PROTOTYPES', 'void bind_' + self.info['blockname'] + '(py::module& m);') ed.remove_value('// BINDING_FUNCTION_CALLS(', '// ) END BINDING_FUNCTION_CALLS', 'bind_' + self.info['blockname'] + '(m);') ed.write() if not self.skip_subdirs['grc']: self._run_subdir('grc', ('*.yml',), ('install',)) def _run_subdir(self, path, globs, makefile_vars, cmakeedit_func=None): """ Delete all files that match a certain pattern in path. path - The directory in which this will take place globs - A tuple of standard UNIX globs of files to delete (e.g. *.yml) makefile_vars - A tuple with a list of CMakeLists.txt-variables which may contain references to the globbed files cmakeedit_func - If the CMakeLists.txt needs special editing, use this """ if self.cli: from ..cli import cli_input # 1. Create a filtered list files = [] for g in globs: files = files + sorted(glob.glob(f"{path}/{g}")) files_filt = [] logger.info(f"Searching for matching files in {path}/:") for f in files: if re.search(self.info['pattern'], os.path.basename(f)) is not None: files_filt.append(f) if len(files_filt) == 0: logger.info("None found.") return [] # 2. Delete files, Makefile entries and other occurrences files_deleted = [] yes = self.info['yes'] for f in files_filt: b = os.path.basename(f) if not yes and self.cli: ans = cli_input(f"Really delete {f}? [Y/n/a/q]: ").lower().strip() if ans == 'a': yes = True if ans == 'q': sys.exit(0) if ans == 'n': continue files_deleted.append(b) logger.info(f"Deleting {f}.") self.scm.remove_file(f) os.unlink(f) if (os.path.exists(f'{path}/CMakeLists.txt')): ed = CMakeFileEditor(f'{path}/CMakeLists.txt') logger.info(f"Deleting occurrences of {b} from {path}/CMakeLists.txt...") for var in makefile_vars: ed.remove_value(var, b) if cmakeedit_func is not None: cmakeedit_func(b, ed) ed.write() self.scm.mark_files_updated((f'{path}/CMakeLists.txt')) return files_deleted