diff options
author | Richard Hughes <richard@hughsie.com> | 2020-02-14 15:58:26 +0000 |
---|---|---|
committer | Richard Hughes <richard@hughsie.com> | 2020-02-20 09:30:28 +0000 |
commit | 3ee71729cf44bdd6d4956a1aad47ea5214cd61f9 (patch) | |
tree | 6b517de2049b2efb3a11f075ad98e8a04e9d5f1c | |
parent | 942b3b78e0ebf97a88634928d872905eef62f27c (diff) | |
download | gusb-3ee71729cf44bdd6d4956a1aad47ea5214cd61f9.tar.gz |
Validate the exported symbol list during check
This is the same script used by fwupd, and would have caught both recent issues
with the exported symbol versions being incorrect.
-rwxr-xr-x | contrib/generate-version-script.py | 118 | ||||
-rw-r--r-- | gusb/libgusb.ver | 75 | ||||
-rw-r--r-- | gusb/meson.build | 29 |
3 files changed, 210 insertions, 12 deletions
diff --git a/contrib/generate-version-script.py b/contrib/generate-version-script.py new file mode 100755 index 0000000..408199f --- /dev/null +++ b/contrib/generate-version-script.py @@ -0,0 +1,118 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2017 Richard Hughes <richard@hughsie.com> +# +# SPDX-License-Identifier: LGPL-2.1+ + +import sys +import xml.etree.ElementTree as ET + +from pkg_resources import parse_version + +XMLNS = '{http://www.gtk.org/introspection/core/1.0}' +XMLNS_C = '{http://www.gtk.org/introspection/c/1.0}' + +def usage(return_code): + """ print usage and exit with the supplied return code """ + if return_code == 0: + out = sys.stdout + else: + out = sys.stderr + out.write("usage: %s <NAME> <INPUT> <OUTPUT>\n" % sys.argv[0]) + sys.exit(return_code) + +class LdVersionScript: + """ Rasterize some text """ + + def __init__(self, library_name): + self.library_name = library_name + self.releases = {} + + def _add_node(self, node): + identifier = node.attrib[XMLNS_C + 'identifier'] + if 'version' not in node.attrib: + print('No version for', identifier) + sys.exit(1) + version = node.attrib['version'] + if version not in self.releases: + self.releases[version] = [] + release = self.releases[version] + if identifier not in release: + release.append(identifier) + return version + + def _add_cls(self, cls): + + # add all class functions + for node in cls.findall(XMLNS + 'function'): + self._add_node(node) + + # choose the lowest version method for the _get_type symbol + version_lowest = None + if '{http://www.gtk.org/introspection/glib/1.0}get-type' not in cls.attrib: + return + type_name = cls.attrib['{http://www.gtk.org/introspection/glib/1.0}get-type'] + + # add all class methods + for node in cls.findall(XMLNS + 'method'): + version_tmp = self._add_node(node) + if version_tmp: + if not version_lowest or version_tmp < version_lowest: + version_lowest = version_tmp + + # add the constructor + for node in cls.findall(XMLNS + 'constructor'): + version_tmp = self._add_node(node) + if version_tmp: + if not version_lowest or version_tmp < version_lowest: + version_lowest = version_tmp + + # finally add the get_type symbol + if version_lowest: + self.releases[version_lowest].append(type_name) + + def import_gir(self, filename): + tree = ET.parse(filename) + root = tree.getroot() + for ns in root.findall(XMLNS + 'namespace'): + for node in ns.findall(XMLNS + 'function'): + self._add_node(node) + for cls in ns.findall(XMLNS + 'record'): + self._add_cls(cls) + for cls in ns.findall(XMLNS + 'class'): + self._add_cls(cls) + + def render(self): + + # get a sorted list of all the versions + versions = [] + for version in self.releases: + versions.append(version) + + # output the version data to a file + verout = '# generated automatically, do not edit!\n' + oldversion = None + for version in sorted(versions, key=parse_version): + symbols = sorted(self.releases[version]) + verout += '\n%s_%s {\n' % (self.library_name, version) + verout += ' global:\n' + for symbol in symbols: + verout += ' %s;\n' % symbol + verout += ' local: *;\n' + if oldversion: + verout += '} %s_%s;\n' % (self.library_name, oldversion) + else: + verout += '};\n' + oldversion = version + return verout + +if __name__ == '__main__': + if {'-?', '--help', '--usage'}.intersection(set(sys.argv)): + usage(0) + if len(sys.argv) != 4: + usage(1) + + ld = LdVersionScript(library_name=sys.argv[1]) + ld.import_gir(sys.argv[2]) + open(sys.argv[3], 'w').write(ld.render()) diff --git a/gusb/libgusb.ver b/gusb/libgusb.ver index c05b661..4fa2141 100644 --- a/gusb/libgusb.ver +++ b/gusb/libgusb.ver @@ -1,17 +1,59 @@ +# generated automatically, do not edit! + LIBGUSB_0.1.0 { global: - g_usb_*; + g_usb_context_error_quark; + g_usb_context_get_source; + g_usb_context_get_type; + g_usb_context_new; + g_usb_context_set_debug; + g_usb_device_bulk_transfer; + g_usb_device_bulk_transfer_async; + g_usb_device_bulk_transfer_finish; + g_usb_device_claim_interface; + g_usb_device_close; + g_usb_device_control_transfer; + g_usb_device_control_transfer_async; + g_usb_device_control_transfer_finish; + g_usb_device_error_quark; + g_usb_device_get_address; + g_usb_device_get_bus; + g_usb_device_get_configuration; + g_usb_device_get_manufacturer_index; + g_usb_device_get_pid; + g_usb_device_get_product_index; + g_usb_device_get_serial_number_index; + g_usb_device_get_string_descriptor; + g_usb_device_get_type; + g_usb_device_get_vid; + g_usb_device_interrupt_transfer; + g_usb_device_interrupt_transfer_async; + g_usb_device_interrupt_transfer_finish; + g_usb_device_list_coldplug; + g_usb_device_list_find_by_bus_address; + g_usb_device_list_find_by_vid_pid; + g_usb_device_list_get_devices; + g_usb_device_list_get_type; + g_usb_device_list_new; + g_usb_device_open; + g_usb_device_release_interface; + g_usb_device_reset; + g_usb_device_set_configuration; + g_usb_source_error_quark; + g_usb_strerror; local: *; }; LIBGUSB_0.1.1 { global: g_usb_device_get_platform_id; + local: *; } LIBGUSB_0.1.0; LIBGUSB_0.1.7 { global: g_usb_device_get_device_class; + local: *; } LIBGUSB_0.1.1; LIBGUSB_0.2.2 { @@ -20,25 +62,28 @@ LIBGUSB_0.2.2 { g_usb_context_find_by_bus_address; g_usb_context_find_by_vid_pid; g_usb_context_get_devices; + local: *; } LIBGUSB_0.1.7; LIBGUSB_0.2.4 { global: - g_usb_device_get_parent; + g_usb_context_find_by_platform_id; g_usb_device_get_children; + g_usb_device_get_device_protocol; + g_usb_device_get_device_subclass; + g_usb_device_get_parent; + g_usb_device_get_pid_as_str; g_usb_device_get_port_number; g_usb_device_get_vid_as_str; - g_usb_device_get_pid_as_str; - g_usb_device_get_device_subclass; - g_usb_device_get_device_protocol; - g_usb_context_find_by_platform_id; + local: *; } LIBGUSB_0.2.2; LIBGUSB_0.2.5 { global: - g_usb_device_get_custom_index; g_usb_context_get_main_context; g_usb_context_set_main_context; + g_usb_device_get_custom_index; + local: *; } LIBGUSB_0.2.4; LIBGUSB_0.2.8 { @@ -56,36 +101,42 @@ LIBGUSB_0.2.8 { g_usb_interface_get_number; g_usb_interface_get_protocol; g_usb_interface_get_subclass; + g_usb_interface_get_type; + local: *; } LIBGUSB_0.2.5; LIBGUSB_0.2.9 { global: g_usb_context_wait_for_replug; + local: *; } LIBGUSB_0.2.8; LIBGUSB_0.2.11 { global: - g_usb_context_set_flags; g_usb_context_get_flags; + g_usb_context_set_flags; + local: *; } LIBGUSB_0.2.9; LIBGUSB_0.3.1 { global: g_usb_device_get_spec; g_usb_version_string; + local: *; } LIBGUSB_0.2.11; LIBGUSB_0.3.3 { global: - g_usb_interface_get_endpoints; - g_usb_endpoint_get_kind; - g_usb_endpoint_get_maximum_packet_size; - g_usb_endpoint_get_polling_interval; g_usb_endpoint_get_address; g_usb_endpoint_get_direction; g_usb_endpoint_get_extra; + g_usb_endpoint_get_kind; + g_usb_endpoint_get_maximum_packet_size; g_usb_endpoint_get_number; + g_usb_endpoint_get_polling_interval; g_usb_endpoint_get_refresh; g_usb_endpoint_get_synch_address; g_usb_endpoint_get_type; + g_usb_interface_get_endpoints; + local: *; } LIBGUSB_0.3.1; diff --git a/gusb/meson.build b/gusb/meson.build index 931399d..fa2b924 100644 --- a/gusb/meson.build +++ b/gusb/meson.build @@ -117,6 +117,7 @@ libgusb_girtarget = gnome.generate_gir(gusb, 'gusb-source.h', 'gusb-util.c', 'gusb-util.h', + 'gusb-version.c', ], nsversion : '1.0', namespace : 'GUsb', @@ -140,6 +141,34 @@ libgusb_girtarget = gnome.generate_gir(gusb, ) libgusb_gir = libgusb_girtarget[0] libgusb_typelib = libgusb_girtarget[1] + +# Verify the map file is correct -- note we can't actually use the generated +# file for two reasons: +# +# 1. We don't hard depend on GObject Introspection +# 2. The map file is required to build the lib that the GIR is built from +# +# To avoid the circular dep, and to ensure we don't change exported API +# accidentally actually check in a version of the version script to git. +mapfile_target = custom_target('gusb_mapfile', + input: libgusb_girtarget[0], + output: 'libgusb.ver', + command: [ + join_paths(meson.source_root(), 'contrib', 'generate-version-script.py'), + 'LIBGUSB', + '@INPUT@', + '@OUTPUT@', + ], +) +diffcmd = find_program('diff') +test('gusb-exported-api', diffcmd, + args : [ + '-urNp', + join_paths(meson.current_source_dir(), 'libgusb.ver'), + mapfile_target, + ], + ) + endif if get_option('vapi') |