Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# A full build prints roughly three thousand lines of Doxygen and Sphinx
# progress, which buries the handful of warnings we actually care about. So we
# build quietly by default: -q drops Sphinx's progress log while leaving
# warnings and errors on stderr, and conf.py passes the same flag to Doxygen.
# Set DOCS_VERBOSE=1 when a build misbehaves and you want the play-by-play:
#
# make html DOCS_VERBOSE=1
#
ifeq ($(DOCS_VERBOSE),)
QUIET = -q
endif

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Expand All @@ -16,15 +28,15 @@ help:

clean:
rm -rf source/api/
@$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(QUIET) $(SPHINXOPTS) $(O)

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(QUIET) $(SPHINXOPTS) $(O)

xml:
cd "$(SOURCEDIR)" && doxygen Doxyfile
cd "$(SOURCEDIR)" && doxygen $(QUIET) Doxyfile

livehtml:
sphinx-autobuild --host 0.0.0.0 -j auto -a -q "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) $(O)
9 changes: 8 additions & 1 deletion docs/make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ if "%SPHINXBUILD%" == "" (
set SOURCEDIR=source
set BUILDDIR=build

REM Build quietly by default so only warnings and errors reach the console.
REM Set DOCS_VERBOSE=1 to get Sphinx's and Doxygen's progress logs back.
set QUIET=-q
if not "%DOCS_VERBOSE%" == "" (
set QUIET=
)

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
Expand All @@ -25,7 +32,7 @@ if errorlevel 9009 (
exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %QUIET% %SPHINXOPTS% %O%
goto end

:help
Expand Down
3 changes: 2 additions & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ myst_parser
sphinxcontrib-bibtex
sphinxemoji
sphinx-last-updated-by-git
sphinx-autobuild
sphinx-autobuild
yaspin
8 changes: 4 additions & 4 deletions docs/source/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -2274,15 +2274,15 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.

MACRO_EXPANSION = NO
MACRO_EXPANSION = YES

# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
# EXPAND_AS_DEFINED tags.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.

EXPAND_ONLY_PREDEF = NO
EXPAND_ONLY_PREDEF = YES

# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
Expand Down Expand Up @@ -2315,7 +2315,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.

PREDEFINED = IPC_TOOLKIT_WITH_CUDA IPC_TOOLKIT_WITH_INEXACT_CCD IPC_TOOLKIT_WITH_ROBIN_MAP IPC_TOOLKIT_WITH_ABSEIL IPC_TOOLKIT_WITH_FILIB IPC_TOOLKIT_WITH_MESHFEM_SPARSE
PREDEFINED = IPC_TOOLKIT_WITH_CUDA IPC_TOOLKIT_WITH_INEXACT_CCD IPC_TOOLKIT_WITH_ROBIN_MAP IPC_TOOLKIT_WITH_ABSEIL IPC_TOOLKIT_WITH_FILIB IPC_TOOLKIT_WITH_MESHFEM_SPARSE IPC_TOOLKIT_HOST_DEVICE=

# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
Expand Down Expand Up @@ -2678,7 +2678,7 @@ PLANTUML_INCLUDE_PATH =
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.

DOT_GRAPH_MAX_NODES = 50
DOT_GRAPH_MAX_NODES = 100

# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
Expand Down
217 changes: 212 additions & 5 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,87 @@
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

import contextlib
import io
import logging
import os
import pathlib
import subprocess
import sys

# A full build narrates itself for a few thousand lines, which buries the
# handful of warnings worth acting on. We keep it quiet by default and let
# DOCS_VERBOSE=1 bring the whole log back when a build needs debugging.
VERBOSE = bool(os.environ.get("DOCS_VERBOSE"))


def quiet_stdout():
"""Swallow stdout from code that writes to it directly.

Sphinx's own -q only silences messages that go through its status stream,
so anything using a bare print() needs to be wrapped by hand.
"""
if VERBOSE:
return contextlib.nullcontext()
return contextlib.redirect_stdout(io.StringIO())


# -- Progress reporting ------------------------------------------------------
# A quiet build says nothing for the half minute or so it runs, which looks
# exactly like a hung one. So each slow stage spins a single self-erasing line
# naming what it is doing, and stamps that line with a result when it finishes.
#
# This only happens on an interactive terminal. Redirected output (CI, a log
# file) stays as quiet as it was, and a verbose build skips the spinner because
# its own logs already show progress.
try:
from yaspin import yaspin
except ImportError: # the docs still build without it, just without progress
yaspin = None

SHOW_PROGRESS = yaspin is not None and not VERBOSE and sys.stdout.isatty()


class SpinnerSafeStream:
"""Keep the spinner from scribbling over whatever is written to a stream.

The spinner owns the last line of the terminal and redraws it on a timer,
so a warning written underneath lands in the middle of that line. We clear
the spinner first, write, then let it resume on a fresh line.
"""

def __init__(self, stream, spinner):
self._stream = stream
self._spinner = spinner

def write(self, text):
try:
self._spinner.hide()
written = self._stream.write(text)
self._stream.flush()
finally:
self._spinner.show()
return written

def __getattr__(self, name):
return getattr(self._stream, name)


@contextlib.contextmanager
def stage(text):
"""Spin while one build stage runs, then stamp the line with its result."""
if not SHOW_PROGRESS:
yield None
return
with yaspin(text=text, color="yellow") as spinner:
try:
yield spinner
except BaseException:
spinner.fail("\U0001f4a5 ")
raise
spinner.ok("\u2705 ")


# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
Expand All @@ -20,20 +97,36 @@
from datetime import datetime

sys.path.append(str(pathlib.Path(__file__).parents[2] / "python"))
from _find_ipctk import ipctk # noqa
with quiet_stdout():
from _find_ipctk import ipctk # noqa

project = "IPC Toolkit"
copyright = f"2020-{datetime.now().year}, IPC-Sim Organization; MIT License"
copyright = f"2020-{datetime.now().year}, Zachary Ferguson; MIT License"
author = "Zachary Ferguson"
version = ipctk.__version__

# -- General configuration ---------------------------------------------------

# Doxygen
# -q silences Doxygen's per-file progress. Its warnings go to stderr and are
# unaffected, so we still hear about anything that is actually wrong. While the
# spinner is up we hold those warnings in a pipe and replay them once the
# spinner has cleared its line, so they arrive intact rather than interleaved.
pathlib.Path("../build/doxyoutput").mkdir(parents=True, exist_ok=True)
if not subprocess.run(["doxygen", "Doxyfile"]):
print("Doxygen failed! Exiting")
exit(1)
doxygen_flags = [] if VERBOSE else ["-q"]

with stage("Doxygen") as doxygen_spinner:
doxygen = subprocess.run(
["doxygen", *doxygen_flags, "Doxyfile"],
stderr=subprocess.PIPE if doxygen_spinner is not None else None,
)
if doxygen.stderr:
doxygen_spinner.hide()
sys.stderr.buffer.write(doxygen.stderr)
sys.stderr.flush()
doxygen_spinner.show()
if doxygen.returncode != 0:
raise SystemExit("Doxygen failed! Exiting")

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named "sphinx.ext.*") or your custom
Expand Down Expand Up @@ -63,6 +156,27 @@
"sphinx_last_updated_by_git",
]

