Skip to content

Commit

Permalink
doc: add infrastructure for building man pages
Browse files Browse the repository at this point in the history
Problem: flux-accounting has no way to build and install its man pages.

Copy over the docs infrastructure from flux-sched and flux-core to
be able to build the manpages for flux-accounting.
  • Loading branch information
cmoussa1 committed May 25, 2024
1 parent fce7cfe commit da285a9
Show file tree
Hide file tree
Showing 12 changed files with 990 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ACLOCAL_AMFLAGS = -I config

SUBDIRS = src etc t
SUBDIRS = src etc doc t

EXTRA_DIST= \
config/tap-driver.sh \
Expand Down
34 changes: 34 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,38 @@ fi
# checks for packages
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.10], [], [])

AS_IF([test "x$enable_docs" != "xno"], [
AM_CHECK_PYMOD(sphinx,
[Version(sphinx.__version__) >= Version ('1.6.7')],
[sphinx=true],
[sphinx=false; AC_MSG_WARN([could not find sphinx to generate docs, version 1.6.7+ required])]
)
AM_CHECK_PYMOD(docutils,
[Version(docutils.__version__) >= Version ('0.11.0')],
[docutils=true],
[docutils=false; AC_MSG_WARN([could not find docutils to generate docs, version 0.11.0+ required])]
)
])
# If --enable-docs=yes, but no doc generator found,
# then error immediately:
#
AS_IF([test "x$enable_docs" = "xyes" -a "x$sphinx" = "xfalse"],[
AC_MSG_ERROR([--enable-docs used but no document generator found!])
])
AS_IF([test "x$enable_docs" = "xyes" -a "x$docutils" = "xfalse"],[
AC_MSG_ERROR([--enable-docs used but docutils not found!])
])
AM_CONDITIONAL([ENABLE_DOCS], [test "x$sphinx" = "xtrue" -a "x$docutils" = "xtrue"])
AC_CHECK_PROG(ASPELL,[aspell],[aspell])

AS_IF([test "x$enable_docs" != "xno"], [
if test "x$sphinx" = "xfalse"; then
AC_MSG_WARN([Python Sphinx not found. Manual pages will not be generated.])
elif test "x$docutils" = "xfalse"; then
AC_MSG_WARN([Python Docutils not found. Manual pages will not be generated.])
fi
])

#
# Project directories
#
Expand Down Expand Up @@ -119,6 +151,8 @@ AC_CONFIG_FILES([Makefile
src/plugins/Makefile
etc/Makefile
etc/flux-accounting.service
doc/Makefile
doc/test/Makefile
t/Makefile
])
AC_OUTPUT
Expand Down
83 changes: 83 additions & 0 deletions doc/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
.NOTPARALLEL:

SUBDIRS = . test

MAN_FILES = $(MAN5_FILES)

MAN5_FILES = $(MAN5_FILES_PRIMARY)

MAN5_FILES_PRIMARY = \
man5/flux-config-accounting.5

RST_FILES = \
$(MAN5_FILES_PRIMARY:.5=.rst)

if ENABLE_DOCS
man_MANS = $(MAN5_FILES)
endif

SUFFIXES = .rst .5

sphinx_man = $(sphinx_man_$(V))
sphinx_man_ = $(sphinx_man_$(AM_DEFAULT_VERBOSITY))
sphinx_man_0 = @echo " BUILD manpages";

sphinx_html = $(sphinx_html_$(V))
sphinx_html_ = $(sphinx_html_$(AM_DEFAULT_VERBOSITY))
sphinx_html_0 = @echo " BUILD html";

sphinx_verbose_flags = $(sphinx_verbose_flags_$(V))
sphinx_verbose_flags_ = $(sphinx_verbose_flags_$(AM_DEFAULT_VERBOSITY))
sphinx_verbose_flags_0 =
sphinx_verbose_flags_1 = -v
sphinx_verbose_flags_2 = -vv

STDERR_DEVNULL = $(stderr_devnull_$(V))
stderr_devnull_ = $(stderr_devnull_$(AM_DEFAULT_VERBOSITY))
stderr_devnull_0 = >/dev/null 2>&1

