From 999c0e723240ee783bca17942f77a9d05bbfc168 Mon Sep 17 00:00:00 2001 From: Josh Morman <mormjb@gmail.com> Date: Thu, 23 Apr 2020 15:03:55 -0400 Subject: utils: add functionality to generate bindings This currently exists in two places 1) Bindtool (longevity TBD) which calls blocktool to parse the public header file in the include directory 2) Modtool - binding of headers added to add and bind. rm, update, info, etc still TODO --- gr-utils/modtool/tools/cppfile_editor.py | 117 +++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 gr-utils/modtool/tools/cppfile_editor.py (limited to 'gr-utils/modtool/tools/cppfile_editor.py') diff --git a/gr-utils/modtool/tools/cppfile_editor.py b/gr-utils/modtool/tools/cppfile_editor.py new file mode 100644 index 0000000000..9f2d3f1fda --- /dev/null +++ b/gr-utils/modtool/tools/cppfile_editor.py @@ -0,0 +1,117 @@ +# +# Copyright 2013, 2018 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# +""" Edit C++ files for bindings """ + +from __future__ import print_function +from __future__ import absolute_import +from __future__ import unicode_literals + +import re +import logging + +logger = logging.getLogger(__name__) + + +class CPPFileEditor(object): + """A tool for editing CMakeLists.txt files. """ + def __init__(self, filename, separator='\n ', indent=' '): + self.filename = filename + with open(filename, 'r') as f: + self.cfile = f.read() + self.separator = separator + self.indent = indent + + # def append_value(self, entry, value, to_ignore_start='', to_ignore_end=''): + # """ Add a value to an entry. """ + # regexp = re.compile(r'({}\({}[^()]*?)\s*?(\s?{})\)'.format(entry, to_ignore_start, to_ignore_end), + # re.MULTILINE) + # substi = r'\1' + self.separator + value + r'\2)' + # (self.cfile, nsubs) = regexp.subn(substi, self.cfile, count=1) + # return nsubs + + def append_value(self, start_tag, end_tag, value): + """ Add a value to an entry. """ + cfile_lines = self.cfile.splitlines() + start_line_idx = [cfile_lines.index(s) for s in cfile_lines if start_tag in s][0] + end_line_idx = [cfile_lines.index(s) for s in cfile_lines if end_tag in s][0] + + self.cfile = '\n'.join((cfile_lines[0:end_line_idx]+[self.indent + value]+cfile_lines[end_line_idx:])) + return 1 + + + def remove_value(self, entry, value, to_ignore_start='', to_ignore_end=''): + # TODO - gr_modtool rm + pass + + def delete_entry(self, entry, value_pattern=''): + """Remove an entry from the current buffer.""" + regexp = r'{}\s*\([^()]*{}[^()]*\)[^\n]*\n'.format(entry, value_pattern) + regexp = re.compile(regexp, re.MULTILINE) + (self.cfile, nsubs) = re.subn(regexp, '', self.cfile, count=1) + return nsubs + + def write(self): + """ Write the changes back to the file. """ + with open(self.filename, 'w') as f: + f.write(self.cfile) + + def remove_double_newlines(self): + """Simply clear double newlines from the file buffer.""" + self.cfile = re.compile('\n\n\n+', re.MULTILINE).sub('\n\n', self.cfile) + + def find_filenames_match(self, regex): + """ Find the filenames that match a certain regex + on lines that aren't comments """ + filenames = [] + reg = re.compile(regex) + fname_re = re.compile(r'[a-zA-Z]\w+\.\w{1,5}$') + for line in self.cfile.splitlines(): + if len(line.strip()) == 0 or line.strip()[0] == '#': + continue + for word in re.split('[ /)(\t\n\r\f\v]', line): + if fname_re.match(word) and reg.search(word): + filenames.append(word) + return filenames + + def disable_file(self, fname): + """ Comment out a file. + Example: + add_library( + file1.cc + ) + + Here, file1.cc becomes #file1.cc with disable_file('file1.cc'). + """ + starts_line = False + for line in self.cfile.splitlines(): + if len(line.strip()) == 0 or line.strip()[0] == '#': + continue + if re.search(r'\b'+fname+r'\b', line): + if re.match(fname, line.lstrip()): + starts_line = True + break + comment_out_re = r'#\1' + '\n' + self.indent + if not starts_line: + comment_out_re = r'\n' + self.indent + comment_out_re + (self.cfile, nsubs) = re.subn(r'(\b'+fname+r'\b)\s*', comment_out_re, self.cfile) + if nsubs == 0: + logger.warning("Warning: A replacement failed when commenting out {}. Check the CMakeFile.txt manually.".format(fname)) + elif nsubs > 1: + logger.warning("Warning: Replaced {} {} times (instead of once). Check the CMakeFile.txt manually.".format(fname, nsubs)) + + def comment_out_lines(self, pattern, comment_str='#'): + """ Comments out all lines that match with pattern """ + for line in self.cfile.splitlines(): + if re.search(pattern, line): + self.cfile = self.cfile.replace(line, comment_str+line) + + def check_for_glob(self, globstr): + """ Returns true if a glob as in globstr is found in the cmake file """ + glob_re = r'GLOB\s[a-z_]+\s"{}"'.format(globstr.replace('*', r'\*')) + return re.search(glob_re, self.cfile, flags=re.MULTILINE|re.IGNORECASE) is not None -- cgit v1.2.3 From fbb1f97a4d3e879fcfd15e9826f3b05cfc8504bd Mon Sep 17 00:00:00 2001 From: Josh Morman <mormjb@gmail.com> Date: Fri, 29 May 2020 08:05:28 -0400 Subject: pybind: incorporate modtool rm --- gr-utils/modtool/core/add.py | 2 +- gr-utils/modtool/core/rm.py | 38 +++++++++++++++++++++++--------- gr-utils/modtool/tools/cppfile_editor.py | 22 +++++++++++++++--- 3 files changed, 47 insertions(+), 15 deletions(-) (limited to 'gr-utils/modtool/tools/cppfile_editor.py') diff --git a/gr-utils/modtool/core/add.py b/gr-utils/modtool/core/add.py index 872d0a08a9..40bfe74d3f 100644 --- a/gr-utils/modtool/core/add.py +++ b/gr-utils/modtool/core/add.py @@ -1,5 +1,5 @@ # -# Copyright 2013-2014,2017-2019 Free Software Foundation, Inc. +# Copyright 2013-2014,2017-2020 Free Software Foundation, Inc. # # This file is part of GNU Radio # diff --git a/gr-utils/modtool/core/rm.py b/gr-utils/modtool/core/rm.py index 1a2a3535e4..b65465cd16 100644 --- a/gr-utils/modtool/core/rm.py +++ b/gr-utils/modtool/core/rm.py @@ -1,5 +1,5 @@ # -# Copyright 2013, 2018, 2019 Free Software Foundation, Inc. +# Copyright 2013, 2018-2020 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -18,7 +18,7 @@ import sys import glob import logging -from ..tools import remove_pattern_from_file, CMakeFileEditor +from ..tools import remove_pattern_from_file, CMakeFileEditor, CPPFileEditor from .base import ModTool, ModToolException logger = logging.getLogger(__name__) @@ -102,6 +102,19 @@ class ModToolRemove(ModTool): 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',)) @@ -120,7 +133,7 @@ class ModToolRemove(ModTool): for g in globs: files = files + sorted(glob.glob(f"{path}/{g}")) files_filt = [] - logger.info("Searching for matching files in {path}/:") + 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) @@ -129,7 +142,6 @@ class ModToolRemove(ModTool): return [] # 2. Delete files, Makefile entries and other occurrences files_deleted = [] - ed = CMakeFileEditor(f'{path}/CMakeLists.txt') yes = self.info['yes'] for f in files_filt: b = os.path.basename(f) @@ -145,11 +157,15 @@ class ModToolRemove(ModTool): logger.info(f"Deleting {f}.") self.scm.remove_file(f) os.unlink(f) - 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')) + + 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 diff --git a/gr-utils/modtool/tools/cppfile_editor.py b/gr-utils/modtool/tools/cppfile_editor.py index 9f2d3f1fda..052a887221 100644 --- a/gr-utils/modtool/tools/cppfile_editor.py +++ b/gr-utils/modtool/tools/cppfile_editor.py @@ -45,9 +45,25 @@ class CPPFileEditor(object): return 1 - def remove_value(self, entry, value, to_ignore_start='', to_ignore_end=''): - # TODO - gr_modtool rm - pass + def remove_value(self, start_tag, end_tag, value): + + cfile_lines = self.cfile.splitlines() + try: + start_line_idx = [cfile_lines.index(s) for s in cfile_lines if start_tag in s][0] + end_line_idx = [cfile_lines.index(s) for s in cfile_lines if end_tag in s][0] + except: + logger.warning("Could not find start or end tags in search") + return 0 + + try: + lines_between_tags = cfile_lines[(start_line_idx+1):end_line_idx] + remove_index = [lines_between_tags.index(s) for s in cfile_lines if value in s][0] + lines_between_tags.pop(remove_index) + except: + return 0 + + self.cfile = '\n'.join((cfile_lines[0:(start_line_idx+1)]+lines_between_tags+cfile_lines[end_line_idx:])) + return 1 def delete_entry(self, entry, value_pattern=''): """Remove an entry from the current buffer.""" -- cgit v1.2.3