0001
0002
0003
0004
0005
0006 u"""
0007 maintainers-include
0008 ~~~~~~~~~~~~~~~~~~~
0009
0010 Implementation of the ``maintainers-include`` reST-directive.
0011
0012 :copyright: Copyright (C) 2019 Kees Cook <keescook@chromium.org>
0013 :license: GPL Version 2, June 1991 see linux/COPYING for details.
0014
0015 The ``maintainers-include`` reST-directive performs extensive parsing
0016 specific to the Linux kernel's standard "MAINTAINERS" file, in an
0017 effort to avoid needing to heavily mark up the original plain text.
0018 """
0019
0020 import sys
0021 import re
0022 import os.path
0023
0024 from docutils import statemachine
0025 from docutils.utils.error_reporting import ErrorString
0026 from docutils.parsers.rst import Directive
0027 from docutils.parsers.rst.directives.misc import Include
0028
0029 __version__ = '1.0'
0030
0031 def setup(app):
0032 app.add_directive("maintainers-include", MaintainersInclude)
0033 return dict(
0034 version = __version__,
0035 parallel_read_safe = True,
0036 parallel_write_safe = True
0037 )
0038
0039 class MaintainersInclude(Include):
0040 u"""MaintainersInclude (``maintainers-include``) directive"""
0041 required_arguments = 0
0042
0043 def parse_maintainers(self, path):
0044 """Parse all the MAINTAINERS lines into ReST for human-readability"""
0045
0046 result = list()
0047 result.append(".. _maintainers:")
0048 result.append("")
0049
0050
0051 descriptions = False
0052 maintainers = False
0053 subsystems = False
0054
0055
0056 field_letter = None
0057 fields = dict()
0058
0059 prev = None
0060 field_prev = ""
0061 field_content = ""
0062
0063 for line in open(path):
0064
0065 if descriptions and line.startswith('Maintainers'):
0066 descriptions = False
0067
0068 result.append("")
0069
0070
0071
0072 if maintainers and not subsystems:
0073 if re.search('^[A-Z0-9]', line):
0074 subsystems = True
0075
0076
0077 line = line.rstrip()
0078
0079
0080 pat = '(Documentation/([^\s\?\*]*)\.rst)'
0081 m = re.search(pat, line)
0082 if m:
0083
0084 line = re.sub(pat, ':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
0085
0086
0087 output = None
0088 if descriptions:
0089
0090 output = "| %s" % (line.replace("\\", "\\\\"))
0091
0092
0093 m = re.search("\s(\S):\s", line)
0094 if m:
0095 field_letter = m.group(1)
0096 if field_letter and not field_letter in fields:
0097 m = re.search("\*([^\*]+)\*", line)
0098 if m:
0099 fields[field_letter] = m.group(1)
0100 elif subsystems:
0101
0102 if len(line) == 0:
0103 continue
0104
0105 if line[1] != ':':
0106
0107
0108
0109
0110
0111 output = field_content + "\n\n"
0112 field_content = ""
0113
0114
0115 heading = re.sub("\s+", " ", line)
0116 output = output + "%s\n%s" % (heading, "~" * len(heading))
0117 field_prev = ""
0118 else:
0119
0120
0121
0122 field, details = line.split(':', 1)
0123 details = details.strip()
0124
0125
0126
0127 if field in ['F', 'N', 'X', 'K']:
0128
0129 if not ':doc:' in details:
0130 details = '``%s``' % (details)
0131
0132
0133 if field == field_prev and field_prev in ['M', 'R', 'L']:
0134 field_content = field_content + ","
0135
0136
0137
0138 if field != field_prev:
0139 output = field_content + "\n"
0140 field_content = ":%s:" % (fields.get(field, field))
0141 field_content = field_content + "\n\t%s" % (details)
0142 field_prev = field
0143 else:
0144 output = line
0145
0146
0147 if output != None:
0148 for separated in output.split('\n'):
0149 result.append(separated)
0150
0151
0152 if line.startswith('----------'):
0153 if prev.startswith('Descriptions'):
0154 descriptions = True
0155 if prev.startswith('Maintainers'):
0156 maintainers = True
0157
0158
0159 prev = line
0160
0161
0162 if field_content != "":
0163 for separated in field_content.split('\n'):
0164 result.append(separated)
0165
0166 output = "\n".join(result)
0167
0168
0169
0170 self.state_machine.insert_input(
0171 statemachine.string2lines(output), path)
0172
0173 def run(self):
0174 """Include the MAINTAINERS file as part of this reST file."""
0175 if not self.state.document.settings.file_insertion_enabled:
0176 raise self.warning('"%s" directive disabled.' % self.name)
0177
0178
0179 path = self.state_machine.document.attributes['source']
0180 path = os.path.realpath(path)
0181 tail = path
0182 while tail != "Documentation" and tail != "":
0183 (path, tail) = os.path.split(path)
0184
0185
0186 path = os.path.join(path, "MAINTAINERS")
0187
0188 try:
0189 self.state.document.settings.record_dependencies.add(path)
0190 lines = self.parse_maintainers(path)
0191 except IOError as error:
0192 raise self.severe('Problems with "%s" directive path:\n%s.' %
0193 (self.name, ErrorString(error)))
0194
0195 return []