Loading a libpeas module written in Vala and built with meson fails

459 Views Asked by At

I tried to build this example with the help of meson: Getting started with libpeas extensions in vala

My meson.build looks like the following:

vala_deps = [
  dependency('gio-2.0', version: '>= 2.50'),
  dependency('gtk+-3.0', version: '>= 3.22'),
]
libpeas_dep = dependency('libpeas-1.0')

libfoo_deps = [vala_deps, libpeas_dep]
libfoo = shared_library(
  'foo',
  'window.vala',
  vala_header: 'foo.h',
  dependencies: libfoo_deps,
  install: true,
)
libfoo_dep = declare_dependency(
      link_with: libfoo,
      dependencies: libfoo_deps,
      include_directories: include_directories('.'),
)

extension_deps = [vala_deps, libpeas_dep]
extension = shared_library(
  'extension',
  'extension.vala',
  vala_header: 'extension.h',
  dependencies: [vala_deps, libpeas_dep, libfoo_dep],
  install: true,
  #install_dir : '/app/bin'
)
extension_dep = declare_dependency(
      link_with: extension,
      dependencies: extension_deps,
      include_directories: include_directories('.'),
)

gnome = import('gnome')
extension_resources = gnome.compile_resources(    
  'extension-resources',                      
  'extension.gresource.xml',                      
  c_name: 'extension',                        
)
foo_sources = [
  'main.vala',
  extension_resources[0],
]
foo = executable(
  'foo',
  foo_sources,
  link_with: extension,
  #objects: 'libextension.so',
  dependencies: [vala_deps, libfoo_dep, extension_dep],
  install: true,
)

if I use the precompiled shared object file objects: 'libextension.so', inside foo = executable( the example works, if I don't it doesn't.

Any suggestions?

EDIT:

the involved files have the following contents:

extension.gresource.xml:

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/org/gnome/Foo/plugins">
    <file>extension.plugin</file>
  </gresource>
</gresources>

extension.plugin:

[Plugin]
Name=extension
Module=extension
Embedded=peas_register_types

extension.vala:

class ValaExtension : Object, Foo.Extension {

    public Foo.Window window { get; construct set; }

    Gtk.Button button;

    void activate() {
        button = new Gtk.Button.with_label("Say Hello");
        button.clicked.connect(() => {
            button.set_label("Hello World!");
        });
        window.buttons.add(button);
        button.show();
    }

    void deactivate() {
        window.buttons.remove(button);
    }
}

[ModuleInit]
public void peas_register_types(TypeModule module) {
    var objmodule = module as Peas.ObjectModule;

    objmodule.register_extension_type(typeof (Foo.Extension), typeof (ValaExtension));
}

main.vala:

int main (string[] args) {
    var app = new Gtk.Application ("org.gnome.Foo", ApplicationFlags.FLAGS_NONE);
    app.activate.connect (() => {
        var win = app.active_window;
        if (win == null) {
            win = new Foo.Window (app);
        }
        win.present ();
    });

    return app.run (args);
}

window.vala:

namespace Foo {

    public class Window : Gtk.Window {

        public Gtk.ButtonBox buttons { get; set; }

        private Peas.ExtensionSet extensions { get; set; }

        public Window(Gtk.Application app) {
            Object(application: app);

            this.buttons = new Gtk.ButtonBox(Gtk.Orientation.VERTICAL);
            this.destroy.connect(Gtk.main_quit);

            var engine = Peas.Engine.get_default();

            engine.add_search_path("/app/lib", null);
            engine.prepend_search_path("resource:///org/gnome/Foo/plugins/", null);

            extensions = new Peas.ExtensionSet(engine, typeof (Foo.Extension), "window", this);
            extensions.extension_added.connect((info, extension) => {
                (extension as Foo.Extension).activate();
            });
            extensions.extension_removed.connect((info, extension) => {
                (extension as Foo.Extension).deactivate();
            });

            foreach (var plugin in engine.get_plugin_list())
                engine.try_load_plugin(plugin);

            this.add(buttons);
            this.show_all();
        }
    }

    public interface Extension : Object {
        public abstract Window window { get; construct set; }
        public abstract void activate();
        public abstract void deactivate();
    }
}

if I comment the objects:'libextension.so' line I get the following error:

(foo:2): libpeas-WARNING **: 20:27:31.400: Failed to get 'peas_register_types' for module 'extension': 'peas_register_types': foo: undefined symbol: peas_register_types

(foo:2): libpeas-WARNING **: 20:27:31.400: Error loading plugin 'extension'

Solved:

if I add a dummy function:

public void extension_init ()
{
}

to extension.vala and call it from main.vala the linking works properly for an Embedded plugin, see also here: gnome_builder_plugins_init.c

1

There are 1 best solutions below

3
On BEST ANSWER

objects: 'libextension.so' would link the object directly in to the executable. From the example it looks as though you are loading a module at run time using libpeas. So by building the module in to the executable I would assume you are having trouble loading the module at runtime.

The tutorial also advises to set LD_LIBRARY_PATH to include the directory with the extension and to also have a libpeas .plugin file. That could be the problem. It would help if you listed the error message or details of what is not working.

By the way to use an object from a meson build target it may be better to do objects: extension.extract_objects() so the name of the object is not hard coded in to the build. See build target object. The use of objects: is mainly useful for tests though.

Update

I think your module name in extension.plugin should be extension.so. I also like to be explicit about the module loader so I've used Loader=C in the past. I've not come across Embedded before. So I would try this as my extension.plugin:

[Plugin]
Name=My first libpeas extension
Module=extension.so
Loader=C