summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gr-utils/python/utils/CMakeLists.txt1
-rwxr-xr-xgr-utils/python/utils/grcc89
-rwxr-xr-xgrc/compiler.py76
-rw-r--r--grc/core/Config.py4
-rw-r--r--grc/core/FlowGraph.py18
-rw-r--r--grc/core/Platform.py23
-rw-r--r--grc/core/utils/shlex.py47
-rw-r--r--grc/gui/Executor.py41
-rw-r--r--grc/scripts/CMakeLists.txt2
-rwxr-xr-xgrc/scripts/gnuradio-companion34
-rwxr-xr-xgrc/scripts/grcc64
-rw-r--r--grc/tests/resources/test_compiler.grc253
-rw-r--r--grc/tests/test_compiler.py38
13 files changed, 532 insertions, 158 deletions
diff --git a/gr-utils/python/utils/CMakeLists.txt b/gr-utils/python/utils/CMakeLists.txt
index 46a21c1508..3ce335c4ed 100644
--- a/gr-utils/python/utils/CMakeLists.txt
+++ b/gr-utils/python/utils/CMakeLists.txt
@@ -49,6 +49,5 @@ GR_PYTHON_INSTALL(
gr_plot_short
gr_plot_qt
gr_read_file_metadata
- grcc
DESTINATION ${GR_RUNTIME_DIR}
)
diff --git a/gr-utils/python/utils/grcc b/gr-utils/python/utils/grcc
deleted file mode 100755
index e93802f051..0000000000
--- a/gr-utils/python/utils/grcc
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2012 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.
-#
-
-import os
-import sys
-from argparse import ArgumentParser
-import warnings
-warnings.simplefilter('ignore')
-
-from gnuradio import gr
-
-try:
- from grc.core.Platform import Platform
-except ImportError:
- from gnuradio.grc.core.Platform import Platform
-
-
-class GRCC:
- def __init__(self, grcfile, out_dir):
- self.out_dir = out_dir
- self.platform = Platform(
- prefs_file=gr.prefs(),
- version=gr.version(),
- version_parts=(gr.major_version(), gr.api_version(), gr.minor_version())
- )
- data = self.platform.parse_flow_graph(grcfile)
-
- self.fg = self.platform.get_new_flow_graph()
- self.fg.import_data(data)
- self.fg.grc_file_path = os.path.abspath(grcfile)
- self.fg.validate()
-
- if not self.fg.is_valid():
- raise StandardError("\n\n".join(
- ["Validation failed:"] + self.fg.get_error_messages()
- ))
-
- self.gen = self.platform.Generator(self.fg, out_dir)
- self.gen.write()
-
- def exec_program(self):
- progname = self.fg.get_option('id')
- os.system("{0}/{1}.py".format(self.out_dir, progname))
-
-
-def main():
- description = "Compiles a GRC file (.grc) into a GNU Radio Python program. The program is stored in ~/.grc_gnuradio by default, but this location can be changed with the -d option."
-
- parser = ArgumentParser(description=description)
- parser.add_argument("-d", "--directory",
- default='{0}/.grc_gnuradio/'.format(os.environ["HOME"]),
- help="Specify the directory to output the compile program [default=%(default)s]")
- parser.add_argument("-e", "--execute", action="store_true", default=False,
- help="Run the program after compiling [default=%(default)s]")
- parser.add_argument('grc_file', metavar="GRC_FILE", help=".grc file to compile")
- args = parser.parse_args()
-
- try:
- g = GRCC(args.grc_file, args.directory + "/")
- except Exception as e:
- sys.stderr.write(str(e) + "\n")
- sys.stderr.write("Error during file compilation.\n")
- sys.exit(1)
-
- if args.execute:
- g.exec_program()
-
-
-if __name__ == "__main__":
- main()
diff --git a/grc/compiler.py b/grc/compiler.py
new file mode 100755
index 0000000000..0cda0d946d
--- /dev/null
+++ b/grc/compiler.py
@@ -0,0 +1,76 @@
+# Copyright 2016 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.
+
+from __future__ import print_function, absolute_import
+
+import argparse
+import os
+import subprocess
+
+from gnuradio import gr
+
+from .core import Messages
+from .core.Platform import Platform
+
+
+def argument_parser():
+ parser = argparse.ArgumentParser(description=(
+ "Compile a GRC file (.grc) into a GNU Radio Python program and run it."
+ ))
+ parser.add_argument("-o", "--output", metavar='DIR', default='.',
+ help="Output directory for compiled program [default=%(default)s]")
+ parser.add_argument("-u", "--user-lib-dir", action='store_true', default=False,
+ help="Output to default hier_block library (overwrites -o)")
+ parser.add_argument("-r", "--run", action="store_true", default=False,
+ help="Run the program after compiling [default=%(default)s]")
+ parser.add_argument(metavar="GRC_FILE", dest='grc_files', nargs='+',
+ help=".grc file to compile")
+ return parser
+
+
+def main(args=None):
+ args = args or argument_parser().parse_args()
+
+ platform = Platform(
+ name='GNU Radio Companion Compiler',
+ prefs_file=gr.prefs(),
+ version=gr.version(),
+ version_parts=(gr.major_version(), gr.api_version(), gr.minor_version())
+ )
+ out_dir = args.output if not args.user_lib_dir else platform.config.hier_block_lib_dir
+ if os.path.exists(out_dir):
+ pass # all is well
+ elif args.save_to_lib:
+ os.mkdir(out_dir) # create missing hier_block lib directory
+ else:
+ exit('Error: Invalid output directory')
+
+ Messages.send_init(platform)
+ flow_graph = file_path = None
+ for grc_file in args.grc_files:
+ os.path.exists(grc_file) or exit('Error: missing ' + grc_file)
+ Messages.send('\n')
+
+ flow_graph, file_path = platform.load_and_generate_flow_graph(
+ os.path.abspath(grc_file), os.path.abspath(out_dir))
+ if not file_path:
+ exit('Compilation error')
+ if file_path and args.run:
+ run_command_args = flow_graph.get_run_command(file_path, split=True)
+ subprocess.call(run_command_args)
diff --git a/grc/core/Config.py b/grc/core/Config.py
index 78ff344998..744ad06ba9 100644
--- a/grc/core/Config.py
+++ b/grc/core/Config.py
@@ -32,10 +32,12 @@ class Config(object):
hier_block_lib_dir = os.environ.get('GRC_HIER_PATH', Constants.DEFAULT_HIER_BLOCK_LIB_DIR)
- def __init__(self, prefs_file, version, version_parts=None):
+ def __init__(self, prefs_file, version, version_parts=None, name=None):
self.prefs = prefs_file
self.version = version
self.version_parts = version_parts or version[1:].split('-', 1)[0].split('.')[:3]
+ if name:
+ self.name = name
@property
def block_paths(self):
diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py
index 949eecaa71..ecae11cf1a 100644
--- a/grc/core/FlowGraph.py
+++ b/grc/core/FlowGraph.py
@@ -16,16 +16,16 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import imp
-import time
from itertools import ifilter, chain
from operator import methodcaller, attrgetter
-
import re
+import sys
+import time
from . import Messages
from .Constants import FLOW_GRAPH_FILE_FORMAT_VERSION
from .Element import Element
-from .utils import odict, expr_utils
+from .utils import odict, expr_utils, shlex
_parameter_matcher = re.compile('^(parameter)$')
_monitors_searcher = re.compile('(ctrlport_monitor)')
@@ -186,6 +186,16 @@ class FlowGraph(Element):
"""
return self._options_block.get_param(key).get_evaluated()
+ def get_run_command(self, file_path, split=False):
+ run_command = self.get_option('run_command')
+ try:
+ run_command = run_command.format(
+ python=shlex.quote(sys.executable),
+ filename=shlex.quote(file_path))
+ return shlex.split(run_command) if split else run_command
+ except Exception as e:
+ raise ValueError("Can't parse run command {!r}: {}".format(run_command, e))
+
##############################################
# Access Elements
##############################################
@@ -410,7 +420,7 @@ class FlowGraph(Element):
cwd=self.grc_file_path
)
if file_path: # grc file found. load and get block
- self.platform.load_and_generate_flow_graph(file_path)
+ self.platform.load_and_generate_flow_graph(file_path, hier_only=True)
block = self.new_block(key) # can be None
if not block: # looks like this block key cannot be found
diff --git a/grc/core/Platform.py b/grc/core/Platform.py
index 0dc6eb055a..b73dade2e8 100644
--- a/grc/core/Platform.py
+++ b/grc/core/Platform.py
@@ -93,42 +93,43 @@ class Platform(Element):
if os.path.exists(os.path.normpath(file_path)):
return file_path
- def load_and_generate_flow_graph(self, file_path):
+ def load_and_generate_flow_graph(self, file_path, out_path=None, hier_only=False):
"""Loads a flow graph from file and generates it"""
Messages.set_indent(len(self._auto_hier_block_generate_chain))
- Messages.send('>>> Loading: %r\n' % file_path)
+ Messages.send('>>> Loading: {}\n'.format(file_path))
if file_path in self._auto_hier_block_generate_chain:
Messages.send(' >>> Warning: cyclic hier_block dependency\n')
- return False
+ return None, None
self._auto_hier_block_generate_chain.add(file_path)
try:
flow_graph = self.get_new_flow_graph()
flow_graph.grc_file_path = file_path
- # Other, nested higiter_blocks might be auto-loaded here
+ # Other, nested hier_blocks might be auto-loaded here
flow_graph.import_data(self.parse_flow_graph(file_path))
flow_graph.rewrite()
flow_graph.validate()
if not flow_graph.is_valid():
raise Exception('Flowgraph invalid')
- if not flow_graph.get_option('generate_options').startswith('hb'):
+ if hier_only and not flow_graph.get_option('generate_options').startswith('hb'):
raise Exception('Not a hier block')
except Exception as e:
Messages.send('>>> Load Error: {}: {}\n'.format(file_path, str(e)))
- return False
+ return None, None
finally:
self._auto_hier_block_generate_chain.discard(file_path)
Messages.set_indent(len(self._auto_hier_block_generate_chain))
try:
- Messages.send('>>> Generating: {}\n'.format(file_path))
- generator = self.Generator(flow_graph, file_path)
+ generator = self.Generator(flow_graph, out_path or file_path)
+ Messages.send('>>> Generating: {}\n'.format(generator.file_path))
generator.write()
except Exception as e:
Messages.send('>>> Generate Error: {}: {}\n'.format(file_path, str(e)))
- return False
+ return None, None
- self.load_block_xml(generator.get_file_path_xml())
- return True
+ if flow_graph.get_option('generate_options').startswith('hb'):
+ self.load_block_xml(generator.get_file_path_xml())
+ return flow_graph, generator.file_path
def build_block_library(self):
"""load the blocks and block tree from the search paths"""
diff --git a/grc/core/utils/shlex.py b/grc/core/utils/shlex.py
new file mode 100644
index 0000000000..6b620fa396
--- /dev/null
+++ b/grc/core/utils/shlex.py
@@ -0,0 +1,47 @@
+# Copyright 2016 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.
+
+from __future__ import absolute_import
+
+import re
+import shlex
+
+# back port from python3
+
+_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
+
+
+def _shlex_quote(s):
+ """Return a shell-escaped version of the string *s*."""
+ if not s:
+ return "''"
+ if _find_unsafe(s) is None:
+ return s
+
+ # use single quotes, and put single quotes into double quotes
+ # the string $'b is then quoted as '$'"'"'b'
+ return "'" + s.replace("'", "'\"'\"'") + "'"
+
+
+if not hasattr(shlex, 'quote'):
+ quote = _shlex_quote
+else:
+ quote = shlex.quote
+
+split = shlex.split
diff --git a/grc/gui/Executor.py b/grc/gui/Executor.py
index bf9eecb9a8..f5a75ab55b 100644
--- a/grc/gui/Executor.py
+++ b/grc/gui/Executor.py
@@ -15,15 +15,14 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-import gobject
-import os
-import threading
-import shlex
import subprocess
-import sys
-import re
+import threading
from distutils.spawn import find_executable
+import gobject
+import os
+
+from ..core.utils import shlex
from ..core import Messages
@@ -40,6 +39,7 @@ class ExecFlowGraphThread(threading.Thread):
threading.Thread.__init__(self)
self.page = flow_graph_page # store page and dont use main window calls in run
+ self.flow_graph = self.page.get_flow_graph()
self.xterm_executable = xterm_executable
self.update_callback = callback
@@ -56,16 +56,9 @@ class ExecFlowGraphThread(threading.Thread):
"""
Execute this python flow graph.
"""
- run_command = self.page.get_flow_graph().get_option('run_command')
generator = self.page.get_generator()
-
- try:
- run_command = run_command.format(
- python=shlex_quote(sys.executable),
- filename=shlex_quote(generator.file_path))
- run_command_args = shlex.split(run_command)
- except Exception as e:
- raise ValueError("Can't parse run command {!r}: {}".format(run_command, e))
+ run_command = self.flow_graph.get_run_command(generator.file_path)
+ run_command_args = shlex.split(run_command)
# When in no gui mode on linux, use a graphical terminal (looks nice)
xterm_executable = find_executable(self.xterm_executable)
@@ -101,21 +94,3 @@ class ExecFlowGraphThread(threading.Thread):
Messages.send_end_exec(self.process.returncode)
self.page.set_proc(None)
self.update_callback()
-
-
-###########################################################
-# back-port from python3
-###########################################################
-_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
-
-
-def shlex_quote(s):
- """Return a shell-escaped version of the string *s*."""
- if not s:
- return "''"
- if _find_unsafe(s) is None:
- return s
-
- # use single quotes, and put single quotes into double quotes
- # the string $'b is then quoted as '$'"'"'b'
- return "'" + s.replace("'", "'\"'\"'") + "'"
diff --git a/grc/scripts/CMakeLists.txt b/grc/scripts/CMakeLists.txt
index 9751952118..20366e0212 100644
--- a/grc/scripts/CMakeLists.txt
+++ b/grc/scripts/CMakeLists.txt
@@ -19,7 +19,7 @@
########################################################################
GR_PYTHON_INSTALL(
- PROGRAMS gnuradio-companion
+ PROGRAMS gnuradio-companion grcc
DESTINATION ${GR_RUNTIME_DIR}
)
diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion
index 34bb0bf110..bacbbe2334 100755
--- a/grc/scripts/gnuradio-companion
+++ b/grc/scripts/gnuradio-companion
@@ -1,22 +1,20 @@
#!/usr/bin/env python
-"""
-Copyright 2016 Free Software Foundation, Inc.
-This file is part of GNU Radio
-
-GNU Radio Companion 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 2
-of the License, or (at your option) any later version.
-
-GNU Radio Companion 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 this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-"""
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion 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 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import os
import sys
diff --git a/grc/scripts/grcc b/grc/scripts/grcc
new file mode 100755
index 0000000000..c3a53a91a6
--- /dev/null
+++ b/grc/scripts/grcc
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# Copyright 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion 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 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+import os
+import sys
+
+
+GR_IMPORT_ERROR_MESSAGE = """\
+Cannot import gnuradio.
+
+Is the model path environment variable set correctly?
+ All OS: PYTHONPATH
+
+Is the library path environment variable set correctly?
+ Linux: LD_LIBRARY_PATH
+ Windows: PATH
+ MacOSX: DYLD_LIBRARY_PATH
+"""
+
+
+def die(error, message):
+ msg = "{0}\n\n({1})".format(message, error)
+ exit(type(error).__name__ + '\n\n' + msg)
+
+
+def check_gnuradio_import():
+ try:
+ from gnuradio import gr
+ except ImportError as err:
+ die(err, GR_IMPORT_ERROR_MESSAGE)
+
+
+def run_main():
+ script_path = os.path.dirname(os.path.abspath(__file__))
+ source_tree_subpath = "/grc/scripts"
+
+ if not script_path.endswith(source_tree_subpath):
+ # run the installed version
+ from gnuradio.grc.compiler import main
+ else:
+ print("Running from source tree")
+ sys.path.insert(1, script_path[:-len(source_tree_subpath)])
+ from grc.compiler import main
+ exit(main())
+
+
+if __name__ == '__main__':
+ check_gnuradio_import()
+ run_main()
diff --git a/grc/tests/resources/test_compiler.grc b/grc/tests/resources/test_compiler.grc
new file mode 100644
index 0000000000..cc56acedca
--- /dev/null
+++ b/grc/tests/resources/test_compiler.grc
@@ -0,0 +1,253 @@
+<?xml version='1.0' encoding='utf-8'?>
+<?grc format='1' created='3.7.11'?>
+<flow_graph>
+ <timestamp>Thu Sep 15 12:56:40 2016</timestamp>
+ <block>
+ <key>options</key>
+ <param>
+ <key>author</key>
+ <value></value>
+ </param>
+ <param>
+ <key>window_size</key>
+ <value></value>
+ </param>
+ <param>
+ <key>category</key>
+ <value>[GRC Hier Blocks]</value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>description</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(8, 8)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>generate_options</key>
+ <value>no_gui</value>
+ </param>
+ <param>
+ <key>hier_block_src_path</key>
+ <value>.:</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>top_block</value>
+ </param>
+ <param>
+ <key>max_nouts</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>qt_qss_theme</key>
+ <value></value>
+ </param>
+ <param>
+ <key>realtime_scheduling</key>
+ <value></value>
+ </param>
+ <param>
+ <key>run_command</key>
+ <value>{python} -u {filename}</value>
+ </param>
+ <param>
+ <key>run_options</key>
+ <value>run</value>
+ </param>
+ <param>
+ <key>run</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>thread_safe_setters</key>
+ <value></value>
+ </param>
+ <param>
+ <key>title</key>
+ <value></value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_add_const_vxx</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>const</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(360, 28)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_add_const_vxx_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>complex</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_null_sink</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>bus_conns</key>
+ <value>[[0,],]</value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(504, 32)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_null_sink_0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>complex</value>
+ </param>
+ <param>
+ <key>num_inputs</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ </block>
+ <block>
+ <key>blocks_vector_source_x</key>
+ <param>
+ <key>alias</key>
+ <value></value>
+ </param>
+ <param>
+ <key>comment</key>
+ <value></value>
+ </param>
+ <param>
+ <key>affinity</key>
+ <value></value>
+ </param>
+ <param>
+ <key>_enabled</key>
+ <value>True</value>
+ </param>
+ <param>
+ <key>_coordinate</key>
+ <value>(208, 12)</value>
+ </param>
+ <param>
+ <key>_rotation</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>id</key>
+ <value>blocks_vector_source_x_0</value>
+ </param>
+ <param>
+ <key>maxoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>minoutbuf</key>
+ <value>0</value>
+ </param>
+ <param>
+ <key>type</key>
+ <value>complex</value>
+ </param>
+ <param>
+ <key>repeat</key>
+ <value>False</value>
+ </param>
+ <param>
+ <key>tags</key>
+ <value>[]</value>
+ </param>
+ <param>
+ <key>vlen</key>
+ <value>1</value>
+ </param>
+ <param>
+ <key>vector</key>
+ <value>(0, 0, 0)</value>
+ </param>
+ </block>
+ <connection>
+ <source_block_id>blocks_add_const_vxx_0</source_block_id>
+ <sink_block_id>blocks_null_sink_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+ <connection>
+ <source_block_id>blocks_vector_source_x_0</source_block_id>
+ <sink_block_id>blocks_add_const_vxx_0</sink_block_id>
+ <source_key>0</source_key>
+ <sink_key>0</sink_key>
+ </connection>
+</flow_graph>
diff --git a/grc/tests/test_compiler.py b/grc/tests/test_compiler.py
new file mode 100644
index 0000000000..27b5670871
--- /dev/null
+++ b/grc/tests/test_compiler.py
@@ -0,0 +1,38 @@
+# Copyright 2016 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.
+
+from argparse import Namespace
+from os import path
+import tempfile
+
+from grc.compiler import main
+
+
+def test_compiler(capsys):
+ args = Namespace(
+ output=tempfile.gettempdir(),
+ user_lib_dir=False,
+ grc_files=[path.join(path.dirname(__file__), 'resources', 'test_compiler.grc')],
+ run=True
+ )
+
+ main(args)
+
+ out, err = capsys.readouterr()
+ assert not err