Merge tag 'rproc-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc...
[linux-block.git] / Documentation / sphinx / maintainers_include.py
CommitLineData
aa204855
KC
1#!/usr/bin/env python
2# SPDX-License-Identifier: GPL-2.0
3# -*- coding: utf-8; mode: python -*-
4# pylint: disable=R0903, C0330, R0914, R0912, E0401
5
6u"""
7 maintainers-include
8 ~~~~~~~~~~~~~~~~~~~
9
10 Implementation of the ``maintainers-include`` reST-directive.
11
12 :copyright: Copyright (C) 2019 Kees Cook <keescook@chromium.org>
13 :license: GPL Version 2, June 1991 see linux/COPYING for details.
14
15 The ``maintainers-include`` reST-directive performs extensive parsing
16 specific to the Linux kernel's standard "MAINTAINERS" file, in an
17 effort to avoid needing to heavily mark up the original plain text.
18"""
19
20import sys
21import re
22import os.path
23
24from docutils import statemachine
25from docutils.utils.error_reporting import ErrorString
26from docutils.parsers.rst import Directive
27from docutils.parsers.rst.directives.misc import Include
28
29__version__ = '1.0'
30
31def setup(app):
32 app.add_directive("maintainers-include", MaintainersInclude)
33 return dict(
34 version = __version__,
35 parallel_read_safe = True,
36 parallel_write_safe = True
37 )
38
39class MaintainersInclude(Include):
40 u"""MaintainersInclude (``maintainers-include``) directive"""
41 required_arguments = 0
42
43 def parse_maintainers(self, path):
44 """Parse all the MAINTAINERS lines into ReST for human-readability"""
45
46 result = list()
47 result.append(".. _maintainers:")
48 result.append("")
49
50 # Poor man's state machine.
51 descriptions = False
52 maintainers = False
53 subsystems = False
54
55 # Field letter to field name mapping.
56 field_letter = None
57 fields = dict()
58
59 prev = None
60 field_prev = ""
61 field_content = ""
62
63 for line in open(path):
aa204855
KC
64 # Have we reached the end of the preformatted Descriptions text?
65 if descriptions and line.startswith('Maintainers'):
66 descriptions = False
67 # Ensure a blank line following the last "|"-prefixed line.
68 result.append("")
69
70 # Start subsystem processing? This is to skip processing the text
71 # between the Maintainers heading and the first subsystem name.
72 if maintainers and not subsystems:
73 if re.search('^[A-Z0-9]', line):
74 subsystems = True
75
76 # Drop needless input whitespace.
77 line = line.rstrip()
78
79 # Linkify all non-wildcard refs to ReST files in Documentation/.
80 pat = '(Documentation/([^\s\?\*]*)\.rst)'
81 m = re.search(pat, line)
82 if m:
83 # maintainers.rst is in a subdirectory, so include "../".
84 line = re.sub(pat, ':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
85
86 # Check state machine for output rendering behavior.
87 output = None
88 if descriptions:
89 # Escape the escapes in preformatted text.
90 output = "| %s" % (line.replace("\\", "\\\\"))
91 # Look for and record field letter to field name mappings:
92 # R: Designated *reviewer*: FullName <address@domain>
93 m = re.search("\s(\S):\s", line)
94 if m:
95 field_letter = m.group(1)
96 if field_letter and not field_letter in fields:
97 m = re.search("\*([^\*]+)\*", line)
98 if m:
99 fields[field_letter] = m.group(1)
100 elif subsystems:
101 # Skip empty lines: subsystem parser adds them as needed.
102 if len(line) == 0:
103 continue
104 # Subsystem fields are batched into "field_content"
105 if line[1] != ':':
106 # Render a subsystem entry as:
107 # SUBSYSTEM NAME
108 # ~~~~~~~~~~~~~~
109
110 # Flush pending field content.
111 output = field_content + "\n\n"
112 field_content = ""
113
114 # Collapse whitespace in subsystem name.
115 heading = re.sub("\s+", " ", line)
116 output = output + "%s\n%s" % (heading, "~" * len(heading))
117 field_prev = ""
118 else:
119 # Render a subsystem field as:
120 # :Field: entry
121 # entry...
122 field, details = line.split(':', 1)
123 details = details.strip()
124
125 # Mark paths (and regexes) as literal text for improved
126 # readability and to escape any escapes.
127 if field in ['F', 'N', 'X', 'K']:
128 # But only if not already marked :)
129 if not ':doc:' in details:
130 details = '``%s``' % (details)
131
132 # Comma separate email field continuations.
133 if field == field_prev and field_prev in ['M', 'R', 'L']:
134 field_content = field_content + ","
135
136 # Do not repeat field names, so that field entries
137 # will be collapsed together.
138 if field != field_prev:
139 output = field_content + "\n"
140 field_content = ":%s:" % (fields.get(field, field))
141 field_content = field_content + "\n\t%s" % (details)
142 field_prev = field
143 else:
144 output = line
145
146 # Re-split on any added newlines in any above parsing.
147 if output != None:
148 for separated in output.split('\n'):
149 result.append(separated)
150
151 # Update the state machine when we find heading separators.
152 if line.startswith('----------'):
153 if prev.startswith('Descriptions'):
154 descriptions = True
155 if prev.startswith('Maintainers'):
156 maintainers = True
157
158 # Retain previous line for state machine transitions.
159 prev = line
160
161 # Flush pending field contents.
162 if field_content != "":
163 for separated in field_content.split('\n'):
164 result.append(separated)
165
166 output = "\n".join(result)
167 # For debugging the pre-rendered results...
168 #print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
169
170 self.state_machine.insert_input(
171 statemachine.string2lines(output), path)
172
173 def run(self):
174 """Include the MAINTAINERS file as part of this reST file."""
175 if not self.state.document.settings.file_insertion_enabled:
176 raise self.warning('"%s" directive disabled.' % self.name)
177
178 # Walk up source path directories to find Documentation/../
179 path = self.state_machine.document.attributes['source']
180 path = os.path.realpath(path)
181 tail = path
182 while tail != "Documentation" and tail != "":
183 (path, tail) = os.path.split(path)
184
185 # Append "MAINTAINERS"
186 path = os.path.join(path, "MAINTAINERS")
187
188 try:
189 self.state.document.settings.record_dependencies.add(path)
190 lines = self.parse_maintainers(path)
191 except IOError as error:
192 raise self.severe('Problems with "%s" directive path:\n%s.' %
193 (self.name, ErrorString(error)))
194
195 return []