summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2020-02-14 15:58:26 +0000
committerRichard Hughes <richard@hughsie.com>2020-02-14 15:58:43 +0000
commita4d71c528577be352e3d52f042293547b9ce3784 (patch)
tree6b517de2049b2efb3a11f075ad98e8a04e9d5f1c
parente3517b395b085d048d094aa3727eaf571c87ac38 (diff)
downloadgusb-wip/hughsie/versioning.tar.gz
Validate the exported symbol list during checkwip/hughsie/versioning
This is the same script used by fwupd, and would have caught both recent issues with the exported symbol versions being incorrect.
-rwxr-xr-xcontrib/generate-version-script.py118
-rw-r--r--gusb/libgusb.ver75
-rw-r--r--gusb/meson.build29
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')