$(MAN_FILES): manpages.py conf.py $(RST_FILES)
$(sphinx_man) \
SPHINX_BUILDDIR=$(abs_builddir) $(PYTHON) \
-m sphinx $(sphinx_verbose_flags) -b man $(srcdir) ./man \
$(STDERR_DEVNULL)
@echo " MV manpages"; \
for sec in 5; do \
$(MKDIR_P) man$$sec && \
mv -f $(abs_builddir)/man/*.$$sec man$$sec/; \
done

.PHONY: html
html: conf.py $(RST_FILES)
$(sphinx_html) \
SPHINX_BUILDDIR=$(abs_builddir) $(PYTHON) \
-m sphinx $(sphinx_verbose_flags) -b html $(srcdir) ./html \
$(STDERR_DEVNULL)

EXTRA_DIST = \
conf.py \
manpages.py \
index.rst \
domainrefs.py \
requirements.txt \
guide/accounting-guide.rst \
$(RST_FILES) \
man5/index.rst

CLEANFILES = \
$(MAN_FILES)

clean-local:
-rm -rf man python/autogenerated

distclean-local:
-rm -rf doctrees

AM_LDFLAGS = \
$(CODE_COVERAGE_LIBS) \
-no-install

AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_srcdir)/src/include \
-I$(top_builddir)/src/common/libflux
150 changes: 150 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
###############################################################
# Copyright 2024 Lawrence Livermore National Security, LLC
# (c.f. AUTHORS, NOTICE.LLNS, COPYING)
#
# This file is part of the Flux resource manager framework.
# For details, see https://github.com/flux-framework.
#
# SPDX-License-Identifier: LGPL-3.0
###############################################################

# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- 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
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys

# add `manpages` directory to sys.path
import pathlib

sys.path.append(str(pathlib.Path(__file__).absolute().parent))

from manpages import man_pages
import docutils.nodes

# -- Project information -----------------------------------------------------

project = "flux-accounting"
copyright = """Copyright 2024 Lawrence Livermore National Security, LLC and Flux developers.
SPDX-License-Identifier: LGPL-3.0"""

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

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

master_doc = "index"
source_suffix = ".rst"

extensions = ["sphinx.ext.intersphinx", "sphinx.ext.napoleon", "domainrefs"]

domainrefs = {
"core:man5": {
"text": "%s(5)",
"url": "https://flux-framework.readthedocs.io/projects/flux-core/en/latest/man5/%s.html",
},
}

# Disable "smartquotes" to avoid things such as turning long-options
# "--" into en-dash in html output, which won't make much sense for
# manpages.
smartquotes = False

# -- Setup for Sphinx API Docs -----------------------------------------------

# Workaround since sphinx does not automatically run apidoc before a build
# Copied from https://github.com/readthedocs/readthedocs.org/issues/1139

script_dir = os.path.normpath(os.path.dirname(__file__))
py_bindings_dir = os.path.normpath(os.path.join(script_dir, "../src/bindings/python/"))

# Make sure that the python bindings are in PYTHONPATH for autodoc
sys.path.insert(0, py_bindings_dir)

# run api doc
def run_apidoc(_):
# Move import inside so that `gen-cmdhelp.py` can exec this file in LGTM.com
# without sphinx installed
# pylint: disable=import-outside-toplevel
from sphinx.ext.apidoc import main

try:
# Check if running under `make`
build_dir = os.path.normpath(os.environ.get("SPHINX_BUILDDIR"))
except:
build_dir = script_dir
output_path = os.path.join(build_dir, "python", "autogenerated")
exclusions = [
os.path.join(py_bindings_dir, "setup.py"),
]
main(["-e", "-f", "-M", "-T", "-o", output_path, py_bindings_dir] + exclusions)


def man_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
section = int(name[-1])
page = None
for man in man_pages:
if man[1] == text and man[4] == section:
page = man[0]
break
if page == None:
page = "man7/flux-undocumented"
section = 7

node = docutils.nodes.reference(
rawsource=rawtext,
text=f"{text}({section})",
refuri=f"../{page}.html",
**options,
)
return [node], []


# launch setup
def setup(app):
app.connect("builder-inited", run_apidoc)
for section in [5]:
app.add_role(f"man{section}", man_role)


napoleon_google_docstring = True

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']

# -- Options for Intersphinx -------------------------------------------------

intersphinx_mapping = {
"core": (
"https://flux-framework.readthedocs.io/projects/flux-core/en/latest/",
None,
),
"rfc": (
"https://flux-framework.readthedocs.io/projects/flux-rfc/en/latest/",
None,
),
}
65 changes: 65 additions & 0 deletions doc/domainrefs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
Originally from:
https://github.com/mitogen-hq/mitogen/blob/master/docs/domainrefs.py
Copyright 2021, the Mitogen authors
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder nor the names of its 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.
"""
import functools
import re

import docutils.nodes
import docutils.utils


CUSTOM_RE = re.compile("(.*) <(.*)>")


def role(config, role, rawtext, text, lineno, inliner, options={}, content=[]):
template = "https://docs.ansible.com/ansible/latest/modules/%s_module.html"

match = CUSTOM_RE.match(text)
if match: # "custom text <real link>"
title = match.group(1)
text = match.group(2)
elif text.startswith("~"): # brief
text = text[1:]
title = config.get("brief", "%s") % (docutils.utils.unescape(text),)
else:
title = config.get("text", "%s") % (docutils.utils.unescape(text),)

node = docutils.nodes.reference(
rawsource=rawtext, text=title, refuri=config["url"] % (text,), **options
)

return [node], []


def setup(app):
for name, info in app.config._raw_config["domainrefs"].items():
app.add_role(name, functools.partial(role, info))
17 changes: 17 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Documentation for flux-accounting
=================================

.. toctree::
:maxdepth: 2
:caption: Contents:


.. _man-pages:

flux-accounting Manual Pages
============================

.. toctree::
:maxdepth: 2

man5/index
8 changes: 8 additions & 0 deletions doc/man5/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
man5
====

.. toctree::
:caption: File formats and conventions
:maxdepth: 1

flux-config-accounting
Loading

0 comments on commit da285a9

Please sign in to comment.