Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use meson to generate docs (html + pdf) #15169

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
123 changes: 123 additions & 0 deletions docs/generate-docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""Generate docs using sphinx in a venv."""

import argparse
import glob
import itertools
import os
import subprocess
import sys
import venv
from pathlib import Path


def main():
"""Start the script."""
args = create_argument_parser()

source_root = args.source_root
build_root = args.build_root

# Create the venv.
venv_directory = build_root.joinpath(args.venv_name)
venv.create(
venv_directory,
with_pip=True,
clear=True,
upgrade_deps=True,
prompt=args.venv_name,
)

# Install some stuff into the venv.
requirements_file = source_root.joinpath(args.requirements_file)
pip = venv_directory.joinpath("bin").joinpath("pip")
subprocess.run([pip, "install", "-U", "pip", "setuptools", "wheel"], check=True)
subprocess.run([pip, "install", "-r", requirements_file], check=True)

# Run sphinx to generate the docs
source_directory = source_root.joinpath(args.source_directory)
target_directory = build_root.joinpath(args.target_directory)
files = [glob.glob(str(source_root.joinpath(pat))) for pat in args.files]
files = list(itertools.chain.from_iterable(files))
sphinx_build = venv_directory.joinpath("bin").joinpath("sphinx-build")

if args.pdf_name:
build_args = [
sphinx_build,
"-M",
"latexpdf",
source_directory,
'.'
]
else:
build_args = [
sphinx_build,
"-b",
"html",
source_directory,
target_directory,
]
subprocess.run(
build_args + files, # if files is empty, it means do all files
check=True
)
if args.pdf_name:
os.rename(build_root.joinpath('latex').joinpath(args.pdf_name), args.pdf_name)

def create_argument_parser():
"""Create command-line argument parser."""
parser = argparse.ArgumentParser(
description="Create a virtualenv from a requirements file"
)
parser.add_argument(
"--build-root",
type=Path,
required=True,
help="Build root",
)
parser.add_argument(
"--source-root",
type=Path,
required=True,
help="Source root",
)
parser.add_argument(
"--venv-name",
type=str,
required=True,
help="Name for the virtualenv",
)
parser.add_argument(
"--requirements-file",
type=Path,
required=True,
help="Package requirements file relative to the source root",
)
parser.add_argument(
"--source-directory",
type=Path,
required=True,
help="Docs directory relative to the source root (contains conf.py)",
)
parser.add_argument(
"--target-directory",
type=Path,
required=True,
help="Target directory for man-pages relative to the build root",
)
parser.add_argument(
"--pdf-name",
type=Path,
required=False,
help="Generate pdf instead of html",
)
parser.add_argument(
"files",
type=Path,
nargs="*",
help="Input files relative to the source root",
)
return parser.parse_args()


if __name__ == "__main__":
sys.exit(main())
51 changes: 51 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -1123,3 +1123,54 @@ if get_option('unit-tests-backends')
endif
endforeach
endif

if python.found()
find_rst_files = run_command(['find', docs_dir, '-name', '*.rst'])
rst_files = find_rst_files.stdout().strip().split('\n')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't be updated when new files are added. The QEMU project faced this challenge already, and wrote a neat trick to solve it:

https://gitlab.com/qemu-project/qemu/-/blob/master/docs/sphinx/depfile.py

This allows you to use depfile: ... and have sphinx report what files it used and which should trigger further rebuilds.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I mentioned the drawback in the commit message. I'll look into it.

Copy link
Contributor

@eli-schwartz eli-schwartz Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Meson isn't a fan of this run_command method... because it's better done by depfiles any time you don't explicitly need the input files as command line arguments! :)

(When you do need them as command-line arguments, for example with executable() sources, I tend to recommend running a generator script as a pre-commit hook, then using fs.read('sources.list').strip().split('\n') instead. fs.read will produce reconfigure dependencies, much like an automake include.)

Copy link
Contributor

@eli-schwartz eli-schwartz Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the depfile (sphinx.d) is removed by nija after generating
it as shown by a trace. I do not know why, but touching an .rts
file has the correct behaviour. So I suspoect it is an optimization.

--> ninja -d list

debugging modes:
[...]
  keepdepfile  don't delete depfiles after they're read by ninja

We use depfiles for all *.o outputs as well. Ninja reads them in after the command finishes for build nodes that declare they provide depfiles, and compiles them into a binary database (.ninja_deps) for, as you guessed, optimization purposes. No need to read tons of tiny files in on every startup. :)

Hence also why keeping the file around is one of the options exposed in the debugging modes. Though, I don't know why it is necessary to delete them after reading them, but maybe the ninja authors figured the file is useless after reading it, so might as well compact the build directory size a tiny bit.


html_docs = custom_target(
'html-docs',
command: [
python,
product_source_dir / docs_dir / 'generate-docs.py',
'--build-root', '@BUILD_ROOT@',
'--source-root', '@SOURCE_ROOT@',
'--venv-name', 'venv-docs',
'--requirements-file', docs_dir / 'requirements.txt',
'--source-directory', docs_dir,
'--target-directory', '@BUILD_ROOT@' / 'html-docs',
],
output: 'html-docs',
console: true,
depend_files: rst_files,
)

docs_tarball = custom_target(
'html-docs.tar.bz2',
command: ['tar', 'cjf', 'html-docs.tar.bz2', html_docs],
output: 'html-docs.tar.bz2',
)

pdf_docs = custom_target(
command: [
python,
product_source_dir / docs_dir / 'generate-docs.py',
'--build-root', '@BUILD_ROOT@',
'--source-root', '@SOURCE_ROOT@',
'--venv-name', 'venv-docs',
'--requirements-file', docs_dir / 'requirements.txt',
'--source-directory', docs_dir,
'--target-directory', '@BUILD_ROOT@',
'--pdf-name', 'PowerDNS-Authoritative.pdf',
],
output: 'PowerDNS-Authoritative.pdf',
console: true,
depend_files: rst_files,
)

run_target(
'all-docs',
# args mentioned in command line become auto-dependency
command: ['echo', 'Generated', html_docs, docs_tarball, pdf_docs],
)
endif
1 change: 1 addition & 0 deletions pdns/dnsdistdist/docs/generate-docs.py
53 changes: 52 additions & 1 deletion pdns/dnsdistdist/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -688,4 +688,55 @@ install_data(
'dnsdist.conf-dist',
install_dir : get_option('sysconfdir'),
follow_symlinks: true
)
)

if python.found()
find_rst_files = run_command(['find', docs_dir, '-name', '*.rst'])
rst_files = find_rst_files.stdout().strip().split('\n')

html_docs = custom_target(
'html-docs',
command: [
python,
product_source_dir / docs_dir / 'generate-docs.py',
'--build-root', '@BUILD_ROOT@',
'--source-root', '@SOURCE_ROOT@',
'--venv-name', 'venv-docs',
'--requirements-file', docs_dir / 'requirements.txt',
'--source-directory', docs_dir,
'--target-directory', '@BUILD_ROOT@' / 'html-docs',
],
output: 'html-docs',
console: true,
depend_files: rst_files,
)

docs_tarball = custom_target(
'html-docs.tar.bz2',
command: ['tar', 'cjf', 'html-docs.tar.bz2', html_docs],
output: 'html-docs.tar.bz2',
)

pdf_docs = custom_target(
command: [
python,
product_source_dir / docs_dir / 'generate-docs.py',
'--build-root', '@BUILD_ROOT@',
'--source-root', '@SOURCE_ROOT@',
'--venv-name', 'venv-docs',
'--requirements-file', docs_dir / 'requirements.txt',
'--source-directory', docs_dir,
'--target-directory', '@BUILD_ROOT@',
'--pdf-name', 'dnsdist.pdf',
],
output: 'dnsdist.pdf',
console: true,
depend_files: rst_files,
)

run_target(
'all-docs',
# args mentioned in command line become auto-dependency
command: ['echo', 'Generated', html_docs, docs_tarball, pdf_docs],
)
endif
1 change: 1 addition & 0 deletions pdns/recursordist/docs/generate-docs.py
50 changes: 50 additions & 0 deletions pdns/recursordist/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -734,3 +734,53 @@ dep_conf_distfile = custom_target(
install_dir: get_option('sysconfdir'),
)

if python.found()
find_rst_files = run_command(['find', docs_dir, '-name', '*.rst'])
rst_files = find_rst_files.stdout().strip().split('\n')

html_docs = custom_target(
'html-docs',
command: [
python,
product_source_dir / docs_dir / 'generate-docs.py',
'--build-root', '@BUILD_ROOT@',
'--source-root', '@SOURCE_ROOT@',
'--venv-name', 'venv-docs',
'--requirements-file', docs_dir / 'requirements.txt',
'--source-directory', docs_dir,
'--target-directory', '@BUILD_ROOT@' / 'html-docs',
],
output: 'html-docs',
console: true,
depend_files: rst_files,
)

docs_tarball = custom_target(
'html-docs.tar.bz2',
command: ['tar', 'cjf', 'html-docs.tar.bz2', html_docs],
output: 'html-docs.tar.bz2',
)

pdf_docs = custom_target(
command: [
python,
product_source_dir / docs_dir / 'generate-docs.py',
'--build-root', '@BUILD_ROOT@',
'--source-root', '@SOURCE_ROOT@',
'--venv-name', 'venv-docs',
'--requirements-file', docs_dir / 'requirements.txt',
'--source-directory', docs_dir,
'--target-directory', '@BUILD_ROOT@',
'--pdf-name', 'PowerDNS-Recursor.pdf',
],
output: 'PowerDNS-Recursor.pdf',
console: true,
depend_files: rst_files,
)

run_target(
'all-docs',
# args mentioned in command line become auto-dependency
command: ['echo', 'Generated', html_docs, docs_tarball, pdf_docs],
)
endif
Loading