fwupd/libfwupd/generate-version-script.py
Richard Hughes d727929327 Generate the LD script from the GObject Introspection data
Also, we now verify the version script is correct each time we run the tests;
the consequences of getting the version script wrong are terribly bad.

Note, we can't actually use generated .map 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
accidentaly actually check in a version of the version script to git.
2017-09-28 09:23:52 +01:00

120 lines
4.1 KiB
Python
Executable File

#!/usr/bin/env python3
# pylint: disable=invalid-name,missing-docstring
#
# Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
#
# Licensed under the GNU General Public License Version 2
#
# This program 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 program 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 program; if not, see <http://www.gnu.org/licenses/>.
import sys
import xml.etree.ElementTree as ET
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)
return
version = node.attrib['version']
if version not in self.releases:
self.releases[version] = []
release = self.releases[version]
release.append(identifier)
return version
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 + 'class'):
# add all class functions
for node in cls.findall(XMLNS + 'function'):
self._add_node(node)
# add the constructor
for node in cls.findall(XMLNS + 'constructor'):
self._add_node(node)
# choose the lowest version method for the _get_type symbol
version_lowest = None
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
# finally add the get_type symbol
if version_lowest:
self.releases[version_lowest].append(type_name)
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):
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())