cglibgobjectmeson-build

Building a gobject library with meson leads to undefined symbols


I would like to implement a library that is based on the gobject type system. So every object is a instance of GObjectClass

In the header gwl-registry.h I have:

#define GWL_TYPE_REGISTRY gwl_registry_get_type()
G_DECLARE_DERIVABLE_TYPE(GwlRegistry, gwl_registry, GWL, REGISTRY, GObject)

Which declares the type and the function that obtains a type. This is what I should do according the gobject tutorial: https://developer.gnome.org/gobject/stable/howto-gobject.html If I understand it correctly these macro's should expand to include the instance cast GWL_REGISTRY() and type check macro GWL_IS_REGISTRY(). However both functions rely on the function gwl_registry_get_type(). If I'm not mistaken this should be implemented in the source file gwl-registry.c. Here I define the type using this code:

G_DEFINE_TYPE_WITH_PRIVATE(GwlRegistry, gwl_registry, G_TYPE_OBJECT)

Than this macro should expand to create the GType gwl_registry_get_type().

Using meson I create a library which currently looks like this:

├── data
│   └── xdg-shell.xml
├── include
│   ├── gwl-display.h
│   ├── gwl.h
│   ├── gwl-import.h
│   ├── gwl-registry.h
│   ├── gwl-registry-private.h
│   └── meson.build
├── meson.build
├── src
│   ├── gwayland.c
│   ├── gwl-display.c
│   ├── gwl-registry.c
│   └── meson.build
└── test
    ├── display-test.c
    ├── gwayland-test.c
    ├── meson.build
    ├── registry-test.c
    └── tests.h

The toplevel meson.build looks like:

project(
    'GWayland', 'c',
    version : '0.1',
    default_options : ['warning_level=3']
)

glib_dep = dependency('glib-2.0')
gobject_dep = dependency('gobject-2.0')
wayland_dep = dependency('wayland-client')

# These arguments are only used to build the shared library
# not the executables that use the library.
lib_args = ['-DBUILDING_GWL']

subdir('src')
subdir('include')

# Make this library usable as a Meson subproject.
gwayland_dep = declare_dependency(
    include_directories: include_directories('./include'),
    dependencies: [glib_dep, gobject_dep, wayland_dep],
    link_with : gwayland_shared_lib
)

subdir('test')

pkg_mod = import('pkgconfig')
pkg_mod.generate(
    name : 'GWayland',
    filebase : 'gwayland',
    description : 'Meson sample project.',
    subdirs : 'gwayland',
    libraries : gwayland_shared_lib,
    version : '0.1',

)

The src meson.build looks like:

gwayland_srcs = [
    'gwl-display.c',
    'gwl-registry.c',
]

gwayland_shared_lib = shared_library(
    'gwayland',
    gwayland_srcs,
    install : true,
    c_args : lib_args,
    gnu_symbol_visibility : 'hidden',
    include_directories : '../include',
    dependencies : [glib_dep, gobject_dep, wayland_dep],
)

and the test:

test_sources = [
    'gwayland-test.c',
    'tests.h',
    'display-test.c',
    'registry-test.c',
]

test_deps = [glib_dep, wayland_dep]

test_exe = executable(
    'gwayland_test',
    test_sources,
    dependencies: gwayland_dep,
#    link_with : gwayland_shared_lib
    env : [
        'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
        'G_TEST_BUILDDIR=@0@'.format(meson.current_source_dir())
    ]
)

test('gwayland', test_exe)

Currently in the tests I'm calling the casting macro GWL_IS_REGISTRY()

g_assert_true(GWL_IS_REGISTRY(registry));

This leads to the following error:

cc  -o test/gwayland_test test/gwayland_test.p/gwayland-test.c.o test/gwayland_test.p/display-test.c.o test/gwayland_test.p/registry-test.c.o -Wl,--as-needed -Wl,--no-undefined -Wl,--start-group src/libgwayland.so /usr/lib/x86_64-linux-gnu/libglib-2.0.so /usr/lib/x86_64-linux-gnu/libgobject-2.0.so /usr/lib/x86_64-linux-gnu/libwayland-client.so -Wl,--end-group '-Wl,-rpath,$ORIGIN/../src' -Wl,-rpath-link,/home/duijn119/github/gwayland/build/src
test/gwayland_test.p/registry-test.c.o: In function `GWL_IS_REGISTRY':
/home/duijn119/github/gwayland/build/../include/gwl-registry.h:30: undefined reference to `gwl_registry_get_type'
collect2: error: ld returned 1 exit status

If I check the library with nm:

nm src/libgwayland.so | egrep 'get_type|new.'
000000000000193f t gwl_display_get_type
0000000000001fcd T gwl_display_new_address
000000000000231f t gwl_registry_get_type

it seems that somehow the gwl_registry_get_type is not exported for use outside of the library, itside the library/compilation unit I can call the function just fine.

Would any one please help me with glib/gobject to tell me what I'm doing wrong, or do I have some issues with meson to build the library and the tests.


Solution

  • You’ve explicitly asked for symbols to not be exported from your library by default:

    gnu_symbol_visibility : 'hidden'
    

    Either change that default, or explicitly mark the symbols you want to be exported by using G_MODULE_EXPORT.

    You can explicitly mark the gwl_display_get_type() symbol to be exported using

    #define GWL_TYPE_REGISTRY gwl_registry_get_type()
    G_MODULE_EXPORT
    G_DECLARE_DERIVABLE_TYPE(GwlRegistry, gwl_registry, GWL, REGISTRY, GObject)
    

    as the get_type() function is guaranteed to be the first thing emitted by the G_DECLARE_DERIVABLE_TYPE macro.