Em Tue, 13 Oct 2020 23:13:40 +0000 Nícolas F. R. A. Prado <nfraprado@xxxxxxxxxxxxxx> escreveu: > Sphinx 3.1 introduced namespaces for C cross-references. With this, > each C domain type/function declaration is put inside the namespace that > was active at the time of its declaration. > > To support automatic cross-reference inside C namespaces: > - Save the C namespace used in each doc file (if any) at the source-read > phase, in the beginning of the Sphinx process. > - When making the automarkup, if any namespace was used in the current > file, try to cross-reference to the symbol inside of it before trying > in the global namespace. > > To make the first step possible, disable the parallel_read_safe option > in Sphinx, since the dictionary that maps the files to the C namespaces > can't be concurrently updated. This unfortunately increases the build > time of the documentation. Disabling parallel_read_safe will make performance very poor. Doesn't the C domain store the current namespace somewhere? If so, then, instead of using the source-read phase, something else could be used instead. > > Signed-off-by: Nícolas F. R. A. Prado <nfraprado@xxxxxxxxxxxxxx> > --- > Documentation/sphinx/automarkup.py | 130 ++++++++++++++++++----------- > 1 file changed, 82 insertions(+), 48 deletions(-) > > diff --git a/Documentation/sphinx/automarkup.py b/Documentation/sphinx/automarkup.py > index 409dbc4100de..bca8cf5f519d 100644 > --- a/Documentation/sphinx/automarkup.py > +++ b/Documentation/sphinx/automarkup.py > @@ -45,6 +45,8 @@ RE_typedef = re.compile(r'\b(typedef)\s+([a-zA-Z_]\w+)', flags=re.ASCII) > # > RE_doc = re.compile(r'\bDocumentation(/[\w\-_/]+)(\.\w+)*') > > +RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') > + > # > # Reserved C words that we should skip when cross-referencing > # > @@ -62,6 +64,8 @@ Skipfuncs = [ 'open', 'close', 'read', 'write', 'fcntl', 'mmap', > 'select', 'poll', 'fork', 'execve', 'clone', 'ioctl', > 'socket' ] > > +c_namespace = {} > + > def markup_refs(docname, app, node): > t = node.astext() > done = 0 > @@ -120,30 +124,42 @@ def markup_func_ref_sphinx3(docname, app, match): > # > # Go through the dance of getting an xref out of the C domain > # > - target = match.group(2) > + base_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 = reftype_s, > - reftarget = target, modname = None, > - classname = None) > - # > - # XXX The Latex builder will throw NoUri exceptions here, > - # work around that by ignoring them. > - # > - try: > - xref = cdom.resolve_xref(app.env, docname, app.builder, > - reftype_s, target, pxref, > - lit_text) > - except NoUri: > - xref = None > - > - if xref: > - return xref > + possible_targets = [base_target] > + # Check if this document has a namespace, and if so, try > + # cross-referencing inside it first. > + try: > + namespace = c_namespace[docname] > + except KeyError: > + pass > + else: > + possible_targets.insert(0, namespace + "." + base_target) > + > + if base_target not in Skipnames: > + for target in possible_targets: > + if target not in Skipfuncs: > + 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 = reftype_s, > + reftarget = target, modname = None, > + classname = None) > + # > + # XXX The Latex builder will throw NoUri exceptions here, > + # work around that by ignoring them. > + # > + try: > + xref = cdom.resolve_xref(app.env, docname, app.builder, > + reftype_s, target, pxref, > + lit_text) > + except NoUri: > + xref = None > + > + if xref: > + return xref > > return target_text > > @@ -171,34 +187,43 @@ def markup_c_ref(docname, app, match): > # > # Go through the dance of getting an xref out of the C domain > # > - target = match.group(2) > + base_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) > - # > - # XXX The Latex builder will throw NoUri exceptions here, > - # work around that by ignoring them. > - # > - 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 > + possible_targets = [base_target] > + # Check if this document has a namespace, and if so, try > + # cross-referencing inside it first. > + try: > + namespace = c_namespace[docname] > + except KeyError: > + pass > else: > - return target_text > + possible_targets.insert(0, namespace + "." + base_target) > + > + if base_target not in Skipnames: > + for target in possible_targets: > + if not (match.re == RE_function and target in Skipfuncs): > + 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) > + # > + # XXX The Latex builder will throw NoUri exceptions here, > + # work around that by ignoring them. > + # > + try: > + xref = cdom.resolve_xref(app.env, docname, app.builder, > + reftype_str[match.re], target, pxref, > + lit_text) > + except NoUri: > + xref = None > + > + if xref: > + return xref > + > + return target_text > > # > # Try to replace a documentation reference of the form Documentation/... with a > @@ -246,9 +271,18 @@ def auto_markup(app, doctree, name): > if not isinstance(node.parent, nodes.literal): > node.parent.replace(node, markup_refs(name, app, node)) > > +def save_c_namespace(app, docname, source): > + lines = iter(source[0].splitlines(True)) > + for l in lines: > + match = RE_namespace.search(l) > + if match: > + c_namespace[docname] = match.group(1) > + return > + > def setup(app): > + app.connect('source-read', save_c_namespace) > app.connect('doctree-resolved', auto_markup) > return { > - 'parallel_read_safe': True, > + 'parallel_read_safe': False, > 'parallel_write_safe': True, > } Thanks, Mauro