Add a "make website" target that builds the reStructuredText
documentation with Sphinx and generates the command help pages from the
crash binary into a single _site directory. The C help data remains the
only source for the help text: scripts/gen-help-site.py asks the binary
for the help menu and for each command's page.
The build comments describe only the build; publishing is left to the
project infrastructure.
Signed-off-by: Rui Yang <yangrr.2009(a)tsinghua.org.cn>
---
.gitignore | 2 +
Makefile | 17 ++++-
doc/Makefile | 12 +++-
doc/_static/crash.jpg | Bin 0 -> 3479 bytes
doc/index.rst | 11 +++
scripts/gen-help-site.py | 146 +++++++++++++++++++++++++++++++++++++++
6 files changed, 182 insertions(+), 6 deletions(-)
create mode 100644 doc/_static/crash.jpg
create mode 100755 scripts/gen-help-site.py
diff --git a/.gitignore b/.gitignore
index 7ac5de1797d7..320805190f55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,5 @@ TAGS
# Clang's compilation database file
/compile_commands.json
+
+/_site/
diff --git a/Makefile b/Makefile
index 95995bb5ae0b..45c62f42c0bc 100644
--- a/Makefile
+++ b/Makefile
@@ -467,6 +467,7 @@ clean: make_configure
do_clean:
rm -f ${OBJECT_FILES} ${DAEMON_OBJECT_FILES} ${PROGRAM} ${PROGRAM}lib.a ${GDB_OFILES}
+ rm -rf ${SITE_DIR}
@$(MAKE) -C extensions -i clean
@$(MAKE) -C memory_driver -i clean
@$(MAKE) -C doc -i clean
@@ -838,8 +839,7 @@ memory_driver: make_configure
#
# Documentation. The reStructuredText sources live in doc/ and are the
-# source of truth for the crash documentation; the HTML generated here is
-# what gets published on the crash utility web site.
+# source of truth for the crash documentation.
#
doc: htmldocs
@@ -852,4 +852,15 @@ pdfdocs:
cleandocs:
$(MAKE) -C doc clean
-.PHONY: doc htmldocs pdfdocs cleandocs
+#
+# Build the documentation and the command help pages into one directory.
+#
+SITE_DIR=_site
+
+website: ${PROGRAM}
+ rm -rf ${SITE_DIR}
+ $(MAKE) -C doc website WEBSITE_DIR=$(abspath ${SITE_DIR})
+ python3 scripts/gen-help-site.py --crash $(abspath ${PROGRAM}) --out $(abspath
${SITE_DIR})
+ @echo "Site generated in ${SITE_DIR}"
+
+.PHONY: doc htmldocs pdfdocs cleandocs website
diff --git a/doc/Makefile b/doc/Makefile
index 9c7014965b1d..a75b2ac3959c 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -2,20 +2,21 @@
# Makefile for the crash documentation.
#
# The reStructuredText sources in this directory are the source of truth for
-# the crash documentation; the HTML output generated here is what is published
-# on the crash-utility web site.
+# the crash documentation.
SPHINXBUILD ?= sphinx-build
SPHINXOPTS ?=
SOURCEDIR = .
BUILDDIR = output
PAPER ?= a4
+WEBSITE_DIR ?= $(abspath ../_site)
-.PHONY: help html htmldocs pdf latexpdf clean cleandocs
+.PHONY: help html htmldocs website pdf latexpdf clean cleandocs
help:
@echo "Please use 'make <target>' where <target> is one
of:"
@echo " html / htmldocs to make standalone HTML files"
+ @echo " website to build the HTML documentation to a directory"
@echo " pdf / latexpdf to make a PDF file (requires a LaTeX toolchain)"
@echo " clean / cleandocs to remove the build directory"
@@ -24,6 +25,11 @@ html htmldocs:
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+website:
+ $(SPHINXBUILD) -b html -d $(BUILDDIR)/doctrees $(SPHINXOPTS) $(SOURCEDIR)
$(WEBSITE_DIR)
+ @echo
+ @echo "Build finished. The HTML pages are in $(WEBSITE_DIR)."
+
latex:
$(SPHINXBUILD) -b latex $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/latex
diff --git a/doc/_static/crash.jpg b/doc/_static/crash.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..554848376e48af39d78a540106c7dc56d3584dc8
GIT binary patch
literal 3479
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
literal 0
HcmV?d00001
diff --git a/doc/index.rst b/doc/index.rst
index 6d41b23c0c31..21794be06254 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -1,10 +1,21 @@
crash utility documentation
===========================
+.. image:: _static/crash.jpg
+ :alt: crash utility
+ :width: 320
+
The **crash** utility is a tool for interactively analyzing the state of the
Linux system, whether it is running (a live system) or has crashed and
produced a core dump (a *dumpfile*).
+* `crash command help pages <help.html>`_
+* `White Paper: Crash Utility <whitepaper.html>`_
+* `crash source code <
https://github.com/crash-utility/crash>`_
+* `GitHub releases <
https://github.com/crash-utility/crash/releases>`_
+* `crash-utility mailing list
+
<
https://lists.crash-utility.osci.io/admin/lists/devel.lists.crash-utility...
+
.. toctree::
:maxdepth: 2
:caption: Contents:
diff --git a/scripts/gen-help-site.py b/scripts/gen-help-site.py
new file mode 100755
index 000000000000..dd3c1deeb63a
--- /dev/null
+++ b/scripts/gen-help-site.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+"""Generate the crash command help pages for the documentation site.
+
+The crash binary is the single source of truth for the command help: this
+script asks it for the help menu and for each command's help page, then wraps
+the text in small HTML pages.
+
+Usage:
+ scripts/gen-help-site.py --crash ./crash --out _site
+"""
+import argparse
+import html
+import os
+import subprocess
+import sys
+
+CSS = """
+body { font-family: sans-serif; max-width: 60em; margin: 2em auto;
+ padding: 0 1em; color: #222; line-height: 1.4; }
+h1 { font-size: 1.35em; }
+h1 tt, td tt { font-family: monospace; }
+pre { background: #f5f5f5; border: 1px solid #ddd; padding: 1em;
+ overflow-x: auto; font-size: .9em; line-height: 1.35; }
+a { color: #005a9c; }
+table.menu { border-collapse: collapse; }
+table.menu td { padding: .2em 1.8em .2em 0; white-space: nowrap; }
+""".strip()
+
+HELP_PAGE = """<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>crash help: {cmd}</title>
+<style>{css}</style>
+</head>
+<body>
+<h1>crash utility help page: <tt>{cmd}</tt></h1>
+<pre>{text}</pre>
+<p><a href="../help.html">← back to the crash help
menu</a></p>
+</body>
+</html>
+"""
+
+MENU_PAGE = """<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>crash utility help pages</title>
+<style>{css}</style>
+</head>
+<body>
+<h1>crash utility help pages</h1>
+<p><a href="index.html">← back to the crash
documentation</a></p>
+<table class="menu">
+{rows}
+</table>
+</body>
+</html>
+"""
+
+
+def run_help(crash, what):
+ """Return the text of "crash -h
<what>"."""
+ cmd = [crash, "--no_scroll", "--no_crashrc", "-h",
what]
+ result = subprocess.run(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ if result.returncode != 0:
+ sys.stderr.write(result.stderr.decode("utf-8", "replace"))
+ raise SystemExit("failed to run: %s" % " ".join(cmd))
+ return result.stdout.decode("utf-8", "replace")
+
+
+def parse_menu(text):
+ """Split the "help commands" output (names, with BREAK row
markers)."""
+ rows = []
+ row = []
+ for line in text.splitlines():
+ line = line.strip()
+ if line == "BREAK":
+ if row:
+ rows.append(row)
+ row = []
+ elif line:
+ row.append(line)
+ if row:
+ rows.append(row)
+ return rows
+
+
+def page_name(cmd):
+ if cmd == "*":
+ return "pointer"
+ return cmd
+
+
+def strip_text(text):
+ lines = text.splitlines()
+ while lines and not lines[0].strip():
+ lines.pop(0)
+ while lines and not lines[-1].strip():
+ lines.pop()
+ return "\n".join(lines)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--crash", required=True, help="path to the crash
binary")
+ parser.add_argument("--out", required=True, help="output
directory")
+ args = parser.parse_args()
+
+ crash = os.path.abspath(args.crash)
+ out = os.path.abspath(args.out)
+ if not os.path.isfile(crash):
+ raise SystemExit("no such crash binary: %s" % crash)
+
+ pages_dir = os.path.join(out, "help_pages")
+ os.makedirs(pages_dir, exist_ok=True)
+
+ menu_rows = parse_menu(run_help(crash, "commands"))
+ commands = [cmd for row in menu_rows for cmd in row]
+
+ table = []
+ for row in menu_rows:
+ cells = []
+ for cmd in row:
+ target = "help_pages/%s.html" % page_name(cmd)
+ cells.append('<td><a
href="%s">%s</a></td>'
+ % (html.escape(target), html.escape(cmd)))
+ table.append("<tr>%s</tr>" % "".join(cells))
+
+ with open(os.path.join(out, "help.html"), "w",
encoding="utf-8") as f:
+ f.write(MENU_PAGE.format(css=CSS, rows="\n".join(table)))
+
+ for cmd in commands:
+ text = strip_text(run_help(crash, cmd))
+ path = os.path.join(pages_dir, page_name(cmd) + ".html")
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(HELP_PAGE.format(cmd=html.escape(cmd),
+ css=CSS,
+ text=html.escape(text)))
+ print("generated help.html and %d help pages in %s"
+ % (len(commands), out))
+
+
+if __name__ == "__main__":
+ main()