# # Copyright 2013, 2018 Free Software Foundation, Inc. # # This file is part of GNU Radio # # SPDX-License-Identifier: GPL-3.0-or-later # # """ Base class for the modules """ from __future__ import print_function from __future__ import absolute_import from __future__ import unicode_literals import os import re import glob import logging import itertools from types import SimpleNamespace from gnuradio import gr from ..tools import get_modname, SCMRepoFactory logger = logging.getLogger('gnuradio.modtool') def get_block_candidates(): """ Returns a list of all possible blocknames """ block_candidates = [] cpp_filters = ["*.cc", "*.cpp"] cpp_blocks = [] for ftr in cpp_filters: cpp_blocks += [x for x in glob.glob1("lib", ftr) if not (x.startswith('qa_') or x.startswith('test_'))] python_blocks = [x for x in glob.glob1("python", "*.py") if not (x.startswith('qa_') or x.startswith('build') or x.startswith('__init__'))] for block in itertools.chain(cpp_blocks, python_blocks): block = os.path.splitext(block)[0] block = block.split('_impl')[0] block_candidates.append(block) return block_candidates class ModToolException(Exception): """ Standard exception for modtool classes. """ pass class ModTool(object): """ Base class for all modtool command classes. """ name = 'base' description = None def __init__(self, blockname=None, module_name=None, **kwargs): # List subdirs where stuff happens self._subdirs = ['lib', 'include', 'python', 'grc'] self.has_subdirs = {} self.skip_subdirs = {} self.info = {} self._file = {} for subdir in self._subdirs: self.has_subdirs[subdir] = False self.skip_subdirs[subdir] = False self.info['blockname'] = blockname self.info['modname'] = module_name self.cli = kwargs.get('cli', False) self.dir = kwargs.get('directory', '.') self.skip_subdirs['lib'] = kwargs.get('skip_lib', False) self.skip_subdirs['python'] = kwargs.get('skip_python', False) self.skip_subdirs['pybind'] = kwargs.get('skip_pybind', False) self.skip_subdirs['grc'] = kwargs.get('skip_grc', False) self._scm = kwargs.get('scm_mode', gr.prefs().get_string('modtool', 'scm_mode', 'no')) if not self.cli: logging.basicConfig(level=logging.ERROR, format='%(message)s') self.info['yes'] = True else: self.info['yes'] = kwargs.get('yes', False) from ..cli import setup_cli_logger setup_cli_logger(logger) if not type(self).__name__ in ['ModToolInfo', 'ModToolNewModule']: if self.cli: self._validate() def _validate(self): """ Validates the arguments """ if not isinstance(self.skip_subdirs['lib'], bool): raise ModToolException('Expected a boolean value for skip_lib') if not isinstance(self.skip_subdirs['pybind'], bool): raise ModToolException('Expected a boolean value for skip_pybind') if not isinstance(self.skip_subdirs['python'], bool): raise ModToolException('Expected a boolean value for skip_python') if not isinstance(self.skip_subdirs['grc'], bool): raise ModToolException('Expected a boolean value for skip_grc') self._assign() def _assign(self): if not self._check_directory(self.dir): raise ModToolException('No GNU Radio module found in the given directory.') if self.info['modname'] is None: self.info['modname'] = get_modname() if self.info['modname'] is None: raise ModToolException('No GNU Radio module found in the given directory.') if self.info['version'] == '36' and ( os.path.isdir(os.path.join('include', self.info['modname'])) or os.path.isdir(os.path.join('include', 'gnuradio', self.info['modname'])) ): self.info['version'] = '37' if not os.path.isfile(os.path.join('cmake', 'Modules', 'FindCppUnit.cmake')): self.info['version'] = '38' if self.skip_subdirs['lib'] or not self.has_subdirs['lib']: self.skip_subdirs['lib'] = True if not self.has_subdirs['python']: self.skip_subdirs['python'] = True # if not self.has_subdirs['pybind']: # self.skip_subdirs['pybind'] = True if not self.has_subdirs['grc']: self.skip_subdirs['grc'] = True self._setup_files() self._setup_scm() def _setup_files(self): """ Initialise the self._file[] dictionary """ self.info['pydir'] = 'python' if os.path.isdir(os.path.join('python', self.info['modname'])): self.info['pydir'] = os.path.join('python', self.info['modname']) self._file['qalib'] = os.path.join('lib', f'qa_{self.info["modname"]}.cc') self._file['pyinit'] = os.path.join(self.info['pydir'], '__init__.py') self._file['cmlib'] = os.path.join('lib', 'CMakeLists.txt') self._file['cmgrc'] = os.path.join('grc', 'CMakeLists.txt') self._file['cmpython'] = os.path.join(self.info['pydir'], 'CMakeLists.txt') self._file['cmpybind'] = os.path.join(self.info['pydir'], 'bindings', 'CMakeLists.txt') self._file['ccpybind'] = os.path.join(self.info['pydir'], 'bindings', 'python_bindings.cc') if self.info['is_component']: self.info['includedir'] = os.path.join('include', 'gnuradio', self.info['modname']) elif self.info['version'] in ('37', '38'): self.info['includedir'] = os.path.join('include', self.info['modname']) else: self.info['includedir'] = 'include' self._file['cminclude'] = os.path.join(self.info['includedir'], 'CMakeLists.txt') self._file['cmfind'] = os.path.join('cmake', 'Modules', 'howtoConfig.cmake') def _setup_scm(self, mode='active'): """ Initialize source control management. """ self.options = SimpleNamespace(scm_mode = self._scm) if mode == 'active': self.scm = SCMRepoFactory(self.options, '.').make_active_scm_manager() else: self.scm = SCMRepoFactory(self.options, '.').make_empty_scm_manager() if self.scm is None: logger.error("Error: Can't set up SCM.") exit(1) def _check_directory(self, directory): """ Guesses if dir is a valid GNU Radio module directory by looking for CMakeLists.txt and at least one of the subdirs lib/, and python/ Changes the directory, if valid. """ has_makefile = False try: files = os.listdir(directory) os.chdir(directory) except OSError: logger.error(f"Can't read or chdir to directory {directory}.") return False self.info['is_component'] = False for f in files: if os.path.isfile(f) and f == 'CMakeLists.txt': with open(f) as filetext: if re.search(r'find_package\(Gnuradio', filetext.read()) is not None: self.info['version'] = '36' # Might be 37, check that later has_makefile = True elif re.search('GR_REGISTER_COMPONENT', filetext.read()) is not None: self.info['version'] = '36' # Might be 37, check that later self.info['is_component'] = True has_makefile = True # TODO search for autofoo elif os.path.isdir(f): if (f in list(self.has_subdirs.keys())): self.has_subdirs[f] = True else: self.skip_subdirs[f] = True return bool(has_makefile and (list(self.has_subdirs.values()))) def run(self): """ Override this. """ raise NotImplementedError('Module implementation missing')