[tracker/sam/introspection-fix] Fix missing introspection data for libtracker-sparql (when using Meson)



commit d0d3ab9296f248dd21f9036f3cb6d60a413e530f
Author: Sam Thursfield <sam afuera me uk>
Date:   Fri Jun 23 16:25:05 2017 +0100

    Fix missing introspection data for libtracker-sparql (when using Meson)
    
    Up til now only the Tracker.SparqlConnection and Tracker.SparqlBuilder
    resources were introspectable. This is because we only used the
    introspection output from `valac`, but other bits of libtracker-sparql
    have since been added that are written in C.
    
    There seems to be no way to generate a single .gir for a combined C and
    Vala codebase, so instead I have written a simple `g-ir-merge` script
    which can combine two different namespaces into a single .gir.
    
    This is currently tested and working with the Meson build instructions.
    It would be possible to implement this for the Autotools build
    instructions as well.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=782091

 meson.build                               |   10 ++
 src/libtracker-sparql-backend/meson.build |   38 +++++++
 src/libtracker-sparql/meson.build         |   71 ++++++------
 utils/Makefile.am                         |    1 +
 utils/g-ir-merge/Makefile.am              |    1 +
 utils/g-ir-merge/g-ir-merge               |  172 +++++++++++++++++++++++++++++
 6 files changed, 258 insertions(+), 35 deletions(-)
---
diff --git a/meson.build b/meson.build
index da221d8..94020d5 100644
--- a/meson.build
+++ b/meson.build
@@ -32,6 +32,7 @@ gio_unix = dependency('gio-unix-2.0', version: '>' + glib_required)
 glib = dependency('glib-2.0', version: '>' + glib_required)
 gmodule = dependency('gmodule-2.0', version: '>' + glib_required)
 gobject = dependency('gobject-2.0', version: '>' + glib_required)
+gobject_introspection = dependency('gobject-introspection-1.0')
 gstreamer = dependency('gstreamer-1.0', required: false)
 gstreamer_pbutils = dependency('gstreamer-pbutils-1.0', required: false)
 gstreamer_tag = dependency('gstreamer-tag-1.0', required: false)
