]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - tools/update-dbus-docs.py
firstboot: Update help string with --root-shell options
[thirdparty/systemd.git] / tools / update-dbus-docs.py
index 2f69b50b89c3c98f6f0fc1f0b3c6ed6a07627220..f95faaaf22a809440c0a041d3d1994bb81bc7aa3 100755 (executable)
@@ -3,10 +3,10 @@
 
 import collections
 import sys
+import os
 import shlex
 import subprocess
 import io
-import pprint
 from lxml import etree
 
 PARSER = etree.XMLParser(no_network=True,
@@ -19,23 +19,6 @@ PRINT_ERRORS = True
 class NoCommand(Exception):
     pass
 
-def find_command(lines):
-    acc = []
-    for num, line in enumerate(lines):
-        # skip empty leading line
-        if num == 0 and not line:
-            continue
-        cont = line.endswith('\\')
-        if cont:
-            line = line[:-1].rstrip()
-        acc.append(line if not acc else line.lstrip())
-        if not cont:
-            break
-    joined = ' '.join(acc)
-    if not joined.startswith('$ '):
-        raise NoCommand
-    return joined[2:], lines[:num+1] + [''], lines[-1]
-
 BORING_INTERFACES = [
     'org.freedesktop.DBus.Peer',
     'org.freedesktop.DBus.Introspectable',
@@ -99,12 +82,15 @@ def print_property(declarations, elem, *, prefix, file):
     access = ACCESS_MAP.get(access, access)
     print(f'''{prefix}{access} {type} {name} = {value_ellipsis(type)};''', file=file)
 
-def print_interface(iface, *, prefix, file, print_boring, declarations):
+def print_interface(iface, *, prefix, file, print_boring, only_interface, declarations):
     name = iface.get('name')
 
-    is_boring = name in BORING_INTERFACES
+    is_boring = (name in BORING_INTERFACES or
+                 only_interface is not None and name != only_interface)
+
     if is_boring and print_boring:
         print(f'''{prefix}interface {name} {{ ... }};''', file=file)
+
     elif not is_boring and not print_boring:
         print(f'''{prefix}interface {name} {{''', file=file)
         prefix2 = prefix + '  '
@@ -157,10 +143,11 @@ def check_documented(document, declarations):
 
     return missing
 
-def xml_to_text(destination, xml):
+def xml_to_text(destination, xml, *, only_interface=None):
     file = io.StringIO()
 
     declarations = collections.defaultdict(list)
+    interfaces = []
 
     print(f'''node {destination} {{''', file=file)
 
@@ -168,36 +155,38 @@ def xml_to_text(destination, xml):
         for iface in xml.findall('./interface'):
             print_interface(iface, prefix='  ', file=file,
                             print_boring=print_boring,
+                            only_interface=only_interface,
                             declarations=declarations)
+            name = iface.get('name')
+            if not name in BORING_INTERFACES:
+                interfaces.append(name)
 
     print(f'''}};''', file=file)
 
-    return file.getvalue(), declarations
+    return file.getvalue(), declarations, interfaces
 
 def subst_output(document, programlisting):
-    try:
-        cmd, prefix_lines, footer = find_command(programlisting.text.splitlines())
-    except NoCommand:
+    executable = programlisting.get('executable', None)
+    if executable is None:
+        # Not our thing
         return
+    executable = programlisting.get('executable')
+    node = programlisting.get('node')
+    interface = programlisting.get('interface')
 
-    argv = shlex.split(cmd)
-    argv += ['--xml']
+    argv = [f'{build_dir}/{executable}', f'--bus-introspect={interface}']
     print(f'COMMAND: {shlex.join(argv)}')
 
-    object_idx = argv.index('--object-path')
-    object_path = argv[object_idx + 1]
-
     try:
         out = subprocess.check_output(argv, text=True)
-    except subprocess.CalledProcessError:
-        print('command failed, ignoring', file=sys.stderr)
+    except FileNotFoundError:
+        print(f'{executable} not found, ignoring', file=sys.stderr)
         return
 
     xml = etree.fromstring(out, parser=PARSER)
 
-    new_text, declarations = xml_to_text(object_path, xml)
-
-    programlisting.text = '\n'.join(prefix_lines) + '\n' + new_text + footer
+    new_text, declarations, interfaces = xml_to_text(node, xml, only_interface=interface)
+    programlisting.text = '\n' + new_text + '    '
 
     if declarations:
         missing = check_documented(document, declarations)
@@ -205,9 +194,50 @@ def subst_output(document, programlisting):
 
         # delete old comments
         for child in parent:
+            if (child.tag == etree.Comment
+                and 'Autogenerated' in child.text):
+                parent.remove(child)
             if (child.tag == etree.Comment
                 and 'not documented' in child.text):
                 parent.remove(child)
+            if (child.tag == "variablelist"
+                and child.attrib.get("generated",False) == "True"):
+                parent.remove(child)
+
+        # insert pointer for systemd-directives generation
+        the_tail = programlisting.tail #tail is erased by addnext, so save it here.
+        prev_element = etree.Comment("Autogenerated cross-references for systemd.directives, do not edit")
+        programlisting.addnext(prev_element)
+        programlisting.tail = the_tail
+
+        for interface in interfaces:
+            variablelist = etree.Element("variablelist")
+            variablelist.attrib['class'] = 'dbus-interface'
+            variablelist.attrib['generated'] = 'True'
+            variablelist.attrib['extra-ref'] = interface
+
+            prev_element.addnext(variablelist)
+            prev_element.tail = the_tail
+            prev_element = variablelist
+
+        for decl_type,decl_list in declarations.items():
+            for declaration in decl_list:
+                variablelist = etree.Element("variablelist")
+                variablelist.attrib['class'] = 'dbus-'+decl_type
+                variablelist.attrib['generated'] = 'True'
+                if decl_type == 'method' :
+                    variablelist.attrib['extra-ref'] = declaration + '()'
+                else:
+                    variablelist.attrib['extra-ref'] = declaration
+
+                prev_element.addnext(variablelist)
+                prev_element.tail = the_tail
+                prev_element = variablelist
+
+        last_element = etree.Comment("End of Autogenerated section")
+        prev_element.addnext(last_element)
+        prev_element.tail = the_tail
+        last_element.tail = the_tail
 
         # insert comments for undocumented items
         for item in reversed(missing):
@@ -228,7 +258,7 @@ def process(page):
         subst_output(xml, pl)
 
     out_text = etree.tostring(xml, encoding='unicode')
-    # massage format to avoid some lxml whitespace handling idiosyncracies
+    # massage format to avoid some lxml whitespace handling idiosyncrasies
     # https://bugs.launchpad.net/lxml/+bug/526799
     out_text = (src[:src.find('<refentryinfo')] +
                 out_text[out_text.find('<refentryinfo'):] +
@@ -240,5 +270,14 @@ def process(page):
 if __name__ == '__main__':
     pages = sys.argv[1:]
 
+    if pages[0].startswith('--build-dir='):
+        build_dir = pages[0].partition('=')[2]
+        pages = pages[1:]
+    else:
+        build_dir = 'build'
+
+    if not os.path.exists(f'{build_dir}/systemd'):
+        exit(f"{build_dir}/systemd doesn't exist. Use --build-dir=.")
+
     for page in pages:
         process(page)