1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
#!/usr/bin/env python
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Copyright (C) 2022 Red Hat, Inc.
#
from __future__ import print_function
import xml.etree.ElementTree as ET
import argparse
import re
import sys
C_NS = "http://www.gtk.org/introspection/c/1.0"
CORE_NS = "http://www.gtk.org/introspection/core/1.0"
GLIB_NS = "http://www.gtk.org/introspection/glib/1.0"
def syms_from_gir(girfile):
def xml_symbols(xml, types):
ret = []
for t in types:
ret += xml.findall("./{%s}namespace/{%s}%s" % (CORE_NS, CORE_NS, t))
ret += xml.findall("./{%s}namespace/*/{%s}%s" % (CORE_NS, CORE_NS, t))
return ret
girxml = ET.parse(girfile)
c_syms = {}
for sym in xml_symbols(girxml, ("constructor", "function", "method")):
c_syms[sym.get("{%s}identifier" % C_NS)] = sym.get("version")
for sym in xml_symbols(
girxml, ("bitfield", "class", "enumeration", "interface", "record")
):
get_type = sym.get("{%s}get-type" % GLIB_NS)
if get_type is None:
continue
version = sym.get("version")
if version is None:
# FIXME: The get_type() functions should be exported in the same
# version the type itself appeared. However, a large number of
# classes lack Since: tags in their doc blocks. Fall back to using
# the tag on _new() method for the test to be able to proceed
# reasonably. This should be fixed eventually.
constructor = sym.find("./{%s}constructor" % CORE_NS)
if constructor is not None:
version = constructor.get("version")
c_syms[get_type] = version
return c_syms
# Older Python doesn't have str.removesuffix()
def str_removesuffix(string, suffix):
try:
return string.removesuffix(suffix)
except AttributeError:
if string.endswith(suffix):
return string[: -len(suffix)]
else:
return string
def syms_from_ver(verfile):
c_syms = {}
for line in open(verfile).readlines():
line = line.strip()
if line.endswith("{"):
line = str_removesuffix(line, " {")
m = re.search(r"^libnm_([0-9]+)_([0-9]+)_([0-9]+)$", line)
if not m:
continue
(major, minor, micro) = m.groups()
if int(major) > 1 or int(minor) > 0:
if int(micro) > 0:
# Snap to next major version. Perhaps not
# exactly correct, but good for all symbols
# we export but nm_ethtool_optname_is_feature().
minor = str(int(minor) + 2)
version = major + "." + minor
else:
version = None
elif (
line.endswith(";")
and not line.startswith("}")
and not line.startswith("#")
and not line == "*;"
):
c_syms[str_removesuffix(line, ";")] = version
# These are exceptions and we cannot know the version for the symbol so we
# hardcode it.
c_syms["nm_ethtool_optname_is_feature"] = "1.20"
c_syms["nm_setting_bond_port_get_prio"] = "1.44"
c_syms["nm_utils_copy_cert_as_user"] = "1.56"
c_syms["nm_vpn_plugin_info_supports_safe_private_file_access"] = "1.56"
return c_syms
parser = argparse.ArgumentParser()
parser.add_argument(
"--gir",
metavar="FILE",
help="NM-1.0.gir file",
required=True,
)
parser.add_argument(
"--ver",
metavar="FILE",
help="libnm.ver file",
required=True,
)
args = parser.parse_args()
gir_syms = syms_from_gir(args.gir)
ver_syms = syms_from_ver(args.ver)
exit_code = 0
for gir_sym, gir_ver in gir_syms.items():
if gir_sym not in ver_syms:
exit_code = 1
print(
'FAIL: "%s" found in "%s", but is not exported. Needs adding to "%s"?'
% (gir_sym, args.gir, args.ver),
file=sys.stderr,
)
continue
if gir_ver != ver_syms[gir_sym]:
exit_code = 1
print(
'FAIL: "%s" exported in version "%s" but documented as available since "%s"'
% (gir_sym, ver_syms[gir_sym], gir_ver),
file=sys.stderr,
)
# In python2, dict.keys() returns lists, not sets. Cast them.
for sym in set(ver_syms.keys()) - set(gir_syms.keys()):
exit_code = 1
print(
'FAIL: "%s" found in "%s", but not in "%s". Maybe the doc comment is wrong or g-ir-scanner messed up?'
% (sym, args.ver, args.gir),
file=sys.stderr,
)
sys.exit(exit_code)
|