@@ -489,6 +490,15 @@ tracker_writeback_modules_dir = join_paths(get_option('prefix'), get_option('lib
 
 vapi_dir = join_paths(get_option('prefix'), get_option('datadir'), 'vala', 'vapi')
 
+g_ir_compiler = find_program('g-ir-compiler', gobject_introspection.get_pkgconfig_variable('g_ir_compiler'))
+g_ir_merge = find_program('g-ir-merge', join_paths(meson.source_root(), 'utils', 'g-ir-merge', 'g-ir-merge'))
+
+# You can use the gobject_introspection.get_pkgconfig_variable() to find these,
+# but then get_option('prefix') won't be honoured if it differs from the prefix
+# gobject-introspection is installed to.
+gir_dir = join_paths(get_option('prefix'), get_option('datadir'), 'gir-1.0')
+typelib_dir = join_paths(get_option('libdir'), 'girepository-1.0')
+
 subdir('src')
 
 if get_option('docs')
diff --git a/src/libtracker-sparql-backend/meson.build b/src/libtracker-sparql-backend/meson.build
index cbbc890..bf2f5ba 100644
--- a/src/libtracker-sparql-backend/meson.build
+++ b/src/libtracker-sparql-backend/meson.build
@@ -2,6 +2,7 @@ libtracker_sparql = library('tracker-sparql-' + tracker_api_version,
     'tracker-backend.vala',
     install: true,
     install_rpath: tracker_internal_libs_dir,
+    link_whole: libtracker_sparql_intermediate_c,
     dependencies: [tracker_sparql_intermediate_dep, tracker_sparql_remote_dep, tracker_sparql_bus_dep, 
tracker_sparql_direct_dep],
 )
 
@@ -10,3 +11,40 @@ tracker_sparql_dep = declare_dependency(
     include_directories: srcinc,
     dependencies: [tracker_sparql_intermediate_dep],
 )
+
+# The introspection generation for libtracker-sparql is awkward because we have
+# both C and Vala code, and we have to generate an introspection repo for each
+# one separately and then combine them together manually.
+#
+# See: <https://bugzilla.gnome.org/show_bug.cgi?id=782091>
+
+tracker_sparql_c_gir = gnome.generate_gir(libtracker_sparql,
+    sources: libtracker_sparql_c_sources + libtracker_sparql_c_public_headers,
+    nsversion: tracker_api_version,
+    namespace: 'Tracker_C',
+    identifier_prefix: 'Tracker',
+    symbol_prefix: 'tracker',
+    includes : ['GLib-2.0', 'GObject-2.0', 'Gio-2.0' ],
+    link_with: libtracker_sparql,
+    extra_args: [
+        '--c-include', 'libtracker-sparql/tracker-sparql.h',
+        '-D', 'TRACKER_COMPILATION',
+    ])
+
+tracker_sparql_c_gir = tracker_sparql_c_gir[0]  # Ignore this .typelib
+tracker_sparql_vala_gir = join_paths(meson.current_build_dir(), '..', 'libtracker-sparql', 'Tracker_Vala-' + 
tracker_api_version + '.gir')
+
+tracker_sparql_gir = custom_target('tracker-sparql-gir',
+    command: [g_ir_merge, '--namespace', 'Tracker', '--nsversion', tracker_api_version, '@INPUT@', 
tracker_sparql_vala_gir],
+    input: [tracker_sparql_c_gir],
+    capture: true,
+    output: 'Tracker-1.0.gir',
+    install: true,
+    install_dir: gir_dir)
+
+tracker_sparql_typelib = custom_target('tracker-sparql-typelib',
+    command: [g_ir_compiler, '--output', '@OUTPUT@', '@INPUT@'],
+    input: tracker_sparql_gir,
+    output: 'Tracker-1.0.typelib',
+    install: true,
+    install_dir: typelib_dir)
diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build
index 9c6c4a7..495c5ad 100644
--- a/src/libtracker-sparql/meson.build
+++ b/src/libtracker-sparql/meson.build
@@ -9,26 +9,18 @@ enums_h = gnome.mkenums('tracker-sparql-enum-types',
 
 tracker_sparql_intermediate_dependencies = [uuid]
 
-libtracker_sparql_intermediate = static_library('tracker-sparql',
+# First build the Vala parts ...
+
+libtracker_sparql_intermediate_vala = static_library('tracker-sparql-intermediate-vala',
     enums_c, enums_h,
     'tracker-namespace.vala',
     'tracker-builder.vala',
     'tracker-connection.vala',
     'tracker-cursor.vala',
     'tracker-utils.vala',
-    'tracker-namespace-manager.c',
-    'tracker-notifier.c',
-    'tracker-resource.c',
-    'tracker-uri.c',
-    'tracker-version.c',
     vala_header: 'tracker-generated-no-checks.h',
     c_args: [ '-DTRACKER_COMPILATION', ],
-    # FIXME: getting Vala to output the GIR means that the symbols from the
-    # C files aren't included. This problem seems to affect the Autotools
-    # build system as well.
-    # FIXME: we also can't depend on this in the TrackerMiner GIR, which
-    # probably breaks stuff
-    vala_gir: 'Tracker-' + tracker_api_version + '.gir',
+    vala_gir: 'Tracker_Vala-' + tracker_api_version + '.gir',
     vala_args: [
         '--debug',
         '--includedir', 'libtracker-sparql',
@@ -56,10 +48,36 @@ tracker_sparql_generated_header = custom_target('tracker-sparql-generated-header
     #
     output: 'tracker-generated.h',
     command: [join_paths(meson.current_source_dir(), 'tracker-sparql-add-include-guards.sh'), 
join_paths(meson.current_build_dir(), 'tracker-generated-no-checks.h'), '@OUTPUT@'],
-    depends: libtracker_sparql_intermediate)
+    depends: libtracker_sparql_intermediate_vala)
+
+# Now build the C parts ...
+
+libtracker_sparql_c_sources = files(
+    'tracker-namespace-manager.c',
+    'tracker-notifier.c',
+    'tracker-resource.c',
+    'tracker-uri.c',
+    'tracker-version.c',
+)
+
+libtracker_sparql_c_public_headers = files(
+    'tracker-namespace-manager.h',
+    'tracker-notifier.h',
+    'tracker-resource.h',
+    'tracker-version.h',
+)
+
+libtracker_sparql_intermediate_c = static_library('tracker-sparql-intermediate-c',
+    enums_c, enums_h,
+    libtracker_sparql_c_sources,
+    dependencies: [tracker_common_dep],
+    c_args: [
+        '-DTRACKER_COMPILATION',
+    ],
+)
 
 tracker_sparql_intermediate_dep = declare_dependency(
-    link_with: [libtracker_sparql_intermediate],
+    link_with: [libtracker_sparql_intermediate_c, libtracker_sparql_intermediate_vala],
     sources: tracker_sparql_generated_header,
     include_directories: [srcinc, include_directories('.')],
     dependencies: tracker_sparql_intermediate_dependencies
@@ -73,12 +91,9 @@ configure_file(
     install_dir: join_paths(get_option('prefix'), get_option('libdir'), 'pkgconfig'))
 
 install_headers(
-    'tracker-notifier.h',
-    'tracker-resource.h',
+    libtracker_sparql_c_public_headers,
     'tracker-ontologies.h',
     'tracker-sparql.h',
-    'tracker-namespace-manager.h',
-    'tracker-version.h',
     subdir: 'tracker-1.0/libtracker-sparql')
 
 # FIXME: we need a custom script to install generated Vala headers; see
@@ -87,26 +102,12 @@ meson.add_install_script('../install-generated-header.sh',
     join_paths(meson.current_build_dir(), 'tracker-generated.h'),
     join_paths(get_option('prefix'), get_option('includedir'), 'tracker-1.0', 'libtracker-sparql'))
 
-custom_target('tracker-sparql-typelib',
-    command: ['g-ir-compiler', '--output', '@OUTPUT@', join_paths(meson.current_build_dir(), 
'Tracker-1.0.gir')],
-    #input: join_paths(meson.current_build_dir(), 'Tracker-1.0.gir'),
-    output: 'Tracker-1.0.typelib',
-    depends: libtracker_sparql_intermediate,
-    install: true,
-    install_dir: join_paths(get_option('libdir'), 'girepository-1.0'))
-
-# FIXME: reusing the same script to install the .gir; see
-# <https://github.com/mesonbuild/meson/issues/891>.
-meson.add_install_script('../install-generated-header.sh',
-    join_paths(meson.current_build_dir(), 'Tracker-1.0.gir'),
-    join_paths(get_option('prefix'), get_option('datadir'), 'gir-1.0'))
-
 # FIXME: we can't depend properly on the generated .vapi to do this.
 custom_target('tracker-sparql-' + tracker_api_version + '.vapi',
-    #input: 'tracker-sparql-vala.vapi',
+    #input: 'tracker-sparql-intermediate-vala.vapi',
     output: 'tracker-sparql-' + tracker_api_version + '.vapi',
-    command: ['sed', '-e', 's/tracker-generated-no-checks.h/tracker-sparql.h/', 
join_paths(meson.current_build_dir(), 'tracker-sparql.vapi')],
-    depends: libtracker_sparql_intermediate,
+    command: ['sed', '-e', 's/tracker-generated-no-checks.h/tracker-sparql.h/', 
join_paths(meson.current_build_dir(), 'tracker-sparql-intermediate-vala.vapi')],
+    depends: libtracker_sparql_intermediate_vala,
     capture: true,
     install: true,
     install_dir: vapi_dir)
diff --git a/utils/Makefile.am b/utils/Makefile.am
index caf0f72..caaa838 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -1,4 +1,5 @@
 SUBDIRS =                                              \
+       g-ir-merge                                     \
        gtk-sparql                                     \
        ontology                                       \
        data-generators                                \
diff --git a/utils/g-ir-merge/Makefile.am b/utils/g-ir-merge/Makefile.am
new file mode 100644
index 0000000..34afbf1
--- /dev/null
+++ b/utils/g-ir-merge/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = g-ir-merge
diff --git a/utils/g-ir-merge/g-ir-merge b/utils/g-ir-merge/g-ir-merge
new file mode 100644
index 0000000..66dfb7c
--- /dev/null
+++ b/utils/g-ir-merge/g-ir-merge
@@ -0,0 +1,172 @@
+#!/usr/bin/python3
+#
+# Copyright 2017, Sam Thursfield <sam afuera me uk>
+#
+# This library 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.
+#
+# This library 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 library; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA  02110-1301, USA.
+
+
+'''g-ir-merge: combine multiple GObject Introspection namespaces together.
+
+This tool exists to solve a problem in Tracker: we have code for
+libtracker-sparql written in both C and Vala. It's not possible to generate
+a single .gir for all of this code as `g-ir-scanner` can't deal with Vala's
+generated .c code, and `valac` only handles .vala code.
+
+The only workable solution seems to be to generate two different .gir files
+and manually combine them. Thankfully it's not too difficult to do this.
+
+For more discussion, see: https://bugzilla.gnome.org/show_bug.cgi?id=782091
+
+'''
+
+
+import argparse
+import sys
+from xml.etree import ElementTree
+
+
+def argument_parser():
+    parser = argparse.ArgumentParser(
+        description="Merge .gir repostories together.")
+
+    parser.add_argument('--namespace', '-n', metavar='NAMESPACE_NAME', type=str, required=True,
+                        help="name for combined .gir namespace")
+    parser.add_argument('--nsversion', metavar='NAMESPACE_VERSION', type=str, required=True,
+                        help="version for combined .gir namespace")
+
+    parser.add_argument('--c-include', metavar='C_INCLUDES', type=list,
+                        help="override list of C includes")
+
+    parser.add_argument('files', metavar="GIR_FILE", type=str, nargs='+',
+                        help="input .gir file")
+    return parser
+
+
+def parse_inputs(files):
+    '''Read the important contents from one or more .gir files and return all.
+
+    This does no post-processing of the data, it simply returns everything
+    including any duplicate or contractory info.
+
+    '''
+    ns = {
+        'core': 'http://www.gtk.org/introspection/core/1.0',
+        'c': 'http://www.gtk.org/introspection/c/1.0'
+    }
+
+    includes = []
+    namespaces = []
+    c_includes = []
+
+    for file in files:
+        gi_repository = ElementTree.parse(file).getroot()
+
+        for gi_include in gi_repository.findall('core:include', ns):
+            includes.append(gi_include)
+
+        for gi_namespace in gi_repository.findall('core:namespace', ns):
+            namespaces.append(gi_namespace)
+
+        for gi_c_include in gi_repository.findall('c:include', ns):
+            c_includes.append(gi_c_include)
+
+    return includes, namespaces, c_includes
+
+
+def merge_includes(all_includes):
+    merged = {}
+    for element in all_includes:
+        name = element.get('name')
+        version = element.get('version')
+        if name not in merged:
+            merged[name] = element
+    return merged.values()
+
+
+def merge_namespaces(all_namespaces):
+    identifier_prefixes = set()
+    symbol_prefixes = set()
+    shared_libraries = set()
+
+    contents = []
+
+    for element in all_namespaces:
+        if element.get('c:identifier_prefixes', None):
+            identifier_prefixes.update(element.get('c:identifier_prefixes').split(','))
+        if element.get('c:symbol_prefixes', None):
+            symbol_prefixes.update(element.get('c:symbol_prefixes').split(','))
+        identifier_prefixes.update(element.get('c:prefix', []))
+        symbol_prefixes.update(element.get('c:prefix', []))
+
+        if element.get('shared-library', None):
+            shared_libraries.update(element.get('shared-library', '').split(','))
+
+        contents.extend(element)
+
+    return (contents,
+            ','.join(sorted(identifier_prefixes)),
+            ','.join(sorted(symbol_prefixes)),
+            ','.join(sorted(shared_libraries)))
+
+
+def create_repository(namespace_name, namespace_version, shared_libraries,
+                      c_identifier_prefixes, c_symbol_prefixes, includes, namespace_contents):
+    '''Create a new GI repository with a single namespace.'''
+    ElementTree.register_namespace(
+        '', 'http://www.gtk.org/introspection/core/1.0')
+    ElementTree.register_namespace(
+        'c', 'http://www.gtk.org/introspection/c/1.0')
+    ElementTree.register_namespace(
+        'glib', 'http://www.gtk.org/introspection/glib/1.0')
+    gir_version = '1.2'
+
+    repository = ElementTree.Element('repository', attrib={'version': gir_version})
+    repository.extend(includes)
+
+    namespace = ElementTree.SubElement(repository, 'namespace', attrib={
+            'name': namespace_name,
+            'version': namespace_version,
+            'shared-library': shared_libraries,
+            'c:identifier-prefixes': c_identifier_prefixes,
+            'c:symbol-prefixes': c_symbol_prefixes,
+        })
+    namespace.extend(namespace_contents)
+
+    return repository
+
+
+def main():
+    args = argument_parser().parse_args()
+
+    all_includes, all_namespaces, all_c_includes = parse_inputs(args.files)
+
+    includes = merge_includes(all_includes)
+
+    namespace_contents, identifier_prefixes, symbol_prefixes, shared_libraries \
+        = merge_namespaces(all_namespaces)
+
+    repository = create_repository(args.namespace, args.nsversion, shared_libraries,
+                                   identifier_prefixes, symbol_prefixes,
+                                   includes, namespace_contents)
+
+    print(ElementTree.tostring(repository, encoding="unicode"))
+
+
+try:
+    main()
+except RuntimeError as e:
+    sys.stderr.write("{}\n".format(e))
+    sys.exit(1)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]