diff options
author | Håkon Vågsether <haakonsv@gmail.com> | 2018-12-02 21:58:11 +0100 |
---|---|---|
committer | Håkon Vågsether <haakonsv@gmail.com> | 2018-12-18 23:46:09 +0100 |
commit | c938d168478efbe59a1f54cbf3e10afe54a307b3 (patch) | |
tree | 439d5a8a34d266605ed7e7bb09003f0c5b8888e2 /grc | |
parent | 4c4cfa3af3a48121b6107ad44b33e1ab88f619eb (diff) |
Fix C++ hier block generation
Diffstat (limited to 'grc')
-rw-r--r-- | grc/blocks/pad_sink.block.yml | 4 | ||||
-rw-r--r-- | grc/blocks/pad_source.block.yml | 4 | ||||
-rw-r--r-- | grc/core/generator/FlowGraphProxy.py | 1 | ||||
-rw-r--r-- | grc/core/generator/Generator.py | 3 | ||||
-rw-r--r-- | grc/core/generator/cpp_hier_block.py | 111 | ||||
-rw-r--r-- | grc/core/generator/cpp_templates/CMakeLists.txt.mako | 3 | ||||
-rw-r--r-- | grc/core/generator/cpp_templates/flow_graph.cpp.mako | 13 | ||||
-rw-r--r-- | grc/core/generator/cpp_templates/flow_graph.hpp.mako | 97 | ||||
-rw-r--r-- | grc/core/generator/cpp_top_block.py | 24 | ||||
-rw-r--r-- | grc/core/generator/top_block.py | 3 | ||||
-rw-r--r-- | grc/core/platform.py | 3 |
11 files changed, 187 insertions, 79 deletions
diff --git a/grc/blocks/pad_sink.block.yml b/grc/blocks/pad_sink.block.yml index 88aa794cf5..b0a93ee24f 100644 --- a/grc/blocks/pad_sink.block.yml +++ b/grc/blocks/pad_sink.block.yml @@ -1,6 +1,6 @@ id: pad_sink label: Pad Sink -flags: [ python ] +flags: [ python, cpp ] parameters: - id: label @@ -15,6 +15,8 @@ parameters: option_attributes: size: [gr.sizeof_gr_complex, gr.sizeof_float, gr.sizeof_int, gr.sizeof_short, gr.sizeof_char, gr.sizeof_char, '0', '0'] + cpp_size: [sizeof(gr_complex), sizeof(float), sizeof(int), sizeof(short), + sizeof(char), sizeof(char), '0', '0'] hide: part - id: vlen label: Vec Length diff --git a/grc/blocks/pad_source.block.yml b/grc/blocks/pad_source.block.yml index ba52905447..0377e6fb1f 100644 --- a/grc/blocks/pad_source.block.yml +++ b/grc/blocks/pad_source.block.yml @@ -1,6 +1,6 @@ id: pad_source label: Pad Source -flags: [ python ] +flags: [ python, cpp ] parameters: - id: label @@ -15,6 +15,8 @@ parameters: option_attributes: size: [gr.sizeof_gr_complex, gr.sizeof_float, gr.sizeof_int, gr.sizeof_short, gr.sizeof_char, gr.sizeof_char, '0', '0'] + cpp_size: [sizeof(gr_complex), sizeof(float), sizeof(int), sizeof(short), + sizeof(char), sizeof(char), '0', '0'] hide: part - id: vlen label: Vec Length diff --git a/grc/core/generator/FlowGraphProxy.py b/grc/core/generator/FlowGraphProxy.py index 8068156c0d..bf73ee8ff0 100644 --- a/grc/core/generator/FlowGraphProxy.py +++ b/grc/core/generator/FlowGraphProxy.py @@ -74,6 +74,7 @@ class FlowGraphProxy(object): # TODO: move this in a refactored Generator 'type': str(pad.params['type'].get_evaluated()), 'vlen': str(pad.params['vlen'].get_value()), 'size': type_param.options.attributes[type_param.get_value()]['size'], + 'cpp_size': type_param.options.attributes[type_param.get_value()]['cpp_size'], 'optional': bool(pad.params['optional'].get_evaluated()), } num_ports = pad.params['num_streams'].get_evaluated() diff --git a/grc/core/generator/Generator.py b/grc/core/generator/Generator.py index 91526d8714..135f4538d5 100644 --- a/grc/core/generator/Generator.py +++ b/grc/core/generator/Generator.py @@ -25,6 +25,7 @@ from mako.template import Template from .hier_block import HierBlockGenerator, QtHierBlockGenerator from .top_block import TopBlockGenerator from .cpp_top_block import CppTopBlockGenerator +from .cpp_hier_block import CppHierBlockGenerator DATA_DIR = os.path.dirname(__file__) FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.py.mako') @@ -58,7 +59,7 @@ class Generator(object): elif self.output_language == 'cpp': if self.generate_options == 'hb': - pass + generator_cls = CppHierBlockGenerator elif self.generate_options == 'hb_qt_gui': pass else: diff --git a/grc/core/generator/cpp_hier_block.py b/grc/core/generator/cpp_hier_block.py index e81d62c549..9cee410810 100644 --- a/grc/core/generator/cpp_hier_block.py +++ b/grc/core/generator/cpp_hier_block.py @@ -2,13 +2,15 @@ import collections import os import six +import codecs -from .top_block import TopBlockGenerator +from .cpp_top_block import CppTopBlockGenerator -from .. import ParseXML, Constants +from .. import Constants +from ..io import yaml class CppHierBlockGenerator(CppTopBlockGenerator): - """Extends the top block generator to also generate a block XML file""" + """Extends the top block generator to also generate a block YML file""" def __init__(self, flow_graph, file_path): """ @@ -16,7 +18,7 @@ class CppHierBlockGenerator(CppTopBlockGenerator): Args: flow_graph: the flow graph object - file_path: where to write the py file (the xml goes into HIER_BLOCK_LIB_DIR) + file_path: where to write the py file (the yml goes into HIER_BLOCK_LIB_DIR) """ CppTopBlockGenerator.__init__(self, flow_graph, file_path) platform = flow_graph.parent @@ -27,24 +29,40 @@ class CppHierBlockGenerator(CppTopBlockGenerator): self._mode = Constants.HIER_BLOCK_FILE_MODE self.file_path = os.path.join(hier_block_lib_dir, self._flow_graph.get_option('id')) - self.file_path_xml = self.file_path + '.xml' # TODO: yml + self.file_path_yml = self.file_path + '.block.yml' def write(self): """generate output and write it to files""" - TopBlockGenerator.write(self) - ParseXML.to_file(self._build_block_n_from_flow_graph_io(), self.file_path_xml) - ParseXML.validate_dtd(self.file_path_xml, Constants.BLOCK_DTD) - try: - os.chmod(self.file_path_xml, self._mode) - except: - pass + CppTopBlockGenerator.write(self) + + data = yaml.dump(self._build_block_n_from_flow_graph_io()) + + replace = [ + ('parameters:', '\nparameters:'), + ('inputs:', '\ninputs:'), + ('outputs:', '\noutputs:'), + ('asserts:', '\nasserts:'), + ('\ntemplates:', '\n\ntemplates:'), + ('cpp_templates:', '\ncpp_templates:'), + ('documentation:', '\ndocumentation:'), + ('file_format:', '\nfile_format:'), + ] + for r in replace: + data = data.replace(*r) + + with codecs.open(self.file_path_yml, 'w', encoding='utf-8') as fp: + fp.write(data) + + # Windows only supports S_IREAD and S_IWRITE, other flags are ignored + os.chmod(self.file_path_yml, self._mode) + def _build_block_n_from_flow_graph_io(self): """ - Generate a block XML nested data from the flow graph IO + Generate a block YML nested data from the flow graph IO Returns: - a xml node tree + a yml node tree """ # Extract info from the flow graph block_id = self._flow_graph.get_option('id') @@ -63,6 +81,7 @@ class CppHierBlockGenerator(CppTopBlockGenerator): self._flow_graph.get_option('id').replace('_', ' ').title() ) data['category'] = self._flow_graph.get_option('category') + data['flags'] = [ 'cpp' ] # Parameters data['parameters'] = [] @@ -70,8 +89,8 @@ class CppHierBlockGenerator(CppTopBlockGenerator): p = collections.OrderedDict() p['id'] = param_block.name p['label'] = param_block.params['label'].get_value() or param_block.name - p['dtype'] = 'raw' - p['value'] = param_block.params['value'].get_value() + p['dtype'] = param_block.params['value'].dtype + p['default'] = param_block.params['value'].get_value() p['hide'] = param_block.params['hide'].get_value() data['param'].append(p) @@ -82,7 +101,7 @@ class CppHierBlockGenerator(CppTopBlockGenerator): p = collections.OrderedDict() if port.domain == Constants.GR_MESSAGE_DOMAIN: p['id'] = port.id - #p['label'] = port.label + p['label'] = port.parent.params['label'].value if port.domain != Constants.DEFAULT_DOMAIN: p['domain'] = port.domain p['dtype'] = port.dtype @@ -106,22 +125,46 @@ class CppHierBlockGenerator(CppTopBlockGenerator): ) else: t['make'] = '{cls}()'.format(cls=block_id) + # Callback data - t['callback'] = [ + t['callbacks'] = [ 'set_{key}(${key})'.format(key=param_block.name) for param_block in parameters ] + t_cpp = data['cpp_templates'] = collections.OrderedDict() + + t_cpp['includes'] = [] + t_cpp['includes'].append('#include "{id}/{id}.hpp"'.format(id=self._flow_graph.get_option('id'))) + + # Make data + if parameters: + t_cpp['make'] = '{cls}(\n {kwargs},\n)'.format( + cls=block_id, + kwargs=',\n '.join( + '{key}=${key}'.format(key=param.name) for param in parameters + ), + ) + else: + t_cpp['make'] = 'this->${{id}} = {cls}_sptr(make_{cls}());'.format(cls=block_id) + t_cpp['declarations'] = '{cls}_sptr ${{id}};'.format(cls=block_id) + + # Callback data + t_cpp['callbacks'] = [ + 'set_{key}(${key})'.format(key=param_block.name) for param_block in parameters + ] # Documentation - data['doc'] = "\n".join(field for field in ( + data['documentation'] = "\n".join(field for field in ( self._flow_graph.get_option('author'), self._flow_graph.get_option('description'), self.file_path ) if field) + data['grc_source'] = str(self._flow_graph.grc_file_path) - n = {'block': data} - return n + data['file_format'] = 1 + + return data class CppQtHierBlockGenerator(CppHierBlockGenerator): @@ -163,34 +206,8 @@ def get_hier_block_io(flow_graph, direction, domain=None): """ pads = flow_graph.get_pad_sources() if direction == 'inputs' else flow_graph.get_pad_sinks() - ports = [] for pad in pads: for port in (pad.sources if direction == 'inputs' else pad.sinks): if domain and port.domain != domain: continue yield port - - type_param = pad.params['type'] - master = { - 'label': str(pad.params['label'].get_evaluated()), - 'type': str(pad.params['type'].get_evaluated()), - 'vlen': str(pad.params['vlen'].get_value()), - 'size': type_param.options.attributes[type_param.get_value()]['size'], - 'optional': bool(pad.params['optional'].get_evaluated()), - } - - if domain and pad: - num_ports = pad.params['num_streams'].get_evaluated() - if num_ports <= 1: - yield master - else: - for i in range(num_ports): - clone = master.copy() - clone['label'] += str(i) - ports.append(clone) - else: - ports.append(master) - - if domain is not None: - ports = [p for p in ports if p.domain == domain] - yield ports # TODO: Not sure this fix is correct diff --git a/grc/core/generator/cpp_templates/CMakeLists.txt.mako b/grc/core/generator/cpp_templates/CMakeLists.txt.mako index de91f1c6fa..783ba71213 100644 --- a/grc/core/generator/cpp_templates/CMakeLists.txt.mako +++ b/grc/core/generator/cpp_templates/CMakeLists.txt.mako @@ -16,7 +16,7 @@ class_name = flow_graph.get_option('id') cmake_opt_list = flow_graph.get_option('cmake_opt').split(";") %>\ -cmake_minimum_required(VERSION 3.8) # Which version? +cmake_minimum_required(VERSION 3.8) % if generate_options == 'qt_gui': find_package(Qt5Widgets REQUIRED) @@ -28,6 +28,7 @@ include_directories( % if generate_options == 'qt_gui': ${'$'}{Qt5Widgets_INCLUDES} % endif + $ENV{HOME}/.grc_gnuradio ) % if generate_options == 'qt_gui': diff --git a/grc/core/generator/cpp_templates/flow_graph.cpp.mako b/grc/core/generator/cpp_templates/flow_graph.cpp.mako index 51291fb42d..c03e0c1cc2 100644 --- a/grc/core/generator/cpp_templates/flow_graph.cpp.mako +++ b/grc/core/generator/cpp_templates/flow_graph.cpp.mako @@ -29,15 +29,6 @@ param_str_without_types = ", ".join(param.name for param in parameters) % if generate_options == 'no_gui': ${class_name}::${class_name} (${param_str}) { -% elif generate_options.startswith('hb'): -## TODO: make_io_sig -${class_name}::${class_name} (${param_str}) : hier_block2("${title}") { -% for pad in flow_graph.get_hier_block_message_io('in'): - message_port_register_hier_in("${pad['label']}") -% endfor -% for pad in flow_graph.get_hier_block_message_io('out'): - message_port_register_hier_out("${pad['label']}") -% endfor % elif generate_options == 'qt_gui': ${class_name}::${class_name} (${param_str}) : QWidget() { this->setWindowTitle("${title}"); @@ -63,9 +54,9 @@ ${class_name}::${class_name} (${param_str}) : QWidget() { ## self._lock = threading.RLock() % endif -% if not generate_options.startswith('hb'): + this->tb = gr::make_top_block("${title}"); -% endif + % if blocks: // Blocks: diff --git a/grc/core/generator/cpp_templates/flow_graph.hpp.mako b/grc/core/generator/cpp_templates/flow_graph.hpp.mako index 6aeccd4ffe..7ae119c44e 100644 --- a/grc/core/generator/cpp_templates/flow_graph.hpp.mako +++ b/grc/core/generator/cpp_templates/flow_graph.hpp.mako @@ -1,4 +1,5 @@ <%def name="indent(code)">${ ' ' + '\n '.join(str(code).splitlines()) }</%def>\ +<%def name="doubleindent(code)">${ '\n '.join(str(code).splitlines()) }</%def>\ #ifndef ${flow_graph.get_option('id').upper()}_HPP #define ${flow_graph.get_option('id').upper()}_HPP /******************** @@ -41,6 +42,11 @@ class_name = flow_graph.get_option('id') + ('_' if flow_graph.get_option('id') = param_str = ", ".join((param.vtype + " " + param.name) for param in parameters) %>\ +% if generate_options.startswith('hb'): +class ${class_name}; +typedef boost::shared_ptr<${class_name}> ${class_name}_sptr; +${class_name}_sptr make_${class_name}(); +% endif % if generate_options == 'no_gui': class ${class_name} { @@ -84,7 +90,7 @@ ${indent(declarations)} public: % if not generate_options.startswith('hb'): - gr::top_block_sptr tb; + top_block_sptr tb; % endif ${class_name}(${param_str}); ~${class_name}(); @@ -95,4 +101,93 @@ public: % endfor }; + + +% if generate_options.startswith('hb'): +<% in_sigs = flow_graph.get_hier_block_stream_io('in') %> +<% out_sigs = flow_graph.get_hier_block_stream_io('out') %> + +<%def name="make_io_sig(io_sigs)">\ + <% size_strs = [ '%s*%s'%(io_sig['cpp_size'], io_sig['vlen']) for io_sig in io_sigs] %>\ + % if len(io_sigs) == 0: + gr::io_signature::make(0, 0, 0)\ + % elif len(io_sigs) == 1: + gr::io_signature::make(1, 1, ${size_strs[0]})\ + % else: + gr::io_signaturev(${len(io_sigs)}, ${len(io_sigs)}, [${', '.join(size_strs)}])\ + % endif +</%def>\ + +${class_name}::${class_name} (${param_str}) : hier_block2("${title}", + ${make_io_sig(in_sigs)}, + ${make_io_sig(out_sigs)} + ) { +% for pad in flow_graph.get_hier_block_message_io('in'): + message_port_register_hier_in("${pad['label']}") +% endfor +% for pad in flow_graph.get_hier_block_message_io('out'): + message_port_register_hier_out("${pad['label']}") +% endfor + +% if flow_graph.get_option('thread_safe_setters'): +## self._lock = threading.RLock() +% endif + +% if blocks: +// Blocks: +% for blk, blk_make, declarations in blocks: + { + ${doubleindent(blk_make)} +## % if 'alias' in blk.params and blk.params['alias'].get_evaluated(): +## ${blk.name}.set_block_alias("${blk.params['alias'].get_evaluated()}") +## % endif +## % if 'affinity' in blk.params and blk.params['affinity'].get_evaluated(): +## ${blk.name}.set_processor_affinity("${blk.params['affinity'].get_evaluated()}") +## % endif +## % if len(blk.sources) > 0 and 'minoutbuf' in blk.params and int(blk.params['minoutbuf'].get_evaluated()) > 0: +## ${blk.name}.set_min_output_buffer(${blk.params['minoutbuf'].get_evaluated()}) +## % endif +## % if len(blk.sources) > 0 and 'maxoutbuf' in blk.params and int(blk.params['maxoutbuf'].get_evaluated()) > 0: +## ${blk.name}.set_max_output_buffer(${blk.params['maxoutbuf'].get_evaluated()}) +## % endif + } +% endfor +% endif + +% if connections: +// Connections: +% for connection in connections: + ${connection.rstrip()}; +% endfor +% endif +} +${class_name}::~${class_name} () { +} + +// Callbacks: +% for var in parameters + variables: +${var.vtype} ${class_name}::get_${var.name} () const { + return this->${var.name}; +} + +void ${class_name}::set_${var.name} (${var.vtype} ${var.name}) { +% if flow_graph.get_option('thread_safe_setters'): + ## with self._lock: + return; +% else: + this->${var.name} = ${var.name}; + % for callback in callbacks[var.name]: + ${callback}; + % endfor +% endif +} + +% endfor +${class_name}_sptr +make_${class_name}() +{ + return gnuradio::get_initial_sptr(new ${class_name}()); +} +% endif #endif + diff --git a/grc/core/generator/cpp_top_block.py b/grc/core/generator/cpp_top_block.py index aeae2fbb23..9622ad93ce 100644 --- a/grc/core/generator/cpp_top_block.py +++ b/grc/core/generator/cpp_top_block.py @@ -72,22 +72,26 @@ class CppTopBlockGenerator(TopBlockGenerator): 'generated_time': time.ctime(), } - if not os.path.exists(os.path.join(self.file_path, 'build')): - os.makedirs(os.path.join(self.file_path, 'build')) + if not os.path.exists(self.file_path): + os.makedirs(self.file_path) for filename, data in self._build_cpp_header_code_from_template(): with codecs.open(filename, 'w', encoding='utf-8') as fp: fp.write(data) - for filename, data in self._build_cpp_source_code_from_template(): - with codecs.open(filename, 'w', encoding='utf-8') as fp: - fp.write(data) + if not self._generate_options.startswith('hb'): + if not os.path.exists(os.path.join(self.file_path, 'build')): + os.makedirs(os.path.join(self.file_path, 'build')) - if fg.get_option('gen_cmake') == 'On': - for filename, data in self._build_cmake_code_from_template(): + for filename, data in self._build_cpp_source_code_from_template(): with codecs.open(filename, 'w', encoding='utf-8') as fp: fp.write(data) + if fg.get_option('gen_cmake') == 'On': + for filename, data in self._build_cmake_code_from_template(): + with codecs.open(filename, 'w', encoding='utf-8') as fp: + fp.write(data) + def _build_cpp_source_code_from_template(self): """ Convert the flow graph to a C++ source file. @@ -289,9 +293,8 @@ class CppTopBlockGenerator(TopBlockGenerator): for key, text in fg.parent_platform.cpp_connection_templates.items()} def make_port_sig(port): - # TODO: make sense of this if port.parent.key in ('pad_source', 'pad_sink'): - block = 'self' + block = 'self()' key = fg.get_pad_port_global_key(port) else: block = 'this->' + port.parent_block.name @@ -351,7 +354,8 @@ class CppTopBlockGenerator(TopBlockGenerator): for con in sorted(connections, key=by_domain_and_blocks): template = templates[con.type] code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) - code = 'this->tb->' + code + if not self._generate_options.startswith('hb'): + code = 'this->tb->' + code rendered.append(code) return rendered diff --git a/grc/core/generator/top_block.py b/grc/core/generator/top_block.py index f2b485d641..cab1bf71cb 100644 --- a/grc/core/generator/top_block.py +++ b/grc/core/generator/top_block.py @@ -266,9 +266,6 @@ class TopBlockGenerator(object): # Remove the virtual connection connections.remove(connection) - for connection in virtual_sink_connections: - connections.remove(connection) - # Bypassing blocks: Need to find all the enabled connections for the block using # the *connections* object rather than get_connections(). Create new connections # that bypass the selected block and remove the existing ones. This allows adjacent diff --git a/grc/core/platform.py b/grc/core/platform.py index 5449a117ad..88bc941495 100644 --- a/grc/core/platform.py +++ b/grc/core/platform.py @@ -366,10 +366,7 @@ class Platform(Element): ('metadata:\n', '\nmetadata:\n'), ] for r in replace: - # the purpose of using re.sub() here is to avoid corrupting the .grc file - out = re.sub(r'(nconnections)(?<!\\)', 'NCONNECTIONS', out) out = out.replace(*r) - out = re.sub(r'(NCONNECTIONS)(?<!\\)', 'nconnections', out) with open(filename, 'w', encoding='utf-8') as fp: fp.write(out) |