diff options
Diffstat (limited to 'Documentation/sphinx')
-rw-r--r-- | Documentation/sphinx/automarkup.py | 203 | ||||
-rw-r--r-- | Documentation/sphinx/cdomain.py | 93 | ||||
-rw-r--r-- | Documentation/sphinx/kerneldoc.py | 15 | ||||
-rwxr-xr-x | Documentation/sphinx/parse-headers.pl | 2 |
4 files changed, 282 insertions, 31 deletions
diff --git a/Documentation/sphinx/automarkup.py b/Documentation/sphinx/automarkup.py index b18236370742..409dbc4100de 100644 --- a/Documentation/sphinx/automarkup.py +++ b/Documentation/sphinx/automarkup.py @@ -13,6 +13,7 @@ if sphinx.version_info[0] < 2 or \ else: from sphinx.errors import NoUri import re +from itertools import chain # # Regex nastiness. Of course. @@ -21,7 +22,34 @@ import re # :c:func: block (i.e. ":c:func:`mmap()`s" flakes out), so the last # bit tries to restrict matches to things that won't create trouble. # -RE_function = re.compile(r'([\w_][\w\d_]+\(\))') +RE_function = re.compile(r'\b(([a-zA-Z_]\w+)\(\))', flags=re.ASCII) + +# +# Sphinx 2 uses the same :c:type role for struct, union, enum and typedef +# +RE_generic_type = re.compile(r'\b(struct|union|enum|typedef)\s+([a-zA-Z_]\w+)', + flags=re.ASCII) + +# +# Sphinx 3 uses a different C role for each one of struct, union, enum and +# typedef +# +RE_struct = re.compile(r'\b(struct)\s+([a-zA-Z_]\w+)', flags=re.ASCII) +RE_union = re.compile(r'\b(union)\s+([a-zA-Z_]\w+)', flags=re.ASCII) +RE_enum = re.compile(r'\b(enum)\s+([a-zA-Z_]\w+)', flags=re.ASCII) +RE_typedef = re.compile(r'\b(typedef)\s+([a-zA-Z_]\w+)', flags=re.ASCII) + +# +# Detects a reference to a documentation page of the form Documentation/... with +# an optional extension +# +RE_doc = re.compile(r'\bDocumentation(/[\w\-_/]+)(\.\w+)*') + +# +# Reserved C words that we should skip when cross-referencing +# +Skipnames = [ 'for', 'if', 'register', 'sizeof', 'struct', 'unsigned' ] + # # Many places in the docs refer to common system calls. It is @@ -34,32 +62,73 @@ Skipfuncs = [ 'open', 'close', 'read', 'write', 'fcntl', 'mmap', 'select', 'poll', 'fork', 'execve', 'clone', 'ioctl', 'socket' ] -# -# Find all occurrences of function() and try to replace them with -# appropriate cross references. -# -def markup_funcs(docname, app, node): - cdom = app.env.domains['c'] +def markup_refs(docname, app, node): t = node.astext() done = 0 repl = [ ] - for m in RE_function.finditer(t): + # + # Associate each regex with the function that will markup its matches + # + markup_func_sphinx2 = {RE_doc: markup_doc_ref, + RE_function: markup_c_ref, + RE_generic_type: markup_c_ref} + + markup_func_sphinx3 = {RE_doc: markup_doc_ref, + RE_function: markup_func_ref_sphinx3, + RE_struct: markup_c_ref, + RE_union: markup_c_ref, + RE_enum: markup_c_ref, + RE_typedef: markup_c_ref} + + if sphinx.version_info[0] >= 3: + markup_func = markup_func_sphinx3 + else: + markup_func = markup_func_sphinx2 + + match_iterators = [regex.finditer(t) for regex in markup_func] + # + # Sort all references by the starting position in text + # + sorted_matches = sorted(chain(*match_iterators), key=lambda m: m.start()) + for m in sorted_matches: # - # Include any text prior to function() as a normal text node. + # Include any text prior to match as a normal text node. # if m.start() > done: repl.append(nodes.Text(t[done:m.start()])) + # - # Go through the dance of getting an xref out of the C domain + # Call the function associated with the regex that matched this text and + # append its return to the text # - target = m.group(1)[:-2] - target_text = nodes.Text(target + '()') - xref = None - if target not in Skipfuncs: - lit_text = nodes.literal(classes=['xref', 'c', 'c-func']) + repl.append(markup_func[m.re](docname, app, m)) + + done = m.end() + if done < len(t): + repl.append(nodes.Text(t[done:])) + return repl + +# +# In sphinx3 we can cross-reference to C macro and function, each one with its +# own C role, but both match the same regex, so we try both. +# +def markup_func_ref_sphinx3(docname, app, match): + class_str = ['c-func', 'c-macro'] + reftype_str = ['function', 'macro'] + + cdom = app.env.domains['c'] + # + # Go through the dance of getting an xref out of the C domain + # + target = match.group(2) + target_text = nodes.Text(match.group(0)) + xref = None + if not (target in Skipfuncs or target in Skipnames): + for class_s, reftype_s in zip(class_str, reftype_str): + lit_text = nodes.literal(classes=['xref', 'c', class_s]) lit_text += target_text pxref = addnodes.pending_xref('', refdomain = 'c', - reftype = 'function', + reftype = reftype_s, reftarget = target, modname = None, classname = None) # @@ -68,21 +137,99 @@ def markup_funcs(docname, app, node): # try: xref = cdom.resolve_xref(app.env, docname, app.builder, - 'function', target, pxref, lit_text) + reftype_s, target, pxref, + lit_text) except NoUri: xref = None + + if xref: + return xref + + return target_text + +def markup_c_ref(docname, app, match): + class_str = {# Sphinx 2 only + RE_function: 'c-func', + RE_generic_type: 'c-type', + # Sphinx 3+ only + RE_struct: 'c-struct', + RE_union: 'c-union', + RE_enum: 'c-enum', + RE_typedef: 'c-type', + } + reftype_str = {# Sphinx 2 only + RE_function: 'function', + RE_generic_type: 'type', + # Sphinx 3+ only + RE_struct: 'struct', + RE_union: 'union', + RE_enum: 'enum', + RE_typedef: 'type', + } + + cdom = app.env.domains['c'] + # + # Go through the dance of getting an xref out of the C domain + # + target = match.group(2) + target_text = nodes.Text(match.group(0)) + xref = None + if not ((match.re == RE_function and target in Skipfuncs) + or (target in Skipnames)): + lit_text = nodes.literal(classes=['xref', 'c', class_str[match.re]]) + lit_text += target_text + pxref = addnodes.pending_xref('', refdomain = 'c', + reftype = reftype_str[match.re], + reftarget = target, modname = None, + classname = None) # - # Toss the xref into the list if we got it; otherwise just put - # the function text. + # XXX The Latex builder will throw NoUri exceptions here, + # work around that by ignoring them. # - if xref: - repl.append(xref) - else: - repl.append(target_text) - done = m.end() - if done < len(t): - repl.append(nodes.Text(t[done:])) - return repl + try: + xref = cdom.resolve_xref(app.env, docname, app.builder, + reftype_str[match.re], target, pxref, + lit_text) + except NoUri: + xref = None + # + # Return the xref if we got it; otherwise just return the plain text. + # + if xref: + return xref + else: + return target_text + +# +# Try to replace a documentation reference of the form Documentation/... with a +# cross reference to that page +# +def markup_doc_ref(docname, app, match): + stddom = app.env.domains['std'] + # + # Go through the dance of getting an xref out of the std domain + # + target = match.group(1) + xref = None + pxref = addnodes.pending_xref('', refdomain = 'std', reftype = 'doc', + reftarget = target, modname = None, + classname = None, refexplicit = False) + # + # XXX The Latex builder will throw NoUri exceptions here, + # work around that by ignoring them. + # + try: + xref = stddom.resolve_xref(app.env, docname, app.builder, 'doc', + target, pxref, None) + except NoUri: + xref = None + # + # Return the xref if we got it; otherwise just return the plain text. + # + if xref: + return xref + else: + return nodes.Text(match.group(0)) def auto_markup(app, doctree, name): # @@ -97,7 +244,7 @@ def auto_markup(app, doctree, name): for para in doctree.traverse(nodes.paragraph): for node in para.traverse(nodes.Text): if not isinstance(node.parent, nodes.literal): - node.parent.replace(node, markup_funcs(name, app, node)) + node.parent.replace(node, markup_refs(name, app, node)) def setup(app): app.connect('doctree-resolved', auto_markup) diff --git a/Documentation/sphinx/cdomain.py b/Documentation/sphinx/cdomain.py index cbac8e608dc4..014a5229e57a 100644 --- a/Documentation/sphinx/cdomain.py +++ b/Documentation/sphinx/cdomain.py @@ -40,14 +40,94 @@ from sphinx import addnodes from sphinx.domains.c import c_funcptr_sig_re, c_sig_re from sphinx.domains.c import CObject as Base_CObject from sphinx.domains.c import CDomain as Base_CDomain +from itertools import chain +import re -__version__ = '1.0' +__version__ = '1.1' # Get Sphinx version major, minor, patch = sphinx.version_info[:3] +# Namespace to be prepended to the full name +namespace = None + +# +# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags +# - Store the namespace if ".. c:namespace::" tag is found +# +RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') + +def markup_namespace(match): + global namespace + + namespace = match.group(1) + + return "" + +# +# Handle c:macro for function-style declaration +# +RE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$') +def markup_macro(match): + return ".. c:function:: " + match.group(1) + ' ' + match.group(2) + +# +# Handle newer c domain tags that are evaluated as .. c:type: for +# backward-compatibility with Sphinx < 3.0 +# +RE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$') + +def markup_ctype(match): + return ".. c:type:: " + match.group(2) + +# +# Handle newer c domain tags that are evaluated as :c:type: for +# backward-compatibility with Sphinx < 3.0 +# +RE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`') +def markup_ctype_refs(match): + return ":c:type:`" + match.group(2) + '`' + +# +# Simply convert :c:expr: and :c:texpr: into a literal block. +# +RE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`') +def markup_c_expr(match): + return '\ ``' + match.group(2) + '``\ ' + +# +# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones +# +def c_markups(app, docname, source): + result = "" + markup_func = { + RE_namespace: markup_namespace, + RE_expr: markup_c_expr, + RE_macro: markup_macro, + RE_ctype: markup_ctype, + RE_ctype_refs: markup_ctype_refs, + } + + lines = iter(source[0].splitlines(True)) + for n in lines: + match_iterators = [regex.finditer(n) for regex in markup_func] + matches = sorted(chain(*match_iterators), key=lambda m: m.start()) + for m in matches: + n = n[:m.start()] + markup_func[m.re](m) + n[m.end():] + + result = result + n + + source[0] = result + +# +# Now implements support for the cdomain namespacing logic +# + def setup(app): + # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace:: + app.connect('source-read', c_markups) + if (major == 1 and minor < 8): app.override_domain(CDomain) else: @@ -75,6 +155,8 @@ class CObject(Base_CObject): function-like macro, the name of the macro is returned. Otherwise ``False`` is returned. """ + global namespace + if not self.objtype == 'function': return False @@ -107,11 +189,16 @@ class CObject(Base_CObject): param += nodes.emphasis(argname, argname) paramlist += param + if namespace: + fullname = namespace + "." + fullname + return fullname def handle_signature(self, sig, signode): """Transform a C signature into RST nodes.""" + global namespace + fullname = self.handle_func_like_macro(sig, signode) if not fullname: fullname = super(CObject, self).handle_signature(sig, signode) @@ -122,6 +209,10 @@ class CObject(Base_CObject): else: # FIXME: handle :name: value of other declaration types? pass + else: + if namespace: + fullname = namespace + "." + fullname + return fullname def add_target_and_index(self, name, sig, signode): diff --git a/Documentation/sphinx/kerneldoc.py b/Documentation/sphinx/kerneldoc.py index 4bcbd6ae01cd..e9857ab904f1 100644 --- a/Documentation/sphinx/kerneldoc.py +++ b/Documentation/sphinx/kerneldoc.py @@ -62,6 +62,7 @@ class KernelDocDirective(Directive): 'export': directives.unchanged, 'internal': directives.unchanged, 'identifiers': directives.unchanged, + 'no-identifiers': directives.unchanged, 'functions': directives.unchanged, } has_content = False @@ -70,6 +71,11 @@ class KernelDocDirective(Directive): env = self.state.document.settings.env cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno'] + # Pass the version string to kernel-doc, as it needs to use a different + # dialect, depending what the C domain supports for each specific + # Sphinx versions + cmd += ['-sphinx-version', sphinx.__version__] + filename = env.config.kerneldoc_srctree + '/' + self.arguments[0] export_file_patterns = [] @@ -99,6 +105,12 @@ class KernelDocDirective(Directive): else: cmd += ['-no-doc-sections'] + if 'no-identifiers' in self.options: + no_identifiers = self.options.get('no-identifiers').split() + if no_identifiers: + for i in no_identifiers: + cmd += ['-nosymbol', i] + for pattern in export_file_patterns: for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern): env.note_dependency(os.path.abspath(f)) @@ -136,7 +148,8 @@ class KernelDocDirective(Directive): lineoffset = int(match.group(1)) - 1 # we must eat our comments since the upset the markup else: - result.append(line, filename, lineoffset) + doc = env.srcdir + "/" + env.docname + ":" + str(self.lineno) + result.append(line, doc + ": " + filename, lineoffset) lineoffset += 1 node = nodes.section() diff --git a/Documentation/sphinx/parse-headers.pl b/Documentation/sphinx/parse-headers.pl index 00a69aceff44..1910079f984f 100755 --- a/Documentation/sphinx/parse-headers.pl +++ b/Documentation/sphinx/parse-headers.pl @@ -110,7 +110,7 @@ while (<IN>) { ) { my $s = $1; - $structs{$s} = "struct :c:type:`$s`\\ "; + $structs{$s} = "struct $s\\ "; next; } } |