diff options
Diffstat (limited to 'gr-utils/src/python/modtool/modtool_add.py')
-rw-r--r-- | gr-utils/src/python/modtool/modtool_add.py | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/gr-utils/src/python/modtool/modtool_add.py b/gr-utils/src/python/modtool/modtool_add.py new file mode 100644 index 000000000..e1d61cf0f --- /dev/null +++ b/gr-utils/src/python/modtool/modtool_add.py @@ -0,0 +1,310 @@ +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# +""" Module to add new blocks """ + +import os +import sys +import re +from optparse import OptionGroup + +from util_functions import append_re_line_sequence, ask_yes_no +from cmakefile_editor import CMakeFileEditor +from modtool_base import ModTool +from templates import Templates +from code_generator import get_template +import Cheetah.Template + +class ModToolAdd(ModTool): + """ Add block to the out-of-tree module. """ + name = 'add' + aliases = ('insert',) + _block_types = ('sink', 'source', 'sync', 'decimator', 'interpolator', + 'general', 'hier', 'noblock') + def __init__(self): + ModTool.__init__(self) + self._add_cc_qa = False + self._add_py_qa = False + + def setup_parser(self): + parser = ModTool.setup_parser(self) + ogroup = OptionGroup(parser, "Add module options") + ogroup.add_option("-t", "--block-type", type="choice", + choices=self._block_types, default=None, help="One of %s." % ', '.join(self._block_types)) + ogroup.add_option("--license-file", type="string", default=None, + help="File containing the license header for every source code file.") + ogroup.add_option("--argument-list", type="string", default=None, + help="The argument list for the constructor and make functions.") + ogroup.add_option("--add-python-qa", action="store_true", default=None, + help="If given, Python QA code is automatically added if possible.") + ogroup.add_option("--add-cpp-qa", action="store_true", default=None, + help="If given, C++ QA code is automatically added if possible.") + ogroup.add_option("--skip-cmakefiles", action="store_true", default=False, + help="If given, only source files are written, but CMakeLists.txt files are left unchanged.") + ogroup.add_option("-l", "--lang", type="choice", choices=('cpp', 'c++', 'python'), + default='cpp', help="Language (cpp or python)") + parser.add_option_group(ogroup) + return parser + + def setup(self): + ModTool.setup(self) + options = self.options + self._info['blocktype'] = options.block_type + if self._info['blocktype'] is None: + while self._info['blocktype'] not in self._block_types: + self._info['blocktype'] = raw_input("Enter code type: ") + if self._info['blocktype'] not in self._block_types: + print 'Must be one of ' + str(self._block_types) + self._info['lang'] = options.lang + if self._info['lang'] == 'c++': + self._info['lang'] = 'cpp' + print "Language: %s" % {'cpp': 'C++', 'python': 'Python'}[self._info['lang']] + + if ((self._skip_subdirs['lib'] and self._info['lang'] == 'cpp') + or (self._skip_subdirs['python'] and self._info['lang'] == 'python')): + print "Missing or skipping relevant subdir." + exit(1) + + if self._info['blockname'] is None: + if len(self.args) >= 2: + self._info['blockname'] = self.args[1] + else: + self._info['blockname'] = raw_input("Enter name of block/code (without module name prefix): ") + if not re.match('[a-zA-Z0-9_]+', self._info['blockname']): + print 'Invalid block name.' + exit(2) + print "Block/code identifier: " + self._info['blockname'] + self._info['fullblockname'] = self._info['modname'] + '_' + self._info['blockname'] + self._info['license'] = self.setup_choose_license() + + if options.argument_list is not None: + self._info['arglist'] = options.argument_list + else: + self._info['arglist'] = raw_input('Enter valid argument list, including default arguments: ') + + if not (self._info['blocktype'] in ('noblock') or self._skip_subdirs['python']): + self._add_py_qa = options.add_python_qa + if self._add_py_qa is None: + self._add_py_qa = ask_yes_no('Add Python QA code?', True) + if self._info['lang'] == 'cpp': + self._add_cc_qa = options.add_cpp_qa + if self._add_cc_qa is None: + self._add_cc_qa = ask_yes_no('Add C++ QA code?', not self._add_py_qa) + if self._info['version'] == 'autofoo' and not self.options.skip_cmakefiles: + print "Warning: Autotools modules are not supported. ", + print "Files will be created, but Makefiles will not be edited." + self.options.skip_cmakefiles = True + + + def setup_choose_license(self): + """ Select a license by the following rules, in this order: + 1) The contents of the file given by --license-file + 2) The contents of the file LICENSE or LICENCE in the modules + top directory + 3) The default license. """ + if self.options.license_file is not None \ + and os.path.isfile(self.options.license_file): + return open(self.options.license_file).read() + elif os.path.isfile('LICENSE'): + return open('LICENSE').read() + elif os.path.isfile('LICENCE'): + return open('LICENCE').read() + else: + return Templates['defaultlicense'] + + def _write_tpl(self, tpl, path, fname): + """ Shorthand for writing a substituted template to a file""" + print "Adding file '%s'..." % fname + open(os.path.join(path, fname), 'w').write(get_template(tpl, **self._info)) + + def run(self): + """ Go, go, go. """ + has_swig = ( + self._info['lang'] == 'cpp' + and not self._skip_subdirs['swig'] + ) + has_grc = False + if self._info['lang'] == 'cpp': + self._run_lib() + has_grc = has_swig + else: # Python + self._run_python() + if self._info['blocktype'] != 'noblock': + has_grc = True + if has_swig: + self._run_swig() + if self._add_py_qa: + self._run_python_qa() + if has_grc and not self._skip_subdirs['grc']: + self._run_grc() + + def _run_lib(self): + """ Do everything that needs doing in the subdir 'lib' and 'include'. + - add .cc and .h files + - include them into CMakeLists.txt + - check if C++ QA code is req'd + - if yes, create qa_*.{cc,h} and add them to CMakeLists.txt + """ + def _add_qa(): + " Add C++ QA files for 3.7 API " + fname_qa_h = 'qa_%s.h' % self._info['blockname'] + fname_qa_cc = 'qa_%s.cc' % self._info['blockname'] + self._write_tpl('qa_cpp', 'lib', fname_qa_cc) + self._write_tpl('qa_h', 'lib', fname_qa_h) + if not self.options.skip_cmakefiles: + try: + append_re_line_sequence(self._file['cmlib'], + '\$\{CMAKE_CURRENT_SOURCE_DIR\}/qa_%s.cc.*\n' % self._info['modname'], + ' ${CMAKE_CURRENT_SOURCE_DIR}/qa_%s.cc' % self._info['blockname']) + append_re_line_sequence(self._file['qalib'], + '#include.*\n', + '#include "%s"' % fname_qa_h) + append_re_line_sequence(self._file['qalib'], + '(addTest.*suite.*\n|new CppUnit.*TestSuite.*\n)', + ' s->addTest(gr::%s::qa_%s::suite());' % (self._info['modname'], + self._info['blockname']) + ) + except IOError: + print "Can't add C++ QA files." + def _add_qa36(): + " Add C++ QA files for pre-3.7 API (not autotools) " + fname_qa_cc = 'qa_%s.cc' % self._info['fullblockname'] + self._write_tpl('qa_cpp36', 'lib', fname_qa_cc) + if not self.options.skip_cmakefiles: + open(self._file['cmlib'], 'a').write( + str( + Cheetah.Template.Template( + Templates['qa_cmakeentry36'], + searchList={'basename': os.path.splitext(fname_qa_cc)[0], + 'filename': fname_qa_cc, + 'modname': self._info['modname'] + } + ) + ) + ) + ed = CMakeFileEditor(self._file['cmlib']) + ed.remove_double_newlines() + ed.write() + fname_cc = None + fname_h = None + if self._info['version'] == '37': + fname_h = self._info['blockname'] + '.h' + fname_cc = self._info['blockname'] + '.cc' + if self._info['blocktype'] in ('source', 'sink', 'sync', 'decimator', + 'interpolator', 'general', 'hier'): + fname_cc = self._info['blockname'] + '_impl.cc' + self._write_tpl('block_impl_h', 'lib', self._info['blockname'] + '_impl.h') + self._write_tpl('block_impl_cpp', 'lib', fname_cc) + self._write_tpl('block_def_h', self._info['includedir'], fname_h) + else: # Pre-3.7 or autotools + fname_h = self._info['fullblockname'] + '.h' + fname_cc = self._info['fullblockname'] + '.cc' + self._write_tpl('block_h36', self._info['includedir'], fname_h) + self._write_tpl('block_cpp36', 'lib', fname_cc) + if not self.options.skip_cmakefiles: + ed = CMakeFileEditor(self._file['cmlib']) + if not ed.append_value('list', fname_cc, to_ignore_start='APPEND %s_sources' % self._info['modname']): + ed.append_value('add_library', fname_cc) + ed.write() + ed = CMakeFileEditor(self._file['cminclude']) + ed.append_value('install', fname_h, to_ignore_end='DESTINATION[^()]+') + ed.write() + if self._add_cc_qa: + if self._info['version'] == '37': + _add_qa() + elif self._info['version'] == '36': + _add_qa36() + elif self._info['version'] == 'autofoo': + print "Warning: C++ QA files not supported for autotools." + + def _run_swig(self): + """ Do everything that needs doing in the subdir 'swig'. + - Edit main *.i file + """ + if self._get_mainswigfile() is None: + print 'Warning: No main swig file found.' + return + print "Editing %s..." % self._file['swig'] + mod_block_sep = '/' + if self._info['version'] == '36': + mod_block_sep = '_' + swig_block_magic_str = get_template('swig_block_magic', **self._info) + open(self._file['swig'], 'a').write(swig_block_magic_str) + include_str = '#include "%s%s%s.h"' % ( + self._info['modname'], + mod_block_sep, + self._info['blockname']) + if re.search('#include', open(self._file['swig'], 'r').read()): + append_re_line_sequence(self._file['swig'], '^#include.*\n', include_str) + else: # I.e., if the swig file is empty + oldfile = open(self._file['swig'], 'r').read() + regexp = re.compile('^%\{\n', re.MULTILINE) + oldfile = regexp.sub('%%{\n%s\n' % include_str, oldfile, count=1) + open(self._file['swig'], 'w').write(oldfile) + + def _run_python_qa(self): + """ Do everything that needs doing in the subdir 'python' to add + QA code. + - add .py files + - include in CMakeLists.txt + """ + fname_py_qa = 'qa_' + self._info['blockname'] + '.py' + self._write_tpl('qa_python', 'python', fname_py_qa) + os.chmod(os.path.join('python', fname_py_qa), 0755) + if self.options.skip_cmakefiles or CMakeFileEditor(self._file['cmpython']).check_for_glob('qa_*.py'): + return + print "Editing python/CMakeLists.txt..." + open(self._file['cmpython'], 'a').write( + 'GR_ADD_TEST(qa_%s ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/%s)\n' % \ + (self._info['blockname'], fname_py_qa)) + + def _run_python(self): + """ Do everything that needs doing in the subdir 'python' to add + a Python block. + - add .py file + - include in CMakeLists.txt + - include in __init__.py + """ + fname_py = self._info['blockname'] + '.py' + self._write_tpl('block_python', 'python', fname_py) + append_re_line_sequence(self._file['pyinit'], + '(^from.*import.*\n|# import any pure.*\n)', + 'from %s import %s' % (self._info['blockname'], self._info['blockname'])) + if self.options.skip_cmakefiles: + return + ed = CMakeFileEditor(self._file['cmpython']) + ed.append_value('GR_PYTHON_INSTALL', fname_py, to_ignore_end='DESTINATION[^()]+') + ed.write() + + def _run_grc(self): + """ Do everything that needs doing in the subdir 'grc' to add + a GRC bindings XML file. + - add .xml file + - include in CMakeLists.txt + """ + fname_grc = self._info['fullblockname'] + '.xml' + self._write_tpl('grc_xml', 'grc', fname_grc) + ed = CMakeFileEditor(self._file['cmgrc'], '\n ') + if self.options.skip_cmakefiles or ed.check_for_glob('*.xml'): + return + print "Editing grc/CMakeLists.txt..." + ed.append_value('install', fname_grc, to_ignore_end='DESTINATION[^()]+') + ed.write() + |