# sphinx_immaterial announces where it wrote the sitemap with a bare print(),
# so -q never reaches it. We swap in a wrapper around the real handler here,
# at config-read time, because that happens before Sphinx loads the extension
# and binds the handler by name. If the theme ever renames the function the
# getattr below just returns None, and the worst case is that one line comes
# back rather than the build failing.
if not VERBOSE:
try:
from sphinx_immaterial import postprocess_html
except ImportError:
postprocess_html = None

create_sitemap = getattr(postprocess_html, "create_sitemap", None)
if create_sitemap is not None:

def quiet_create_sitemap(app, exception, _wrapped=create_sitemap):
with quiet_stdout():
_wrapped(app, exception)

postprocess_html.create_sitemap = quiet_create_sitemap

bibtex_bibfiles = ["references.bib"]
bibtex_reference_style = "author_year"
bibtex_default_style = "plain"
Expand Down Expand Up @@ -209,10 +323,103 @@

# html_last_updated_fmt = "%B %d, %Y"

# -- Progress reporting for Sphinx's own stages -------------------------------


def connect_progress(app):
"""Drive one spinner through Sphinx's read and write passes.

Those two passes are the slow half of the build and -q makes them silent,
so we name the running phase and count the documents as they go by.

Warnings have to keep printing cleanly underneath all that. Sphinx binds
its warning stream when the application is constructed, which is before any
event we can hook, so swapping sys.stderr is not enough on its own -- we
also wrap the stream on Sphinx's own warning handler. Both wrappers clear
the spinner before writing, so a warning never lands mid-frame.
"""
state = {
"spinner": None,
"stderr": None,
"handlers": [],
"phase": "",
"done": 0,
"total": 0,
}

