~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/Documentation/sphinx/automarkup.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

Diff markup

Differences between /Documentation/sphinx/automarkup.py (Version linux-6.12-rc7) and /Documentation/sphinx/automarkup.py (Version linux-6.10.14)


  1 # SPDX-License-Identifier: GPL-2.0                  1 # SPDX-License-Identifier: GPL-2.0
  2 # Copyright 2019 Jonathan Corbet <corbet@lwn.ne      2 # Copyright 2019 Jonathan Corbet <corbet@lwn.net>
  3 #                                                   3 #
  4 # Apply kernel-specific tweaks after the initi      4 # Apply kernel-specific tweaks after the initial document processing
  5 # has been done.                                    5 # has been done.
  6 #                                                   6 #
  7 from docutils import nodes                          7 from docutils import nodes
  8 import sphinx                                       8 import sphinx
  9 from sphinx import addnodes                         9 from sphinx import addnodes
 10 from sphinx.errors import NoUri                    10 from sphinx.errors import NoUri
 11 import re                                          11 import re
 12 from itertools import chain                        12 from itertools import chain
 13                                                    13 
 14 #                                                  14 #
 15 # Python 2 lacks re.ASCII...                       15 # Python 2 lacks re.ASCII...
 16 #                                                  16 #
 17 try:                                               17 try:
 18     ascii_p3 = re.ASCII                            18     ascii_p3 = re.ASCII
 19 except AttributeError:                             19 except AttributeError:
 20     ascii_p3 = 0                                   20     ascii_p3 = 0
 21                                                    21 
 22 #                                                  22 #
 23 # Regex nastiness.  Of course.                     23 # Regex nastiness.  Of course.
 24 # Try to identify "function()" that's not alre     24 # Try to identify "function()" that's not already marked up some
 25 # other way.  Sphinx doesn't like a lot of stu     25 # other way.  Sphinx doesn't like a lot of stuff right after a
 26 # :c:func: block (i.e. ":c:func:`mmap()`s" fla     26 # :c:func: block (i.e. ":c:func:`mmap()`s" flakes out), so the last
 27 # bit tries to restrict matches to things that     27 # bit tries to restrict matches to things that won't create trouble.
 28 #                                                  28 #
 29 RE_function = re.compile(r'\b(([a-zA-Z_]\w+)\(     29 RE_function = re.compile(r'\b(([a-zA-Z_]\w+)\(\))', flags=ascii_p3)
 30                                                    30 
 31 #                                                  31 #
 32 # Sphinx 2 uses the same :c:type role for stru     32 # Sphinx 2 uses the same :c:type role for struct, union, enum and typedef
 33 #                                                  33 #
 34 RE_generic_type = re.compile(r'\b(struct|union     34 RE_generic_type = re.compile(r'\b(struct|union|enum|typedef)\s+([a-zA-Z_]\w+)',
 35                              flags=ascii_p3)       35                              flags=ascii_p3)
 36                                                    36 
 37 #                                                  37 #
 38 # Sphinx 3 uses a different C role for each on     38 # Sphinx 3 uses a different C role for each one of struct, union, enum and
 39 # typedef                                          39 # typedef
 40 #                                                  40 #
 41 RE_struct = re.compile(r'\b(struct)\s+([a-zA-Z     41 RE_struct = re.compile(r'\b(struct)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
 42 RE_union = re.compile(r'\b(union)\s+([a-zA-Z_]     42 RE_union = re.compile(r'\b(union)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
 43 RE_enum = re.compile(r'\b(enum)\s+([a-zA-Z_]\w     43 RE_enum = re.compile(r'\b(enum)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
 44 RE_typedef = re.compile(r'\b(typedef)\s+([a-zA     44 RE_typedef = re.compile(r'\b(typedef)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
 45                                                    45 
 46 #                                                  46 #
 47 # Detects a reference to a documentation page      47 # Detects a reference to a documentation page of the form Documentation/... with
 48 # an optional extension                            48 # an optional extension
 49 #                                                  49 #
 50 RE_doc = re.compile(r'(\bDocumentation/)?((\.\     50 RE_doc = re.compile(r'(\bDocumentation/)?((\.\./)*[\w\-/]+)\.(rst|txt)')
 51                                                    51 
 52 RE_namespace = re.compile(r'^\s*..\s*c:namespa     52 RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')
 53                                                    53 
 54 #                                                  54 #
 55 # Reserved C words that we should skip when cr     55 # Reserved C words that we should skip when cross-referencing
 56 #                                                  56 #
 57 Skipnames = [ 'for', 'if', 'register', 'sizeof     57 Skipnames = [ 'for', 'if', 'register', 'sizeof', 'struct', 'unsigned' ]
 58                                                    58 
 59                                                    59 
 60 #                                                  60 #
 61 # Many places in the docs refer to common syst     61 # Many places in the docs refer to common system calls.  It is
 62 # pointless to try to cross-reference them and     62 # pointless to try to cross-reference them and, as has been known
 63 # to happen, somebody defining a function by t     63 # to happen, somebody defining a function by these names can lead
 64 # to the creation of incorrect and confusing c     64 # to the creation of incorrect and confusing cross references.  So
 65 # just don't even try with these names.            65 # just don't even try with these names.
 66 #                                                  66 #
 67 Skipfuncs = [ 'open', 'close', 'read', 'write'     67 Skipfuncs = [ 'open', 'close', 'read', 'write', 'fcntl', 'mmap',
 68               'select', 'poll', 'fork', 'execv     68               'select', 'poll', 'fork', 'execve', 'clone', 'ioctl',
 69               'socket' ]                           69               'socket' ]
 70                                                    70 
 71 c_namespace = ''                                   71 c_namespace = ''
 72                                                    72 
 73 #                                                  73 #
 74 # Detect references to commits.                    74 # Detect references to commits.
 75 #                                                  75 #
 76 RE_git = re.compile(r'commit\s+(?P<rev>[0-9a-f     76 RE_git = re.compile(r'commit\s+(?P<rev>[0-9a-f]{12,40})(?:\s+\(".*?"\))?',
 77     flags=re.IGNORECASE | re.DOTALL)               77     flags=re.IGNORECASE | re.DOTALL)
 78                                                    78 
 79 def markup_refs(docname, app, node):               79 def markup_refs(docname, app, node):
 80     t = node.astext()                              80     t = node.astext()
 81     done = 0                                       81     done = 0
 82     repl = [ ]                                     82     repl = [ ]
 83     #                                              83     #
 84     # Associate each regex with the function t     84     # Associate each regex with the function that will markup its matches
 85     #                                              85     #
 86     markup_func_sphinx2 = {RE_doc: markup_doc_     86     markup_func_sphinx2 = {RE_doc: markup_doc_ref,
 87                            RE_function: markup     87                            RE_function: markup_c_ref,
 88                            RE_generic_type: ma     88                            RE_generic_type: markup_c_ref}
 89                                                    89 
 90     markup_func_sphinx3 = {RE_doc: markup_doc_     90     markup_func_sphinx3 = {RE_doc: markup_doc_ref,
 91                            RE_function: markup     91                            RE_function: markup_func_ref_sphinx3,
 92                            RE_struct: markup_c     92                            RE_struct: markup_c_ref,
 93                            RE_union: markup_c_     93                            RE_union: markup_c_ref,
 94                            RE_enum: markup_c_r     94                            RE_enum: markup_c_ref,
 95                            RE_typedef: markup_     95                            RE_typedef: markup_c_ref,
 96                            RE_git: markup_git}     96                            RE_git: markup_git}
 97                                                    97 
 98     if sphinx.version_info[0] >= 3:                98     if sphinx.version_info[0] >= 3:
 99         markup_func = markup_func_sphinx3          99         markup_func = markup_func_sphinx3
100     else:                                         100     else:
101         markup_func = markup_func_sphinx2         101         markup_func = markup_func_sphinx2
102                                                   102 
103     match_iterators = [regex.finditer(t) for r    103     match_iterators = [regex.finditer(t) for regex in markup_func]
104     #                                             104     #
105     # Sort all references by the starting posi    105     # Sort all references by the starting position in text
106     #                                             106     #
107     sorted_matches = sorted(chain(*match_itera    107     sorted_matches = sorted(chain(*match_iterators), key=lambda m: m.start())
108     for m in sorted_matches:                      108     for m in sorted_matches:
109         #                                         109         #
110         # Include any text prior to match as a    110         # Include any text prior to match as a normal text node.
111         #                                         111         #
112         if m.start() > done:                      112         if m.start() > done:
113             repl.append(nodes.Text(t[done:m.st    113             repl.append(nodes.Text(t[done:m.start()]))
114                                                   114 
115         #                                         115         #
116         # Call the function associated with th    116         # Call the function associated with the regex that matched this text and
117         # append its return to the text           117         # append its return to the text
118         #                                         118         #
119         repl.append(markup_func[m.re](docname,    119         repl.append(markup_func[m.re](docname, app, m))
120                                                   120 
121         done = m.end()                            121         done = m.end()
122     if done < len(t):                             122     if done < len(t):
123         repl.append(nodes.Text(t[done:]))         123         repl.append(nodes.Text(t[done:]))
124     return repl                                   124     return repl
125                                                   125 
126 #                                                 126 #
127 # Keep track of cross-reference lookups that f    127 # Keep track of cross-reference lookups that failed so we don't have to
128 # do them again.                                  128 # do them again.
129 #                                                 129 #
130 failed_lookups = { }                              130 failed_lookups = { }
131 def failure_seen(target):                         131 def failure_seen(target):
132     return (target) in failed_lookups             132     return (target) in failed_lookups
133 def note_failure(target):                         133 def note_failure(target):
134     failed_lookups[target] = True                 134     failed_lookups[target] = True
135                                                   135 
136 #                                                 136 #
137 # In sphinx3 we can cross-reference to C macro    137 # In sphinx3 we can cross-reference to C macro and function, each one with its
138 # own C role, but both match the same regex, s    138 # own C role, but both match the same regex, so we try both.
139 #                                                 139 #
140 def markup_func_ref_sphinx3(docname, app, matc    140 def markup_func_ref_sphinx3(docname, app, match):
141     cdom = app.env.domains['c']                   141     cdom = app.env.domains['c']
142     #                                             142     #
143     # Go through the dance of getting an xref     143     # Go through the dance of getting an xref out of the C domain
144     #                                             144     #
145     base_target = match.group(2)                  145     base_target = match.group(2)
146     target_text = nodes.Text(match.group(0))      146     target_text = nodes.Text(match.group(0))
147     xref = None                                   147     xref = None
148     possible_targets = [base_target]              148     possible_targets = [base_target]
149     # Check if this document has a namespace,     149     # Check if this document has a namespace, and if so, try
150     # cross-referencing inside it first.          150     # cross-referencing inside it first.
151     if c_namespace:                               151     if c_namespace:
152         possible_targets.insert(0, c_namespace    152         possible_targets.insert(0, c_namespace + "." + base_target)
153                                                   153 
154     if base_target not in Skipnames:              154     if base_target not in Skipnames:
155         for target in possible_targets:           155         for target in possible_targets:
156             if (target not in Skipfuncs) and n    156             if (target not in Skipfuncs) and not failure_seen(target):
157                 lit_text = nodes.literal(class    157                 lit_text = nodes.literal(classes=['xref', 'c', 'c-func'])
158                 lit_text += target_text           158                 lit_text += target_text
159                 pxref = addnodes.pending_xref(    159                 pxref = addnodes.pending_xref('', refdomain = 'c',
160                                                   160                                               reftype = 'function',
161                                                   161                                               reftarget = target,
162                                                   162                                               modname = None,
163                                                   163                                               classname = None)
164                 #                                 164                 #
165                 # XXX The Latex builder will t    165                 # XXX The Latex builder will throw NoUri exceptions here,
166                 # work around that by ignoring    166                 # work around that by ignoring them.
167                 #                                 167                 #
168                 try:                              168                 try:
169                     xref = cdom.resolve_xref(a    169                     xref = cdom.resolve_xref(app.env, docname, app.builder,
170                                              '    170                                              'function', target, pxref,
171                                              l    171                                              lit_text)
172                 except NoUri:                     172                 except NoUri:
173                     xref = None                   173                     xref = None
174                                                   174 
175                 if xref:                          175                 if xref:
176                     return xref                   176                     return xref
177                 note_failure(target)              177                 note_failure(target)
178                                                   178 
179     return target_text                            179     return target_text
180                                                   180 
181 def markup_c_ref(docname, app, match):            181 def markup_c_ref(docname, app, match):
182     class_str = {# Sphinx 2 only                  182     class_str = {# Sphinx 2 only
183                  RE_function: 'c-func',           183                  RE_function: 'c-func',
184                  RE_generic_type: 'c-type',       184                  RE_generic_type: 'c-type',
185                  # Sphinx 3+ only                 185                  # Sphinx 3+ only
186                  RE_struct: 'c-struct',           186                  RE_struct: 'c-struct',
187                  RE_union: 'c-union',             187                  RE_union: 'c-union',
188                  RE_enum: 'c-enum',               188                  RE_enum: 'c-enum',
189                  RE_typedef: 'c-type',            189                  RE_typedef: 'c-type',
190                  }                                190                  }
191     reftype_str = {# Sphinx 2 only                191     reftype_str = {# Sphinx 2 only
192                    RE_function: 'function',       192                    RE_function: 'function',
193                    RE_generic_type: 'type',       193                    RE_generic_type: 'type',
194                    # Sphinx 3+ only               194                    # Sphinx 3+ only
195                    RE_struct: 'struct',           195                    RE_struct: 'struct',
196                    RE_union: 'union',             196                    RE_union: 'union',
197                    RE_enum: 'enum',               197                    RE_enum: 'enum',
198                    RE_typedef: 'type',            198                    RE_typedef: 'type',
199                    }                              199                    }
200                                                   200 
201     cdom = app.env.domains['c']                   201     cdom = app.env.domains['c']
202     #                                             202     #
203     # Go through the dance of getting an xref     203     # Go through the dance of getting an xref out of the C domain
204     #                                             204     #
205     base_target = match.group(2)                  205     base_target = match.group(2)
206     target_text = nodes.Text(match.group(0))      206     target_text = nodes.Text(match.group(0))
207     xref = None                                   207     xref = None
208     possible_targets = [base_target]              208     possible_targets = [base_target]
209     # Check if this document has a namespace,     209     # Check if this document has a namespace, and if so, try
210     # cross-referencing inside it first.          210     # cross-referencing inside it first.
211     if c_namespace:                               211     if c_namespace:
212         possible_targets.insert(0, c_namespace    212         possible_targets.insert(0, c_namespace + "." + base_target)
213                                                   213 
214     if base_target not in Skipnames:              214     if base_target not in Skipnames:
215         for target in possible_targets:           215         for target in possible_targets:
216             if not (match.re == RE_function an    216             if not (match.re == RE_function and target in Skipfuncs):
217                 lit_text = nodes.literal(class    217                 lit_text = nodes.literal(classes=['xref', 'c', class_str[match.re]])
218                 lit_text += target_text           218                 lit_text += target_text
219                 pxref = addnodes.pending_xref(    219                 pxref = addnodes.pending_xref('', refdomain = 'c',
220                                                   220                                               reftype = reftype_str[match.re],
221                                                   221                                               reftarget = target, modname = None,
222                                                   222                                               classname = None)
223                 #                                 223                 #
224                 # XXX The Latex builder will t    224                 # XXX The Latex builder will throw NoUri exceptions here,
225                 # work around that by ignoring    225                 # work around that by ignoring them.
226                 #                                 226                 #
227                 try:                              227                 try:
228                     xref = cdom.resolve_xref(a    228                     xref = cdom.resolve_xref(app.env, docname, app.builder,
229                                              r    229                                              reftype_str[match.re], target, pxref,
230                                              l    230                                              lit_text)
231                 except NoUri:                     231                 except NoUri:
232                     xref = None                   232                     xref = None
233                                                   233 
234                 if xref:                          234                 if xref:
235                     return xref                   235                     return xref
236                                                   236 
237     return target_text                            237     return target_text
238                                                   238 
239 #                                                 239 #
240 # Try to replace a documentation reference of     240 # Try to replace a documentation reference of the form Documentation/... with a
241 # cross reference to that page                    241 # cross reference to that page
242 #                                                 242 #
243 def markup_doc_ref(docname, app, match):          243 def markup_doc_ref(docname, app, match):
244     stddom = app.env.domains['std']               244     stddom = app.env.domains['std']
245     #                                             245     #
246     # Go through the dance of getting an xref     246     # Go through the dance of getting an xref out of the std domain
247     #                                             247     #
248     absolute = match.group(1)                     248     absolute = match.group(1)
249     target = match.group(2)                       249     target = match.group(2)
250     if absolute:                                  250     if absolute:
251        target = "/" + target                      251        target = "/" + target
252     xref = None                                   252     xref = None
253     pxref = addnodes.pending_xref('', refdomai    253     pxref = addnodes.pending_xref('', refdomain = 'std', reftype = 'doc',
254                                   reftarget =     254                                   reftarget = target, modname = None,
255                                   classname =     255                                   classname = None, refexplicit = False)
256     #                                             256     #
257     # XXX The Latex builder will throw NoUri e    257     # XXX The Latex builder will throw NoUri exceptions here,
258     # work around that by ignoring them.          258     # work around that by ignoring them.
259     #                                             259     #
260     try:                                          260     try:
261         xref = stddom.resolve_xref(app.env, do    261         xref = stddom.resolve_xref(app.env, docname, app.builder, 'doc',
262                                    target, pxr    262                                    target, pxref, None)
263     except NoUri:                                 263     except NoUri:
264         xref = None                               264         xref = None
265     #                                             265     #
266     # Return the xref if we got it; otherwise     266     # Return the xref if we got it; otherwise just return the plain text.
267     #                                             267     #
268     if xref:                                      268     if xref:
269         return xref                               269         return xref
270     else:                                         270     else:
271         return nodes.Text(match.group(0))         271         return nodes.Text(match.group(0))
272                                                   272 
273 def get_c_namespace(app, docname):                273 def get_c_namespace(app, docname):
274     source = app.env.doc2path(docname)            274     source = app.env.doc2path(docname)
275     with open(source) as f:                       275     with open(source) as f:
276         for l in f:                               276         for l in f:
277             match = RE_namespace.search(l)        277             match = RE_namespace.search(l)
278             if match:                             278             if match:
279                 return match.group(1)             279                 return match.group(1)
280     return ''                                     280     return ''
281                                                   281 
282 def markup_git(docname, app, match):              282 def markup_git(docname, app, match):
283     # While we could probably assume that we a    283     # While we could probably assume that we are running in a git
284     # repository, we can't know for sure, so l    284     # repository, we can't know for sure, so let's just mechanically
285     # turn them into git.kernel.org links with    285     # turn them into git.kernel.org links without checking their
286     # validity. (Maybe we can do something in     286     # validity. (Maybe we can do something in the future to warn about
287     # these references if this is explicitly r    287     # these references if this is explicitly requested.)
288     text = match.group(0)                         288     text = match.group(0)
289     rev = match.group('rev')                      289     rev = match.group('rev')
290     return nodes.reference('', nodes.Text(text    290     return nodes.reference('', nodes.Text(text),
291         refuri=f'https://git.kernel.org/torval    291         refuri=f'https://git.kernel.org/torvalds/c/{rev}')
292                                                   292 
293 def auto_markup(app, doctree, name):              293 def auto_markup(app, doctree, name):
294     global c_namespace                            294     global c_namespace
295     c_namespace = get_c_namespace(app, name)      295     c_namespace = get_c_namespace(app, name)
296     def text_but_not_a_reference(node):           296     def text_but_not_a_reference(node):
297         # The nodes.literal test catches ``lit    297         # The nodes.literal test catches ``literal text``, its purpose is to
298         # avoid adding cross-references to fun    298         # avoid adding cross-references to functions that have been explicitly
299         # marked with cc:func:.                   299         # marked with cc:func:.
300         if not isinstance(node, nodes.Text) or    300         if not isinstance(node, nodes.Text) or isinstance(node.parent, nodes.literal):
301             return False                          301             return False
302                                                   302 
303         child_of_reference = False                303         child_of_reference = False
304         parent = node.parent                      304         parent = node.parent
305         while parent:                             305         while parent:
306             if isinstance(parent, nodes.Refere    306             if isinstance(parent, nodes.Referential):
307                 child_of_reference = True         307                 child_of_reference = True
308                 break                             308                 break
309             parent = parent.parent                309             parent = parent.parent
310         return not child_of_reference             310         return not child_of_reference
311                                                   311 
312     #                                             312     #
313     # This loop could eventually be improved o    313     # This loop could eventually be improved on.  Someday maybe we
314     # want a proper tree traversal with a lot     314     # want a proper tree traversal with a lot of awareness of which
315     # kinds of nodes to prune.  But this works    315     # kinds of nodes to prune.  But this works well for now.
316     #                                             316     #
317     for para in doctree.traverse(nodes.paragra    317     for para in doctree.traverse(nodes.paragraph):
318         for node in para.traverse(condition=te    318         for node in para.traverse(condition=text_but_not_a_reference):
319             node.parent.replace(node, markup_r    319             node.parent.replace(node, markup_refs(name, app, node))
320                                                   320 
321 def setup(app):                                   321 def setup(app):
322     app.connect('doctree-resolved', auto_marku    322     app.connect('doctree-resolved', auto_markup)
323     return {                                      323     return {
324         'parallel_read_safe': True,               324         'parallel_read_safe': True,
325         'parallel_write_safe': True,              325         'parallel_write_safe': True,
326         }                                         326         }
                                                      

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php