pax_global_header00006660000000000000000000000064146232701310014511gustar00rootroot0000000000000052 comment=b4cdb0089ad40189f4cffeab7f859265da599336 scikit-build-moderncmakedomain-b4cdb00/000077500000000000000000000000001462327013100202275ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/.github/000077500000000000000000000000001462327013100215675ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/.github/dependabot.yml000066400000000000000000000003401462327013100244140ustar00rootroot00000000000000version: 2 updates: # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" scikit-build-moderncmakedomain-b4cdb00/.github/release.yml000066400000000000000000000001141462327013100237260ustar00rootroot00000000000000changelog: exclude: authors: - dependabot - pre-commit-ci scikit-build-moderncmakedomain-b4cdb00/.github/workflows/000077500000000000000000000000001462327013100236245ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/.github/workflows/cd.yml000066400000000000000000000014251462327013100247370ustar00rootroot00000000000000name: CD on: workflow_dispatch: release: types: - published jobs: dist: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build SDist and wheel run: pipx run build - uses: actions/upload-artifact@v4 with: path: dist/* - name: Check metadata run: pipx run twine check dist/* publish: needs: [dist] runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' environment: name: pypi url: https://pypi.org/p/sphinxcontrib-moderncmakedomain permissions: id-token: write steps: - uses: actions/download-artifact@v4 with: name: artifact path: dist - uses: pypa/gh-action-pypi-publish@release/v1 scikit-build-moderncmakedomain-b4cdb00/.github/workflows/ci.yml000066400000000000000000000021401462327013100247370ustar00rootroot00000000000000name: CI on: workflow_dispatch: pull_request: push: branches: - main concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: FORCE_COLOR: 3 jobs: check-package: name: Build & inspect runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: hynek/build-and-inspect-python-package@v2 test: name: Run quick tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup uv uses: yezz123/setup-uv@v4 - uses: wntrblm/nox@2024.04.15 with: python-versions: "3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13" - name: Test Python 3.7 run: nox -s tests -P 3.7 - name: Test Python 3.8 run: nox -s tests -P 3.8 - name: Test Python 3.9 run: nox -s tests -P 3.9 - name: Test Python 3.10 run: nox -s tests -P 3.10 - name: Test Python 3.11 run: nox -s tests -P 3.11 - name: Test Python 3.12 run: nox -s tests -P 3.13 - name: Test Python 3.13 run: nox -s tests -P 3.13 scikit-build-moderncmakedomain-b4cdb00/.gitignore000066400000000000000000000060371462327013100222250ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ # OS-specific .DS_Store scikit-build-moderncmakedomain-b4cdb00/.pre-commit-config.yaml000066400000000000000000000023221462327013100245070ustar00rootroot00000000000000ci: autoupdate_commit_msg: "chore: update pre-commit hooks" autofix_commit_msg: "style: pre-commit fixes" repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: "v4.6.0" hooks: - id: check-added-large-files - id: check-case-conflict - id: check-merge-conflict - id: check-symlinks - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - id: name-tests-test args: ["--pytest-test-first"] exclude: "^tests/roots" - id: requirements-txt-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.4.4 hooks: - id: ruff args: ["--fix", "--show-fixes"] exclude: "$sphinxcontrib/moderncmakedomain/(cmake|colors).py$" - id: ruff-format exclude: "$sphinxcontrib/moderncmakedomain/(cmake|colors).py$" - repo: https://github.com/henryiii/validate-pyproject-schema-store rev: 2024.05.13 hooks: - id: validate-pyproject - repo: https://github.com/python-jsonschema/check-jsonschema rev: 0.28.4 hooks: - id: check-dependabot - id: check-github-workflows - id: check-readthedocs scikit-build-moderncmakedomain-b4cdb00/LICENSE000066400000000000000000000030721462327013100212360ustar00rootroot00000000000000CMake - Cross Platform Makefile Generator Copyright 2000-2018 Kitware, Inc. and Contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Kitware, Inc. nor the names of Contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- See https://cmake.org/licensing for more details scikit-build-moderncmakedomain-b4cdb00/README.md000066400000000000000000000120111462327013100215010ustar00rootroot00000000000000# Sphinx Domain for Modern CMake This is taken directly from the Kitware git repository's Utilities directory. The original [sphinxcontrib-cmakedomain][] has not been touched in quite some and as a result it was wildly out of date. Documenting CMake domain entities in projects is painful otherwise. This works *exactly* in the same way as Kitware, so some time might be needed to study their approach to these problems. This repository is under the same License as all of CMake, which is the BSD-3-Clause license. 🚨🚨🚨 Any issues you run into with this plugin must be reported to [Kitware][], unless they involve the packaging itself. The Python files exactly match the CMake source for the released version numbers. 🚨🚨🚨 # Installation ## PyPI This domain is available via PyPI. Install it directly via `pip`: ``` $ pip install sphinxcontrib-moderncmakedomain ``` Alternatively, place it inside of your `setup.py`, `pyproject.toml`, `requirements.txt` or whatever system it is that you use to declare and manage your dependencies. A new version will usually only be released if there is a change to this extension inside CMake. ## Git This module is installable via `pip` and GitHub directly as well ``` $ pip install git+https://github.com/scikit-build/moderncmakedomain.git ``` # Usage To enable the use of the `moderncmakedomain`, add `sphinxcontrib.moderncmakedomain` to the `extensions` variable of your `conf.py` file: ```python extensions = [..., 'sphinxcontrib.moderncmakedomain', ...] ``` The plugin currently provides several directives and references. These are documented below. ## Directives | directive | description | |:------------------:|:----------------------------------------------------| | `cmake:variable::` | For a basic variable | | `cmake:command::` | For a function | | `cmake-module::` | Autodoc style extractor (takes a relative filepath) | | `cmake:envvar::` | For environment variables | To declare any of the references found below, they must be placed into a directory with the same name under the sphinx SOURCEDIR/master doc. Thus, `prop_tgt/MY_PERSONAL_PROPERTY.rst` can be referred to with ``:prop_tgt:`MY_PERSONAL_PROPERTY` ``. This is currently the *only* way CMake permits declaring new properties. ## References Each reference below can be placed into a directory with the same name to document custom extensions provided by your CMake libraries. | ref | description | |:--------------:|:---------------------------------------------------| | `:variable:` | Refer to a CMake variable | | `:command:` | Refer to a CMake command | | `:envvar:` | Refers to an environment variable | | `:cpack_gen:` | Refers to CPack generators | | `:generator:` | Refers to a build file generator | | `:genex:` | Refers to a generator expression | | `:guide:` | Used to refer to a "guide" page | | `:manual:` | Used to refer to a "manual" page (like `cmake(1)`) | | `:policy:` | Refers to CMake Policies | | `:module:` | Refers to CMake Modules | | `:prop_tgt:` | For target properties | | `:prop_test:` | For test properties | | `:prop_sf:` | For source file properties | | `:prop_gbl:` | For global properties | | `:prop_dir:` | For directory properties | | `:prop_inst:` | For installed file properties | | `:prop_cache:` | For cache properties | # History `sphinx-moderncmakedomain` was initially developed in October 2018 by [slurps-mad-rips][slurps-mad-rips] to help write CMake documentation by simply publishing a python package of the same. This was a critical step to ease the maintenance of sphinx-based documentation and avoid systematically copying the associated python module maintained within the CMake repository. Later in early August 2021, [henryiii][henryiii] discovered the `sphinx-moderncmakedomain` project while working on scikit-build issue [#574][skbuild-issue-574] intended to simplify its documentation generation infrastructure and avoid updating its own copy of the sphinx extension. [henryiii][henryiii] and [jcfr][jcfr] then worked with [slurps-mad-rips][slurps-mad-rips] to establish a transition plan to collaboratively maintain the project within the scikit-build organization. [sphinxcontrib-cmakedomain]: https://github.com/sphinx-contrib/cmakedomain [Kitware]: https://gitlab.kitware.com/ [skbuild-issue-574]: https://github.com/scikit-build/scikit-build/pull/574 [slurps-mad-rips]: https://github.com/slurps-mad-rips [henryiii]: https://github.com/henryiii [jcfr]: https://github.com/jcfr scikit-build-moderncmakedomain-b4cdb00/noxfile.py000066400000000000000000000034421462327013100222500ustar00rootroot00000000000000import re import urllib.request from pathlib import Path import nox nox.needs_version = ">=2024.3.2" nox.options.sessions = ["lint", "tests"] nox.options.default_venv_backend = "uv|virtualenv" @nox.session def lint(session: nox.Session) -> None: """ Run the linter. """ session.install("pre-commit") session.run( "pre-commit", "run", "--all-files", "--hook-stage=manual", *session.posargs ) @nox.session def build(session: nox.Session) -> None: """ Build an SDist and wheel. """ session.install("build") session.run("python", "-m", "build") @nox.session def update(session: nox.Session) -> None: """ Get the latest (or given) version of CMake and update the copy with it. """ if session.posargs: (version,) = session.posargs else: session.install("lastversion") version = session.run( "lastversion", "kitware/cmake", log=False, silent=True ).strip() session.log(f"CMake {version}") cmake_url = f"https://raw.githubusercontent.com/Kitware/CMake/v{version}/Utilities/Sphinx/cmake.py" colors_url = f"https://raw.githubusercontent.com/Kitware/CMake/v{version}/Utilities/Sphinx/colors.py" urllib.request.urlretrieve(cmake_url, "sphinxcontrib/moderncmakedomain/cmake.py") urllib.request.urlretrieve(colors_url, "sphinxcontrib/moderncmakedomain/colors.py") init_file = Path("sphinxcontrib/moderncmakedomain/__init__.py") txt = init_file.read_text(encoding="utf_8") txt_new = re.sub(r'__version__ = ".*"', f'__version__ = "{version}"', txt) init_file.write_text(txt_new, encoding="utf_8") @nox.session def tests(session): """ Run the unit and regular tests. """ session.install(".[test]", silent=False) session.run("pytest", *session.posargs) scikit-build-moderncmakedomain-b4cdb00/pyproject.toml000066400000000000000000000035301462327013100231440ustar00rootroot00000000000000[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "sphinxcontrib-moderncmakedomain" description = "Sphinx Domain for Modern CMake" readme = "README.md" keywords = [ "cmake", "documentation", "kitware", "sphinx", "sphinxcontrib", ] authors = [ { name = "Kitware" }, ] requires-python = ">=3.7" classifiers = [ "Environment :: Console", "Environment :: Web Environment", "Framework :: Sphinx :: Domain", "Framework :: Sphinx :: Extension", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Documentation", "Topic :: Utilities", ] dynamic = ["version"] dependencies = [ "sphinx>=2", ] [project.optional-dependencies] test = [ "defusedxml", "pytest", ] [project.urls] Homepage = "https://github.com/scikit-build/moderncmakedomain" [tool.hatch] version.path = "sphinxcontrib/moderncmakedomain/__init__.py" build.targets.wheel.packages = ["sphinxcontrib"] [tool.ruff] exclude = [ "sphinxcontrib/moderncmakedomain/cmake.py", "sphinxcontrib/moderncmakedomain/colors.py", ] lint.extend-select = [ "B", # flake8-bugbear "RUF", # Ruff-specific "I", # isort "UP", # pyupgrade ] [tool.pytest.ini_options] minversion = "6.0" addopts = [ "-ra", "--showlocals", "--strict-markers", "--strict-config", ] xfail_strict = true filterwarnings = [ "error", "ignore::DeprecationWarning:sphinx.builders.gettext", ] log_cli_level = "info" testpaths = ["tests"] scikit-build-moderncmakedomain-b4cdb00/sphinxcontrib/000077500000000000000000000000001462327013100231215ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/sphinxcontrib/moderncmakedomain/000077500000000000000000000000001462327013100265765ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/sphinxcontrib/moderncmakedomain/__init__.py000066400000000000000000000001251462327013100307050ustar00rootroot00000000000000from .cmake import setup __version__ = "3.29.0" __all__ = ["__version__", "setup"] scikit-build-moderncmakedomain-b4cdb00/sphinxcontrib/moderncmakedomain/cmake.py000066400000000000000000000654701462327013100302440ustar00rootroot00000000000000# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. # BEGIN imports import os import re from dataclasses import dataclass from typing import Any, List, Tuple, Type, cast import sphinx # The following imports may fail if we don't have Sphinx 2.x or later. if sphinx.version_info >= (2,): from docutils import io, nodes from docutils.nodes import Element, Node, TextElement, system_message from docutils.parsers.rst import Directive, directives from docutils.transforms import Transform from docutils.utils.code_analyzer import Lexer, LexerError from sphinx import addnodes from sphinx.directives import ObjectDescription, nl_escape_re from sphinx.domains import Domain, ObjType from sphinx.roles import XRefRole from sphinx.util import logging, ws_re from sphinx.util.docutils import ReferenceRole from sphinx.util.nodes import make_refnode else: # Sphinx 2.x is required. assert sphinx.version_info >= (2,) # END imports # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # BEGIN pygments tweaks # Override much of pygments' CMakeLexer. # We need to parse CMake syntax definitions, not CMake code. # For hard test cases that use much of the syntax below, see # - module/FindPkgConfig.html # (with "glib-2.0>=2.10 gtk+-2.0" and similar) # - module/ExternalProject.html # (with http:// https:// git@; also has command options -E --build) # - manual/cmake-buildsystem.7.html # (with nested $<..>; relative and absolute paths, "::") from pygments.lexer import bygroups # noqa I100 from pygments.lexers import CMakeLexer from pygments.token import (Comment, Name, Number, Operator, Punctuation, String, Text, Whitespace) # Notes on regular expressions below: # - [\.\+-] are needed for string constants like gtk+-2.0 # - Unix paths are recognized by '/'; support for Windows paths may be added # if needed # - (\\.) allows for \-escapes (used in manual/cmake-language.7) # - $<..$<..$>..> nested occurrence in cmake-buildsystem # - Nested variable evaluations are only supported in a limited capacity. # Only one level of nesting is supported and at most one nested variable can # be present. CMakeLexer.tokens["root"] = [ # fctn( (r'\b(\w+)([ \t]*)(\()', bygroups(Name.Function, Text, Name.Function), '#push'), (r'\(', Name.Function, '#push'), (r'\)', Name.Function, '#pop'), (r'\[', Punctuation, '#push'), (r'\]', Punctuation, '#pop'), (r'[|;,.=*\-]', Punctuation), # used in commands/source_group (r'\\\\', Punctuation), (r'[:]', Operator), # used in FindPkgConfig.cmake (r'[<>]=', Punctuation), # $<...> (r'\$<', Operator, '#push'), # (r'<[^<|]+?>(\w*\.\.\.)?', Name.Variable), # ${..} $ENV{..}, possibly nested (r'(\$\w*\{)([^\}\$]*)?(?:(\$\w*\{)([^\}]+?)(\}))?([^\}]*?)(\})', bygroups(Operator, Name.Tag, Operator, Name.Tag, Operator, Name.Tag, Operator)), # DATA{ ...} (r'([A-Z]+\{)(.+?)(\})', bygroups(Operator, Name.Tag, Operator)), # URL, git@, ... (r'[a-z]+(@|(://))((\\.)|[\w.+-:/\\])+', Name.Attribute), # absolute path (r'/\w[\w\.\+-/\\]*', Name.Attribute), (r'/', Name.Attribute), # relative path (r'\w[\w\.\+-]*/[\w.+-/\\]*', Name.Attribute), # initial A-Z, contains a-z (r'[A-Z]((\\.)|[\w.+-])*[a-z]((\\.)|[\w.+-])*', Name.Builtin), (r'@?[A-Z][A-Z0-9_]*', Name.Constant), (r'[a-z_]((\\;)|(\\ )|[\w.+-])*', Name.Builtin), (r'[0-9][0-9\.]*', Number), # "string" (r'(?s)"(\\"|[^"])*"', String), (r'\.\.\.', Name.Variable), # <..|..> is different from (r'<', Operator, '#push'), (r'>', Operator, '#pop'), (r'\n', Whitespace), (r'[ \t]+', Whitespace), (r'#.*\n', Comment), # fallback, for debugging only # (r'[^<>\])\}\|$"# \t\n]+', Name.Exception), ] # END pygments tweaks # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% logger = logging.getLogger(__name__) # RE to split multiple command signatures. sig_end_re = re.compile(r'(?<=[)])\n') @dataclass class ObjectEntry: docname: str objtype: str node_id: str name: str 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(f'{self.name!r} directive disabled.') 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: msg = (f'Problems with {self.name!r} directive path:\n' f'Cannot encode input file path {path!r} (wrong locale?).') raise self.severe(msg) except IOError as error: msg = f'Problems with {self.name!r} directive path:\n{error}.' raise self.severe(msg) 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 = f']{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(f'{self.name!r} found unclosed bracket ' f'"#[{rst[1:-1]}[.rst:" in {path!r}') 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', f'{self.desc} ; {title}', targetid, main, None) _cmake_index_objs = { 'command': _cmake_index_entry('command'), 'cpack_gen': _cmake_index_entry('cpack generator'), 'envvar': _cmake_index_entry('envvar'), 'generator': _cmake_index_entry('generator'), 'genex': _cmake_index_entry('genex'), 'guide': _cmake_index_entry('guide'), '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'), } 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. """ settings = self.document.settings env = settings.env title = self.titles.get(docname) if title is None: fname = os.path.join(env.srcdir, docname+'.rst') try: f = open(fname, 'r', encoding=settings.input_encoding) except IOError: title = False else: for line in f: if len(line) > 0 and (line[0].isalnum() or line[0] == '<' 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.partition('/') 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() elif objtype == 'guide' and not tail.endswith('/index'): targetname = tail else: if objtype == 'genex': m = CMakeXRefRole._re_genex.match(title) if m: title = m.group(1) targetname = title targetid = f'{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 domain = cast(CMakeDomain, env.get_domain('cmake')) domain.note_object(objtype, targetname, targetid, targetid) class CMakeObject(ObjectDescription): def __init__(self, *args, **kwargs): self.targetname = None super().__init__(*args, **kwargs) 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() elif self.targetname: targetname = self.targetname else: targetname = name targetid = f'{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) domain = cast(CMakeDomain, self.env.get_domain('cmake')) domain.note_object(self.objtype, targetname, targetid, targetid, location=signode) make_index_entry = _cmake_index_objs.get(self.objtype) if make_index_entry: self.indexnode['entries'].append(make_index_entry(name, targetid)) class CMakeGenexObject(CMakeObject): option_spec = { 'target': directives.unchanged, } def handle_signature(self, sig, signode): name = super().handle_signature(sig, signode) m = CMakeXRefRole._re_genex.match(sig) if m: name = m.group(1) return name def run(self): target = self.options.get('target') if target is not None: self.targetname = target return super().run() class CMakeSignatureObject(CMakeObject): object_type = 'signature' BREAK_ALL = 'all' BREAK_SMART = 'smart' BREAK_VERBATIM = 'verbatim' BREAK_CHOICES = {BREAK_ALL, BREAK_SMART, BREAK_VERBATIM} def break_option(argument): return directives.choice(argument, CMakeSignatureObject.BREAK_CHOICES) option_spec = { 'target': directives.unchanged, 'break': break_option, } def _break_signature_all(sig: str) -> str: return ws_re.sub(' ', sig) def _break_signature_verbatim(sig: str) -> str: lines = [ws_re.sub('\xa0', line.strip()) for line in sig.split('\n')] return ' '.join(lines) def _break_signature_smart(sig: str) -> str: tokens = [] for line in sig.split('\n'): token = '' delim = '' for c in line.strip(): if len(delim) == 0 and ws_re.match(c): if len(token): tokens.append(ws_re.sub('\xa0', token)) token = '' else: if c == '[': delim += ']' elif c == '<': delim += '>' elif len(delim) and c == delim[-1]: delim = delim[:-1] token += c if len(token): tokens.append(ws_re.sub('\xa0', token)) return ' '.join(tokens) def __init__(self, *args, **kwargs): self.targetnames = {} self.break_style = CMakeSignatureObject.BREAK_SMART super().__init__(*args, **kwargs) def get_signatures(self) -> List[str]: content = nl_escape_re.sub('', self.arguments[0]) lines = sig_end_re.split(content) if self.break_style == CMakeSignatureObject.BREAK_VERBATIM: fixup = CMakeSignatureObject._break_signature_verbatim elif self.break_style == CMakeSignatureObject.BREAK_SMART: fixup = CMakeSignatureObject._break_signature_smart else: fixup = CMakeSignatureObject._break_signature_all return [fixup(line.strip()) for line in lines] def handle_signature(self, sig, signode): language = 'cmake' classes = ['code', 'cmake', 'highlight'] node = addnodes.desc_name(sig, '', classes=classes) try: tokens = Lexer(sig, language, 'short') except LexerError as error: if self.state.document.settings.report_level > 2: # Silently insert without syntax highlighting. tokens = Lexer(sig, language, 'none') else: raise self.warning(error) for classes, value in tokens: if value == '\xa0': node += nodes.inline(value, value, classes=['nbsp']) elif classes: node += nodes.inline(value, value, classes=classes) else: node += nodes.Text(value) signode.clear() signode += node return sig def add_target_and_index(self, name, sig, signode): sig = sig.replace('\xa0', ' ') if sig in self.targetnames: sigargs = self.targetnames[sig] else: def extract_keywords(params): for p in params: if p[0].isalpha(): yield p else: return keywords = extract_keywords(sig.split('(')[1].split()) sigargs = ' '.join(keywords) targetname = sigargs.lower() targetid = nodes.make_id(targetname) if targetid not in self.state.document.ids: signode['names'].append(targetname) signode['ids'].append(targetid) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) # Register the signature as a command object. command = sig.split('(')[0].lower() refname = f'{command}({sigargs})' refid = f'command:{command}({targetname})' domain = cast(CMakeDomain, self.env.get_domain('cmake')) domain.note_object('command', name=refname, target_id=refid, node_id=targetid, location=signode) def run(self): self.break_style = CMakeSignatureObject.BREAK_ALL targets = self.options.get('target') if targets is not None: signatures = self.get_signatures() targets = [t.strip() for t in targets.split('\n')] for signature, target in zip(signatures, targets): self.targetnames[signature] = target self.break_style = ( self.options.get('break', CMakeSignatureObject.BREAK_SMART)) return super().run() class CMakeReferenceRole: # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'. _re = re.compile(r'^(.+?)(\s*)(?$', re.DOTALL) @staticmethod def _escape_angle_brackets(text: str) -> str: # CMake cross-reference targets frequently contain '<' so escape # any explicit `` with '<' not preceded by whitespace. while True: m = CMakeReferenceRole._re.match(text) if m and len(m.group(2)) == 0: text = f'{m.group(1)}\x00<{m.group(3)}>' else: break return text def __class_getitem__(cls, parent: Any): class Class(parent): def __call__(self, name: str, rawtext: str, text: str, *args, **kwargs ) -> Tuple[List[Node], List[system_message]]: text = CMakeReferenceRole._escape_angle_brackets(text) return super().__call__(name, rawtext, text, *args, **kwargs) return Class class CMakeCRefRole(CMakeReferenceRole[ReferenceRole]): nodeclass: Type[Element] = nodes.reference innernodeclass: Type[TextElement] = nodes.literal classes: List[str] = ['cmake', 'literal'] def run(self) -> Tuple[List[Node], List[system_message]]: refnode = self.nodeclass(self.rawtext) self.set_source_info(refnode) refnode['refid'] = nodes.make_id(self.target) refnode += self.innernodeclass(self.rawtext, self.title, classes=self.classes) return [refnode], [] class CMakeXRefRole(CMakeReferenceRole[XRefRole]): _re_sub = re.compile(r'^([^()\s]+)\s*\(([^()]*)\)$', re.DOTALL) _re_genex = re.compile(r'^\$<([^<>:]+)(:[^<>]+)?>$', re.DOTALL) _re_guide = re.compile(r'^([^<>/]+)/([^<>]*)$', re.DOTALL) def __call__(self, typ, rawtext, text, *args, **kwargs): if typ == 'cmake:command': # Translate a CMake command cross-reference of the form: # `command_name(SUB_COMMAND)` # to be its own explicit target: # `command_name(SUB_COMMAND) ` # so the XRefRole `fix_parens` option does not add more `()`. m = CMakeXRefRole._re_sub.match(text) if m: text = f'{text} <{text}>' elif typ == 'cmake:genex': m = CMakeXRefRole._re_genex.match(text) if m: text = f'{text} <{m.group(1)}>' elif typ == 'cmake:guide': m = CMakeXRefRole._re_guide.match(text) if m: text = f'{m.group(2)} <{text}>' return super().__call__(typ, rawtext, text, *args, **kwargs) # 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 # This helper supports docutils < 0.18, which is missing 'findall', # and docutils == 0.18.0, which is missing 'traverse'. def _document_findall_as_list(self, condition): if hasattr(self.document, 'findall'): # Fully iterate into a list so the caller can grow 'self.document' # while iterating. return list(self.document.findall(condition)) # Fallback to 'traverse' on old docutils, which returns a list. return self.document.traverse(condition) 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_findall_as_list(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'] if objtype == 'guide' and CMakeXRefRole._re_guide.match(objname): # Do not index cross-references to guide sections. continue if objtype == 'command': # Index signature references to their parent command. objname = objname.split('(')[0].lower() targetnum = env.new_serialno(f'index-{objtype}:{objname}') targetid = f'index-{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'), 'cpack_gen': ObjType('cpack_gen', 'cpack_gen'), 'envvar': ObjType('envvar', 'envvar'), 'generator': ObjType('generator', 'generator'), 'genex': ObjType('genex', 'genex'), 'guide': ObjType('guide', 'guide'), '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, 'envvar': CMakeObject, 'genex': CMakeGenexObject, 'signature': CMakeSignatureObject, 'variable': CMakeObject, # Other `object_types` cannot be created except by the `CMakeTransform` } roles = { 'cref': CMakeCRefRole(), 'command': CMakeXRefRole(fix_parens=True, lowercase=True), 'cpack_gen': CMakeXRefRole(), 'envvar': CMakeXRefRole(), 'generator': CMakeXRefRole(), 'genex': CMakeXRefRole(), 'guide': 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 -> ObjectEntry } def clear_doc(self, docname): to_clear = set() for fullname, obj in self.data['objects'].items(): if obj.docname == docname: to_clear.add(fullname) for fullname in to_clear: del self.data['objects'][fullname] def merge_domaindata(self, docnames, otherdata): """Merge domaindata from the workers/chunks when they return. Called once per parallelization chunk. Only used when sphinx is run in parallel mode. :param docnames: a Set of the docnames that are part of the current chunk to merge :param otherdata: the partial data calculated by the current chunk """ for refname, obj in otherdata['objects'].items(): if obj.docname in docnames: self.data['objects'][refname] = obj def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): targetid = f'{typ}:{target}' obj = self.data['objects'].get(targetid) if obj is None and typ == 'command': # If 'command(args)' wasn't found, try just 'command'. # TODO: remove this fallback? warn? # logger.warning(f'no match for {targetid}') command = target.split('(')[0] targetid = f'{typ}:{command}' obj = self.data['objects'].get(targetid) if obj is None: # TODO: warn somehow? return None return make_refnode(builder, fromdocname, obj.docname, obj.node_id, contnode, target) def note_object(self, objtype: str, name: str, target_id: str, node_id: str, location: Any = None): if target_id in self.data['objects']: other = self.data['objects'][target_id].docname logger.warning( f'CMake object {target_id!r} also described in {other!r}', location=location) self.data['objects'][target_id] = ObjectEntry( self.env.docname, objtype, node_id, name) def get_objects(self): for refname, obj in self.data['objects'].items(): yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1) def setup(app): app.add_directive('cmake-module', CMakeModule) app.add_transform(CMakeTransform) app.add_transform(CMakeXRefTransform) app.add_domain(CMakeDomain) return {"parallel_read_safe": True} scikit-build-moderncmakedomain-b4cdb00/sphinxcontrib/moderncmakedomain/colors.py000066400000000000000000000022371462327013100304550ustar00rootroot00000000000000# -*- coding: utf-8 -*- from pygments.style import Style from pygments.token import Name, Comment, String, Number, Operator, Whitespace class CMakeTemplateStyle(Style): """ for more token names, see pygments/styles.default """ background_color = "#f8f8f8" default_style = "" styles = { Whitespace: "#bbbbbb", Comment: "italic #408080", Operator: "#555555", String: "#217A21", Number: "#105030", Name.Builtin: "#333333", # anything lowercase Name.Function: "#007020", # function Name.Variable: "#1080B0", # <..> Name.Tag: "#bb60d5", # ${..} Name.Constant: "#4070a0", # uppercase only Name.Entity: "italic #70A020", # @..@ Name.Attribute: "#906060", # paths, URLs Name.Label: "#A0A000", # anything left over Name.Exception: "bold #FF0000", # for debugging only } scikit-build-moderncmakedomain-b4cdb00/tests/000077500000000000000000000000001462327013100213715ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/tests/conftest.py000066400000000000000000000011271462327013100235710ustar00rootroot00000000000000import sys import pytest if sys.version_info < (3, 8): from importlib_metadata import version else: from importlib.metadata import version sphinx_vesion = tuple(int(d) for d in version("sphinx").split(".")[:2]) pytest_plugins = "sphinx.testing.fixtures" if sphinx_vesion < (7, 3): from sphinx.testing.path import path @pytest.fixture(scope="session") def rootdir(): return path(__file__).parent.abspath() / "roots" else: from pathlib import Path @pytest.fixture(scope="session") def rootdir(): return Path(__file__).parent.absolute() / "roots" scikit-build-moderncmakedomain-b4cdb00/tests/roots/000077500000000000000000000000001462327013100225375ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/000077500000000000000000000000001462327013100244775ustar00rootroot00000000000000scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/conf.py000066400000000000000000000000611462327013100257730ustar00rootroot00000000000000extensions = ["sphinxcontrib.moderncmakedomain"] scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/external.rst000066400000000000000000000001141462327013100270470ustar00rootroot00000000000000External -------- An external reference is :cmake:command:`find_program`. scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/index.rst000066400000000000000000000001021462327013100263310ustar00rootroot00000000000000.. toctree:: local external parallel padding more scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/local.rst000066400000000000000000000003371462327013100263260ustar00rootroot00000000000000Local ----- Some CMake ========== .. cmake:variable:: MYVAR .. cmake:variable:: MYVAR2 .. cmake:command:: somecommand Some references =============== Using :cmake:variable:`MYVAR`. Also :cmake:command:`somecommand`. scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/more.rst000066400000000000000000000000611462327013100261700ustar00rootroot00000000000000More ---- Even more padding to get to 5 pages. scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/padding.rst000066400000000000000000000001141462327013100266330ustar00rootroot00000000000000Padding ------- Sphinx requires 5 or more pages to enable parallel builds. scikit-build-moderncmakedomain-b4cdb00/tests/roots/test-root/parallel.rst000066400000000000000000000002751462327013100270310ustar00rootroot00000000000000 Parallel -------- An extra file to make sure parallel is tested. Some CMake ========== .. cmake:variable:: OTHERVAR Some references =============== Using :cmake:variable:`OTHERVAR`. scikit-build-moderncmakedomain-b4cdb00/tests/test_basic.py000066400000000000000000000015341462327013100240660ustar00rootroot00000000000000import pytest @pytest.mark.parametrize("parallel", [0, 1, 2]) @pytest.mark.sphinx( "html", freshenv=True, confoverrides={"html_baseurl": "https://example.org/docs/", "language": "en"}, ) def test_simple_html(app, status, warning, parallel): app.warningiserror = True app.parallel = parallel app.build() local_pth = app.outdir / "local.html" external_pth = app.outdir / "external.html" with open(str(local_pth), encoding="utf-8") as f: local = f.read() with open(str(external_pth), encoding="utf-8") as f: external = f.read() assert 'href="#variable:MYVAR"' in local assert 'id="variable:MYVAR"' in local assert 'id="index-0-command:find_program"' in external assert "find_program()" in external assert 'class="xref cmake cmake-command docutils literal notranslate"' in external scikit-build-moderncmakedomain-b4cdb00/tests/test_version.py000066400000000000000000000002751462327013100244730ustar00rootroot00000000000000"""Version tests.""" from sphinxcontrib.moderncmakedomain import __version__ def test_version(): """Test that version has at least 3 parts.""" assert __version__.count(".") >= 2