From 04f9dceffd436abf9c38c49b72f0f6aa6a838bca Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Mon, 9 Aug 2021 09:34:19 -0700 Subject: [PATCH] docs: use moderncmakedomain, Sphinx 4 (#574) * docs: use moderncmakedomain, Sphinx 4 * docs: Fix CMake example syntax in FindPythonExtensions documentation This commit fixes the following warning reported when building the documentation: /path/to/scikit-build/skbuild/resources/cmake/FindPythonExtensions.cmake:169: WARNING: Could not lex literal_block as "cmake". Highlighting skipped. * docs: Update "generators.rst" fixing documentation formatting issues This commit fixes the following warnings reported when building the documentation: /path/to/scikit-build/docs/generators.rst:130: WARNING: Inline strong start-string without end-string. /path/to/scikit-build/docs/generators.rst:223: WARNING: Block quote ends without a blank line; unexpected unindent. /path/to/scikit-build/docs/generators.rst:223: WARNING: Unexpected section title or transition. Co-authored-by: Jean-Christophe Fillion-Robin --- docs/cmake.py | 392 ------------------ docs/conf.py | 2 +- docs/generators.rst | 4 +- requirements-docs.txt | 9 +- .../cmake/FindPythonExtensions.cmake | 2 +- 5 files changed, 10 insertions(+), 399 deletions(-) delete mode 100644 docs/cmake.py diff --git a/docs/cmake.py b/docs/cmake.py deleted file mode 100644 index 6cf0dede..00000000 --- a/docs/cmake.py +++ /dev/null @@ -1,392 +0,0 @@ -#============================================================================= -# CMake - Cross Platform Makefile Generator -# Copyright 2000-2013 Kitware, Inc., Insight Software Consortium -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -import os -import re - -# Monkey patch for pygments reporting an error when generator expressions are -# used. -# https://bitbucket.org/birkenfeld/pygments-main/issue/942/cmake-generator-expressions-not-handled -from pygments.lexers import CMakeLexer -from pygments.token import Name, Operator -from pygments.lexer import bygroups -CMakeLexer.tokens["args"].append(('(\\$<)(.+?)(>)', - bygroups(Operator, Name.Variable, Operator))) - -# Monkey patch for sphinx generating invalid content for qcollectiongenerator -# https://bitbucket.org/birkenfeld/sphinx/issue/1435/qthelp-builder-should-htmlescape-keywords -from sphinx.util.pycompat import htmlescape -from sphinx.builders.qthelp import QtHelpBuilder -old_build_keywords = QtHelpBuilder.build_keywords -def new_build_keywords(self, title, refs, subitems): - old_items = old_build_keywords(self, title, refs, subitems) - new_items = [] - for item in old_items: - before, rest = item.split("ref=\"", 1) - ref, after = rest.split("\"") - if ("<" in ref and ">" in ref): - new_items.append(before + "ref=\"" + htmlescape(ref) + "\"" + after) - else: - new_items.append(item) - return new_items -QtHelpBuilder.build_keywords = new_build_keywords - - -from docutils.parsers.rst import Directive, directives -from docutils.transforms import Transform -try: - from docutils.utils.error_reporting import SafeString, ErrorString -except ImportError: - # error_reporting was not in utils before version 0.11: - from docutils.error_reporting import SafeString, ErrorString - -from docutils import io, nodes - -from sphinx.directives import ObjectDescription -from sphinx.domains import Domain, ObjType -from sphinx.roles import XRefRole -from sphinx.util.nodes import make_refnode -from sphinx import addnodes - -class CMakeModule(Directive): - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = True - option_spec = {'encoding': directives.encoding} - - def __init__(self, *args, **keys): - self.re_start = re.compile(r'^#\[(?P=*)\[\.rst:$') - Directive.__init__(self, *args, **keys) - - def run(self): - settings = self.state.document.settings - if not settings.file_insertion_enabled: - raise self.warning('"%s" directive disabled.' % self.name) - - env = self.state.document.settings.env - rel_path, path = env.relfn2path(self.arguments[0]) - path = os.path.normpath(path) - encoding = self.options.get('encoding', settings.input_encoding) - e_handler = settings.input_encoding_error_handler - try: - settings.record_dependencies.add(path) - f = io.FileInput(source_path=path, encoding=encoding, - error_handler=e_handler) - except UnicodeEncodeError as error: - raise self.severe('Problems with "%s" directive path:\n' - 'Cannot encode input file path "%s" ' - '(wrong locale?).' % - (self.name, SafeString(path))) - except IOError as error: - raise self.severe('Problems with "%s" directive path:\n%s.' % - (self.name, ErrorString(error))) - raw_lines = f.read().splitlines() - f.close() - rst = None - lines = [] - for line in raw_lines: - if rst is not None and rst != '#': - # Bracket mode: check for end bracket - pos = line.find(rst) - if pos >= 0: - if line[0] == '#': - line = '' - else: - line = line[0:pos] - rst = None - else: - # Line mode: check for .rst start (bracket or line) - m = self.re_start.match(line) - if m: - rst = ']%s]' % m.group('eq') - line = '' - elif line == '#.rst:': - rst = '#' - line = '' - elif rst == '#': - if line == '#' or line[:2] == '# ': - line = line[2:] - else: - rst = None - line = '' - elif rst is None: - line = '' - lines.append(line) - if rst is not None and rst != '#': - raise self.warning('"%s" found unclosed bracket "#[%s[.rst:" in %s' % - (self.name, rst[1:-1], path)) - self.state_machine.insert_input(lines, path) - return [] - -class _cmake_index_entry: - def __init__(self, desc): - self.desc = desc - - def __call__(self, title, targetid, main = 'main'): - return ('pair', u'%s ; %s' % (self.desc, title), targetid, main, None) - -_cmake_index_objs = { - 'command': _cmake_index_entry('command'), - 'generator': _cmake_index_entry('generator'), - 'manual': _cmake_index_entry('manual'), - 'module': _cmake_index_entry('module'), - 'policy': _cmake_index_entry('policy'), - 'prop_cache': _cmake_index_entry('cache property'), - 'prop_dir': _cmake_index_entry('directory property'), - 'prop_gbl': _cmake_index_entry('global property'), - 'prop_inst': _cmake_index_entry('installed file property'), - 'prop_sf': _cmake_index_entry('source file property'), - 'prop_test': _cmake_index_entry('test property'), - 'prop_tgt': _cmake_index_entry('target property'), - 'variable': _cmake_index_entry('variable'), - } - -def _cmake_object_inventory(env, document, line, objtype, targetid): - inv = env.domaindata['cmake']['objects'] - if targetid in inv: - document.reporter.warning( - 'CMake object "%s" also described in "%s".' % - (targetid, env.doc2path(inv[targetid][0])), line=line) - inv[targetid] = (env.docname, objtype) - -class CMakeTransform(Transform): - - # Run this transform early since we insert nodes we want - # treated as if they were written in the documents. - default_priority = 210 - - def __init__(self, document, startnode): - Transform.__init__(self, document, startnode) - self.titles = {} - - def parse_title(self, docname): - """Parse a document title as the first line starting in [A-Za-z0-9<] - or fall back to the document basename if no such line exists. - The cmake --help-*-list commands also depend on this convention. - Return the title or False if the document file does not exist. - """ - env = self.document.settings.env - title = self.titles.get(docname) - if title is None: - fname = os.path.join(env.srcdir, docname+'.rst') - try: - f = open(fname, 'r') - except IOError: - title = False - else: - for line in f: - if len(line) > 0 and (line[0].isalnum() or line[0] == '<'): - title = line.rstrip() - break - f.close() - if title is None: - title = os.path.basename(docname) - self.titles[docname] = title - return title - - def apply(self): - env = self.document.settings.env - - # Treat some documents as cmake domain objects. - objtype, sep, tail = env.docname.rpartition('/') - make_index_entry = _cmake_index_objs.get(objtype) - if make_index_entry: - title = self.parse_title(env.docname) - # Insert the object link target. - if objtype == 'command': - targetname = title.lower() - else: - targetname = title - targetid = '%s:%s' % (objtype, targetname) - targetnode = nodes.target('', '', ids=[targetid]) - self.document.note_explicit_target(targetnode) - self.document.insert(0, targetnode) - # Insert the object index entry. - indexnode = addnodes.index() - indexnode['entries'] = [make_index_entry(title, targetid)] - self.document.insert(0, indexnode) - # Add to cmake domain object inventory - _cmake_object_inventory(env, self.document, 1, objtype, targetid) - -class CMakeObject(ObjectDescription): - - def handle_signature(self, sig, signode): - # called from sphinx.directives.ObjectDescription.run() - signode += addnodes.desc_name(sig, sig) - return sig - - def add_target_and_index(self, name, sig, signode): - if self.objtype == 'command': - targetname = name.lower() - else: - targetname = name - targetid = '%s:%s' % (self.objtype, targetname) - if targetid not in self.state.document.ids: - signode['names'].append(targetid) - signode['ids'].append(targetid) - signode['first'] = (not self.names) - self.state.document.note_explicit_target(signode) - _cmake_object_inventory(self.env, self.state.document, - self.lineno, self.objtype, targetid) - - make_index_entry = _cmake_index_objs.get(self.objtype) - if make_index_entry: - self.indexnode['entries'].append(make_index_entry(name, targetid)) - -class CMakeXRefRole(XRefRole): - - # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'. - _re = re.compile(r'^(.+?)(\s*)(?$', re.DOTALL) - _re_sub = re.compile(r'^([^()\s]+)\s*\(([^()]*)\)$', re.DOTALL) - - def __call__(self, typ, rawtext, text, *args, **keys): - # Translate CMake command cross-references of the form: - # `command_name(SUB_COMMAND)` - # to have an explicit target: - # `command_name(SUB_COMMAND) ` - if typ == 'cmake:command': - m = CMakeXRefRole._re_sub.match(text) - if m: - text = '%s <%s>' % (text, m.group(1)) - # CMake cross-reference targets frequently contain '<' so escape - # any explicit `` with '<' not preceded by whitespace. - while True: - m = CMakeXRefRole._re.match(text) - if m and len(m.group(2)) == 0: - text = '%s\x00<%s>' % (m.group(1), m.group(3)) - else: - break - return XRefRole.__call__(self, typ, rawtext, text, *args, **keys) - - # We cannot insert index nodes using the result_nodes method - # because CMakeXRefRole is processed before substitution_reference - # nodes are evaluated so target nodes (with 'ids' fields) would be - # duplicated in each evaluated substitution replacement. The - # docutils substitution transform does not allow this. Instead we - # use our own CMakeXRefTransform below to add index entries after - # substitutions are completed. - # - # def result_nodes(self, document, env, node, is_ref): - # pass - -class CMakeXRefTransform(Transform): - - # Run this transform early since we insert nodes we want - # treated as if they were written in the documents, but - # after the sphinx (210) and docutils (220) substitutions. - default_priority = 221 - - def apply(self): - env = self.document.settings.env - - # Find CMake cross-reference nodes and add index and target - # nodes for them. - for ref in self.document.traverse(addnodes.pending_xref): - if not ref['refdomain'] == 'cmake': - continue - - objtype = ref['reftype'] - make_index_entry = _cmake_index_objs.get(objtype) - if not make_index_entry: - continue - - objname = ref['reftarget'] - targetnum = env.new_serialno('index-%s:%s' % (objtype, objname)) - - targetid = 'index-%s-%s:%s' % (targetnum, objtype, objname) - targetnode = nodes.target('', '', ids=[targetid]) - self.document.note_explicit_target(targetnode) - - indexnode = addnodes.index() - indexnode['entries'] = [make_index_entry(objname, targetid, '')] - ref.replace_self([indexnode, targetnode, ref]) - -class CMakeDomain(Domain): - """CMake domain.""" - name = 'cmake' - label = 'CMake' - object_types = { - 'command': ObjType('command', 'command'), - 'generator': ObjType('generator', 'generator'), - 'variable': ObjType('variable', 'variable'), - 'module': ObjType('module', 'module'), - 'policy': ObjType('policy', 'policy'), - 'prop_cache': ObjType('prop_cache', 'prop_cache'), - 'prop_dir': ObjType('prop_dir', 'prop_dir'), - 'prop_gbl': ObjType('prop_gbl', 'prop_gbl'), - 'prop_inst': ObjType('prop_inst', 'prop_inst'), - 'prop_sf': ObjType('prop_sf', 'prop_sf'), - 'prop_test': ObjType('prop_test', 'prop_test'), - 'prop_tgt': ObjType('prop_tgt', 'prop_tgt'), - 'manual': ObjType('manual', 'manual'), - } - directives = { - 'command': CMakeObject, - 'variable': CMakeObject, - # Other object types cannot be created except by the CMakeTransform - # 'generator': CMakeObject, - # 'module': CMakeObject, - # 'policy': CMakeObject, - # 'prop_cache': CMakeObject, - # 'prop_dir': CMakeObject, - # 'prop_gbl': CMakeObject, - # 'prop_inst': CMakeObject, - # 'prop_sf': CMakeObject, - # 'prop_test': CMakeObject, - # 'prop_tgt': CMakeObject, - # 'manual': CMakeObject, - } - roles = { - 'command': CMakeXRefRole(fix_parens = True, lowercase = True), - 'generator': CMakeXRefRole(), - 'variable': CMakeXRefRole(), - 'module': CMakeXRefRole(), - 'policy': CMakeXRefRole(), - 'prop_cache': CMakeXRefRole(), - 'prop_dir': CMakeXRefRole(), - 'prop_gbl': CMakeXRefRole(), - 'prop_inst': CMakeXRefRole(), - 'prop_sf': CMakeXRefRole(), - 'prop_test': CMakeXRefRole(), - 'prop_tgt': CMakeXRefRole(), - 'manual': CMakeXRefRole(), - } - initial_data = { - 'objects': {}, # fullname -> docname, objtype - } - - def clear_doc(self, docname): - to_clear = set() - for fullname, (fn, _) in self.data['objects'].items(): - if fn == docname: - to_clear.add(fullname) - for fullname in to_clear: - del self.data['objects'][fullname] - - def resolve_xref(self, env, fromdocname, builder, - typ, target, node, contnode): - targetid = '%s:%s' % (typ, target) - obj = self.data['objects'].get(targetid) - if obj is None: - # TODO: warn somehow? - return None - return make_refnode(builder, fromdocname, obj[0], targetid, - contnode, target) - - def get_objects(self): - for refname, (docname, type) in self.data['objects'].items(): - yield (refname, refname, type, docname, refname, 1) - -def setup(app): - app.add_directive('cmake-module', CMakeModule) - app.add_transform(CMakeTransform) - app.add_transform(CMakeXRefTransform) - app.add_domain(CMakeDomain) diff --git a/docs/conf.py b/docs/conf.py index af86248b..575fe4cc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,7 +41,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - 'cmake', + 'sphinxcontrib.moderncmakedomain', 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx_issues' diff --git a/docs/generators.rst b/docs/generators.rst index ef44d0bf..29db35a8 100644 --- a/docs/generators.rst +++ b/docs/generators.rst @@ -126,7 +126,7 @@ Visual Studio IDE | **3.6 and above** | Visual Studio 16 2019 | Visual Studio 16 2019 Win64 | | | Visual Studio 15 2017 | Visual Studio 15 2017 Win64 | +-------------------+------------------------+-----------------------------+ - | **3.5 ** | Visual Studio 15 2017 | Visual Studio 15 2017 Win64 | + | **3.5** | Visual Studio 15 2017 | Visual Studio 15 2017 Win64 | | | Visual Studio 14 2015 | Visual Studio 14 2015 Win64 | +-------------------+------------------------+-----------------------------+ | **3.3 to 3.4** | Visual Studio 10 2010 | Visual Studio 10 2010 Win64 | @@ -219,7 +219,7 @@ MacOSX wheels will allow them to work on `System CPython`, the `Official CPython | CPython Distribution | CPython Version | OSX Version | ``get_platform()`` [#getplat]_ | +======================+=========================+==============+================================+ | Official CPython | 3.8 | 10.13 | macosx-10.9-x86_64 | - | +-------------------------+--------------+--------------------------------| + | +-------------------------+--------------+--------------------------------+ | | 3.7, 3.6, 3.5, 3.4, 2.7 | 10.12 | macosx-**10.6**-intel | | +-------------------------+--------------+ | | | 3.4, 2.7 | 10.9 | | diff --git a/requirements-docs.txt b/requirements-docs.txt index 39f75bc5..8710c5db 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,5 +1,8 @@ -docutils +# docutils 0.17 breaks HTML tags & RTD theme +# https://github.com/sphinx-doc/sphinx/issues/9001 +docutils==0.16 # rq.filter: ==0.16 pygments -sphinx<4 # rq.filter: <4 +sphinx<5 # rq.filter: <5 sphinx-issues -sphinx_rtd_theme +sphinx-rtd-theme +sphinxcontrib-moderncmakedomain diff --git a/skbuild/resources/cmake/FindPythonExtensions.cmake b/skbuild/resources/cmake/FindPythonExtensions.cmake index a2e4515b..78d82f5f 100644 --- a/skbuild/resources/cmake/FindPythonExtensions.cmake +++ b/skbuild/resources/cmake/FindPythonExtensions.cmake @@ -201,7 +201,7 @@ # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # module2 -- dynamically linked -# include_directories({Boost_INCLUDE_DIRS}) +# include_directories(${Boost_INCLUDE_DIRS}) # add_library(module2 SHARED boost_module2.cxx) # target_link_libraries(module2 ${Boost_LIBRARIES}) # python_extension_module(module2