docs: move generate-docs scripts from "libnm/" to "tools/"
They are not only used in "libnm/" directory. Move to "tools/".
This commit is contained in:
246
tools/generate-docs-nm-settings-docs-gir.py
Executable file
246
tools/generate-docs-nm-settings-docs-gir.py
Executable file
@@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
#
|
||||
# Copyright (C) 2009 - 2017 Red Hat, Inc.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import gi
|
||||
gi.require_version('GIRepository', '2.0')
|
||||
from gi.repository import GIRepository
|
||||
import argparse, re, sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
try:
|
||||
libs = os.environ['LD_LIBRARY_PATH'].split(':')
|
||||
libs.reverse()
|
||||
for lib in libs:
|
||||
GIRepository.Repository.prepend_library_path(lib)
|
||||
except AttributeError:
|
||||
# An old GI version, that has no prepend_library_path
|
||||
# It's alright, it probably interprets LD_LIBRARY_PATH
|
||||
# correctly.
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
gi.require_version('NM', '1.0')
|
||||
from gi.repository import NM, GObject
|
||||
|
||||
dbus_type_name_map = {
|
||||
'b': 'boolean',
|
||||
's': 'string',
|
||||
'i': 'int32',
|
||||
'u': 'uint32',
|
||||
't': 'uint64',
|
||||
'x': 'int64',
|
||||
'y': 'byte',
|
||||
'as': 'array of string',
|
||||
'au': 'array of uint32',
|
||||
'ay': 'byte array',
|
||||
'a{ss}': 'dict of string to string',
|
||||
'a{sv}': 'vardict',
|
||||
'aa{sv}': 'array of vardict',
|
||||
'aau': 'array of array of uint32',
|
||||
'aay': 'array of byte array',
|
||||
'a(ayuay)': 'array of legacy IPv6 address struct',
|
||||
'a(ayuayu)': 'array of legacy IPv6 route struct',
|
||||
}
|
||||
|
||||
ns_map = {
|
||||
'c': 'http://www.gtk.org/introspection/c/1.0',
|
||||
'gi': 'http://www.gtk.org/introspection/core/1.0',
|
||||
'glib': 'http://www.gtk.org/introspection/glib/1.0'
|
||||
}
|
||||
identifier_key = '{%s}identifier' % ns_map['c']
|
||||
nick_key = '{%s}nick' % ns_map['glib']
|
||||
symbol_prefix_key = '{%s}symbol-prefix' % ns_map['c']
|
||||
|
||||
constants = {
|
||||
'TRUE': 'TRUE',
|
||||
'FALSE': 'FALSE',
|
||||
'G_MAXUINT32': 'G_MAXUINT32',
|
||||
'NULL': 'NULL' }
|
||||
setting_names = {}
|
||||
|
||||
def get_setting_name_define(setting):
|
||||
n = setting.attrib[symbol_prefix_key]
|
||||
if n and n.startswith("setting_"):
|
||||
return n[8:].upper()
|
||||
raise Exception("Unexpected symbol_prefix_key \"%s\"" % (n))
|
||||
|
||||
def init_constants(girxml, settings):
|
||||
for const in girxml.findall('./gi:namespace/gi:constant', ns_map):
|
||||
cname = const.attrib['{%s}type' % ns_map['c']]
|
||||
cvalue = const.attrib['value']
|
||||
if const.find('./gi:type[@name="utf8"]', ns_map) is not None:
|
||||
cvalue = '"%s"' % cvalue
|
||||
constants[cname] = cvalue
|
||||
|
||||
for enum in girxml.findall('./gi:namespace/gi:enumeration', ns_map):
|
||||
for enumval in enum.findall('./gi:member', ns_map):
|
||||
cname = enumval.attrib[identifier_key]
|
||||
cvalue = '%s (%s)' % (cname, enumval.attrib['value'])
|
||||
constants[cname] = cvalue
|
||||
|
||||
for enum in girxml.findall('./gi:namespace/gi:bitfield', ns_map):
|
||||
for enumval in enum.findall('./gi:member', ns_map):
|
||||
cname = enumval.attrib[identifier_key]
|
||||
cvalue = '%s (0x%x)' % (cname, int(enumval.attrib['value']))
|
||||
constants[cname] = cvalue
|
||||
|
||||
for setting in settings:
|
||||
setting_type_name = 'NM' + setting.attrib['name'];
|
||||
setting_name_symbol = 'NM_SETTING_' + get_setting_name_define(setting) + '_SETTING_NAME'
|
||||
if setting_name_symbol in constants:
|
||||
setting_name = constants[setting_name_symbol]
|
||||
setting_names[setting_type_name] = setting_name
|
||||
|
||||
def get_prop_type(setting, pspec):
|
||||
dbus_type = setting.get_dbus_property_type(pspec.name).dup_string()
|
||||
prop_type = dbus_type_name_map[dbus_type]
|
||||
|
||||
if GObject.type_is_a(pspec.value_type, GObject.TYPE_ENUM) or GObject.type_is_a(pspec.value_type, GObject.TYPE_FLAGS):
|
||||
prop_type = "%s (%s)" % (pspec.value_type.name, prop_type)
|
||||
|
||||
return prop_type
|
||||
|
||||
def get_docs(propxml):
|
||||
doc_xml = propxml.find('gi:doc', ns_map)
|
||||
if doc_xml is None:
|
||||
return None
|
||||
|
||||
doc = doc_xml.text
|
||||
if 'deprecated' in propxml.attrib:
|
||||
doc = doc + ' Deprecated: ' + propxml.attrib['deprecated']
|
||||
|
||||
doc = re.sub(r'\n\s*', r' ', doc)
|
||||
|
||||
# Expand constants
|
||||
doc = re.sub(r'%([^%]\w*)', lambda match: constants[match.group(1)], doc)
|
||||
|
||||
# #NMSettingWired:mac-address -> "mac-address"
|
||||
doc = re.sub(r'#[A-Za-z0-9_]*:([A-Za-z0-9_-]*)', r'"\1"', doc)
|
||||
|
||||
# #NMSettingWired setting -> "802-3-ethernet" setting
|
||||
doc = re.sub(r'#([A-Z]\w*) setting', lambda match: setting_names[match.group(1)] + ' setting', doc)
|
||||
|
||||
# remaining gtk-doc cleanup
|
||||
doc = doc.replace('%%', '%')
|
||||
doc = doc.replace('<!-- -->', '')
|
||||
doc = re.sub(r' Element-.ype:.*', '', doc)
|
||||
doc = re.sub(r'#([A-Z]\w*)', r'\1', doc)
|
||||
|
||||
# Remove sentences that refer to functions
|
||||
doc = re.sub(r'\.\s+[^.]*\w\(\)[^.]*\.', r'.', doc)
|
||||
|
||||
return doc
|
||||
|
||||
def get_default_value(setting, pspec, propxml):
|
||||
default_value = setting.get_property(pspec.name.replace('-', '_'))
|
||||
if default_value is None:
|
||||
return default_value
|
||||
|
||||
value_type = get_prop_type(setting, pspec)
|
||||
if value_type == 'string' and default_value != '' and pspec.name != 'name':
|
||||
default_value = '"%s"' % default_value
|
||||
elif value_type == 'boolean':
|
||||
default_value = str(default_value).upper()
|
||||
elif value_type == 'byte array':
|
||||
default_value = '[]'
|
||||
elif str(default_value).startswith('<'):
|
||||
default_value = None
|
||||
elif str(default_value).startswith('['):
|
||||
default_value = None
|
||||
|
||||
return default_value
|
||||
|
||||
def settings_sort_key(x):
|
||||
x_prefix = x.attrib['{%s}symbol-prefix' % ns_map['c']]
|
||||
# always sort NMSettingConnection first
|
||||
return (x_prefix != "setting_connection", x_prefix);
|
||||
|
||||
def escape(val):
|
||||
return str(val).replace('"', '"')
|
||||
|
||||
def usage():
|
||||
print("Usage: %s --gir FILE --output FILE" % sys.argv[0])
|
||||
exit()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-l', '--lib-path', metavar='PATH', action='append', help='path to scan for shared libraries')
|
||||
parser.add_argument('-g', '--gir', metavar='FILE', help='NM-1.0.gir file')
|
||||
parser.add_argument('-o', '--output', metavar='FILE', help='output file')
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.gir is None or args.output is None:
|
||||
usage()
|
||||
|
||||
if args.lib_path:
|
||||
for lib in args.lib_path:
|
||||
GIRepository.Repository.prepend_library_path(lib)
|
||||
|
||||
girxml = ET.parse(args.gir).getroot()
|
||||
outfile = open(args.output, mode='w')
|
||||
|
||||
basexml = girxml.find('./gi:namespace/gi:class[@name="Setting"]', ns_map)
|
||||
settings = girxml.findall('./gi:namespace/gi:class[@parent="Setting"]', ns_map)
|
||||
# Hack. Need a better way to do this
|
||||
ipxml = girxml.find('./gi:namespace/gi:class[@name="SettingIPConfig"]', ns_map)
|
||||
settings.extend(girxml.findall('./gi:namespace/gi:class[@parent="SettingIPConfig"]', ns_map))
|
||||
settings = sorted(settings, key=settings_sort_key)
|
||||
|
||||
init_constants(girxml, settings)
|
||||
|
||||
outfile.write("""<?xml version=\"1.0\"?>
|
||||
<!DOCTYPE nm-setting-docs [
|
||||
<!ENTITY quot """>
|
||||
]>
|
||||
<nm-setting-docs>
|
||||
""")
|
||||
|
||||
for settingxml in settings:
|
||||
if 'abstract' in settingxml.attrib:
|
||||
continue
|
||||
|
||||
new_func = NM.__getattr__(settingxml.attrib['name'])
|
||||
setting = new_func()
|
||||
|
||||
class_desc = get_docs(settingxml)
|
||||
if class_desc is None:
|
||||
raise Exception("%s needs a gtk-doc block with one-line description" % setting.props.name)
|
||||
outfile.write(" <setting name=\"%s\" description=\"%s\" name_upper=\"%s\" >\n" % (setting.props.name, class_desc, get_setting_name_define (settingxml)))
|
||||
|
||||
setting_properties = { prop.name: prop for prop in GObject.list_properties(setting) if prop.name != 'name' }
|
||||
|
||||
for prop in sorted(setting_properties):
|
||||
pspec = setting_properties[prop]
|
||||
|
||||
propxml = settingxml.find('./gi:property[@name="%s"]' % pspec.name, ns_map)
|
||||
if propxml is None:
|
||||
propxml = basexml.find('./gi:property[@name="%s"]' % pspec.name, ns_map)
|
||||
if propxml is None:
|
||||
propxml = ipxml.find('./gi:property[@name="%s"]' % pspec.name, ns_map)
|
||||
|
||||
value_type = get_prop_type(setting, pspec)
|
||||
value_desc = get_docs(propxml)
|
||||
default_value = get_default_value(setting, pspec, propxml)
|
||||
|
||||
prop_upper = prop.upper().replace('-', '_')
|
||||
|
||||
if value_desc is None:
|
||||
raise Exception("%s.%s needs a documentation description" % (setting.props.name, prop))
|
||||
|
||||
default_value_as_xml = ''
|
||||
if default_value is not None:
|
||||
default_value_as_xml = (' default=\"%s\"' % (escape(default_value)))
|
||||
|
||||
outfile.write(" <property name=\"%s\" name_upper=\"%s\" type=\"%s\"%s description=\"%s\" />\n" %
|
||||
(prop, prop_upper, value_type, default_value_as_xml, escape(value_desc)))
|
||||
|
||||
outfile.write(" </setting>\n")
|
||||
|
||||
outfile.write("</nm-setting-docs>\n")
|
||||
outfile.close()
|
Reference in New Issue
Block a user