Extending the Run Manager
Running projects is managed by the Ide.RunManager
.
You can provide special “run handlers” to alter how the applications are executed.
For example, the Sysprof-based profiler uses a custom run handler to ensure that the profiler can monitor the process. Additionally, Builder’s GDB debugger backend will alter the arguments so that GDB is used to launch the program.
Generally, hooking into the run manager involves adding a menu entry to the “Run Button” and a callback to process the launch request.
Let’s take a look at a practical example using the Valgrind plugin.
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="run-menu">
<section id="run-menu-section">
<item>
<attribute name="id">valgrind-run-handler</attribute>
<attribute name="after">default-run-handler</attribute>
<attribute name="action">run-manager.run-with-handler</attribute>
<attribute name="target">valgrind</attribute>
<attribute name="label" translatable="yes">Run with Valgrind</attribute>
<attribute name="verb-icon-name">system-run-symbolic</attribute>
<attribute name="accel"><Control>F10</attribute>
</item>
</section>
</menu>
</interface>
For more information on embedding resources with Python-based plugins, see creating embedded GResources.
Now register a run handler to handle the launch request.
from gi.repository import Ide
from gi.repository import GLib
from gi.repository import GObject
_ = Ide.gettext
class ValgrindWorkbenchAddin(GObject.Object, Ide.WorkbenchAddin):
workbench = None
has_handler = False
def do_load(self, workbench):
self.workbench = workbench
build_manager = self.workbench.get_context().get_build_manager()
build_manager.connect('notify::pipeline', self.notify_pipeline)
self.notify_pipeline(build_manager, None)
def notify_pipeline(self, build_manager, pspec):
run_manager = self.workbench.get_context().get_run_manager()
# When the pipeline changes, we need to check to see if we can find
# valgrind inside the runtime environment.
pipeline = build_manager.get_pipeline()
if pipeline:
runtime = pipeline.get_config().get_runtime()
if runtime and runtime.contains_program_in_path('valgrind'):
if not self.has_handler:
run_manager.add_handler('valgrind', _('Run with Valgrind'), 'system-run-symbolic', '<primary>F10', self.valgrind_handler)
self.has_handler = True
return
if self.has_handler:
run_manager.remove_handler('valgrind')
def do_unload(self, workbench):
if self.has_handler:
run_manager = self.workbench.get_context().get_run_manager()
run_manager.remove_handler('valgrind')
self.workbench = None
def valgrind_handler(self, run_manager, runner):
# We want to run with valgrind --log-fd=N so that we get the valgrind
# output redirected to our temp file. Then when the process exits, we
# we will open the temp file in the builder editor.
source_fd, name = GLib.file_open_tmp('gnome-builder-valgrind-XXXXXX.txt')
map_fd = runner.take_fd(source_fd, -1)
runner.prepend_argv('--track-origins=yes')
runner.prepend_argv('--log-fd='+str(map_fd))
runner.prepend_argv('valgrind')
runner.connect('exited', self.runner_exited, name)
def runner_exited(self, runner, name):
# If we weren't unloaded in the meantime, we can open the file using
# the "editor" hint to ensure the editor opens the file.
if self.workbench:
uri = Ide.Uri.new('file://'+name, 0)
self.workbench.open_uri_async(uri, 'editor', 0, None, None, None)