def retitle():
spinner = state["spinner"]
if spinner is None:
return
progress = ""
if state["total"]:
progress = f" ({state['done']}/{state['total']})"
elif state["done"]:
progress = f" ({state['done']})"
spinner.text = f"Sphinx{state['phase']}{progress}"

def warning_handlers():
"""Sphinx's warning handler, the one thing above the WARNING level."""
return [
handler
for handler in logging.getLogger("sphinx").handlers
if handler.level >= logging.WARNING and hasattr(handler, "stream")
]

def on_start(_app):
spinner = yaspin(text="Sphinx", color="yellow")
spinner.start()
state["spinner"] = spinner
state["stderr"] = sys.stderr
sys.stderr = SpinnerSafeStream(sys.stderr, spinner)
for handler in warning_handlers():
state["handlers"].append((handler, handler.stream))
handler.stream = SpinnerSafeStream(handler.stream, spinner)

def on_read_start(_app, _env, docnames):
state.update(phase=": reading sources", done=0, total=len(docnames))
retitle()

def on_read_doc(_app, _docname, _source):
state["done"] += 1
retitle()

def on_write_page(_app, _pagename, _templatename, _context, _doctree):
if not state["phase"].endswith("writing output"):
state.update(phase=": writing output", done=0, total=0)
state["done"] += 1
retitle()

def on_finish(_app, exception):
spinner = state["spinner"]
if spinner is None:
return
state["spinner"] = None
sys.stderr = state["stderr"]
for handler, stream in state["handlers"]:
handler.stream = stream
state["handlers"].clear()
spinner.text = "Sphinx"
if exception is None:
spinner.ok("\u2705 ")
else:
spinner.fail("\U0001f4a5 ")

app.connect("builder-inited", on_start)
app.connect("env-before-read-docs", on_read_start)
app.connect("source-read", on_read_doc)
app.connect("html-page-context", on_write_page)
# Last, so the spinner covers everything else that runs at the end.
app.connect("build-finished", on_finish, priority=900)


# -- Custom skip logic for autodoc --------------------------------------------


def setup(app):
if SHOW_PROGRESS:
connect_progress(app)

def skip(app, what, name, obj, skip, options):
# Skip the specific private attribute that is causing the crash
if name == "__entries":
Expand Down
11 changes: 11 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ We provide Python bindings for functions in the toolkit using `pybind11 <https:/

**Contributing**

.. figure:: https://contrib.rocks/image?repo=ipc-sim/ipc-toolkit
:align: center
:alt: Top contributors to the IPC Toolkit.
:target: https://github.com/ipc-sim/ipc-toolkit/graphs/contributors?all=1

This project is open to contributors! Contributions can come in the form of feature requests, bug fixes, documentation, tutorials, and the like. We highly recommend filing an Issue first before submitting a Pull Request.

Simply fork this repository and make a Pull Request! We would appreciate:
Expand All @@ -142,6 +147,12 @@ Simply fork this repository and make a Pull Request! We would appreciate:
* Documentation
* Testing

..
.. image:: https://api.star-history.com/chart?repos=ipc-sim/ipc-toolkit&type=date&legend=top-left
:align: center
:alt: Star History Chart
:target: https://www.star-history.com/?repos=ipc-sim%2Fipc-toolkit&type=date&releases=&legend=bottom-right

**Citation**

IPC Toolkit is created and maintained by academics: citations let us know our work is having impact! Please cite the IPC Toolkit or otherwise give a shout-out if and when it contributes to published works.
Expand Down
Loading
Loading