manpage: use bpy.app.help_text() for manual text extraction

Call Blender directly to generate the man-page,
instead of relying on the systems Python which called Blender twice and
processed it's output.
This commit is contained in:
Campbell Barton
2023-05-27 16:34:26 +10:00
parent 28b8ceea7c
commit 61d99d450e
2 changed files with 33 additions and 58 deletions

View File

@@ -5,7 +5,7 @@
This script generates the blender.1 man page, embedding the help text This script generates the blender.1 man page, embedding the help text
from the Blender executable itself. Invoke it as follows: from the Blender executable itself. Invoke it as follows:
blender.1.py --blender <path-to-blender> --output <output-filename> ./blender.bin -b --python doc/manpage/blender.1.py -- --output <output-filename>
where <path-to-blender> is the path to the Blender executable, where <path-to-blender> is the path to the Blender executable,
and <output-filename> is where to write the generated man page. and <output-filename> is where to write the generated man page.
@@ -13,8 +13,8 @@ and <output-filename> is where to write the generated man page.
import argparse import argparse
import os import os
import subprocess
import time import time
import sys
from typing import ( from typing import (
Dict, Dict,
@@ -28,61 +28,28 @@ def man_format(data: str) -> str:
return data return data
def blender_extract_info(blender_bin: str) -> Dict[str, str]: def blender_extract_info() -> Dict[str, str]:
# Only use of `bpy` in this file.
import bpy # type: ignore
blender_help_text = bpy.app.help_text()
blender_version_text = bpy.app.version_string
blender_build_date_text = bpy.app.build_date
blender_env = { if blender_build_date_text == b'Unknown':
"ASAN_OPTIONS": (
os.environ.get("ASAN_OPTIONS", "") +
":exitcode=0:check_initialization_order=0:strict_init_order=0"
).lstrip(":"),
}
blender_help = subprocess.run(
[blender_bin, "--help"],
env=blender_env,
check=True,
stdout=subprocess.PIPE,
).stdout.decode(encoding="utf-8")
blender_version_output = subprocess.run(
[blender_bin, "--version"],
env=blender_env,
check=True,
stdout=subprocess.PIPE,
).stdout.decode(encoding="utf-8")
# Extract information from the version string.
# Note that some internal modules may print errors (e.g. color management),
# check for each lines prefix to ensure these aren't included.
blender_version = ""
blender_date = ""
for l in blender_version_output.split("\n"):
if l.startswith("Blender "):
# Remove 'Blender' prefix.
blender_version = l.split(" ", 1)[1].strip()
elif l.lstrip().startswith("build date:"):
# Remove 'build date:' prefix.
blender_date = l.split(":", 1)[1].strip()
if blender_version and blender_date:
break
if not blender_date:
# Happens when built without WITH_BUILD_INFO e.g. # Happens when built without WITH_BUILD_INFO e.g.
date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))) blender_date = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
else: else:
date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d")) blender_date = time.strftime("%B %d, %Y", time.strptime(blender_build_date_text, "%Y-%m-%d"))
return { return {
"help": blender_help, "help": blender_help_text,
"version": blender_version, "version": blender_version_text,
"date": date_string, "date": blender_date,
} }
def man_page_from_blender_help(fh: TextIO, blender_bin: str, verbose: bool) -> None: def man_page_from_blender_help(fh: TextIO, verbose: bool) -> None:
if verbose: blender_info = blender_extract_info()
print("Extracting help text:", blender_bin)
blender_info = blender_extract_info(blender_bin)
# Header Content. # Header Content.
fh.write( fh.write(
@@ -175,11 +142,6 @@ def create_argparse() -> argparse.ArgumentParser:
required=True, required=True,
help="The man page to write to." help="The man page to write to."
) )
parser.add_argument(
"--blender",
required=True,
help="Path to the blender binary."
)
parser.add_argument( parser.add_argument(
"--verbose", "--verbose",
default=False, default=False,
@@ -192,15 +154,15 @@ def create_argparse() -> argparse.ArgumentParser:
def main() -> None: def main() -> None:
argv = sys.argv[sys.argv.index("--") + 1:]
parser = create_argparse() parser = create_argparse()
args = parser.parse_args() args = parser.parse_args(argv)
blender_bin = args.blender
output_filename = args.output output_filename = args.output
verbose = args.verbose verbose = args.verbose
with open(output_filename, "w", encoding="utf-8") as fh: with open(output_filename, "w", encoding="utf-8") as fh:
man_page_from_blender_help(fh, blender_bin, verbose) man_page_from_blender_help(fh, verbose)
if verbose: if verbose:
print("Written:", output_filename) print("Written:", output_filename)

View File

@@ -1715,7 +1715,20 @@ if(\n\
($\{BLENDER_BIN\} IS_NEWER_THAN $\{MANPAGE_OUT\}) OR\n\ ($\{BLENDER_BIN\} IS_NEWER_THAN $\{MANPAGE_OUT\}) OR\n\
($\{MANPAGE_GEN\} IS_NEWER_THAN $\{MANPAGE_OUT\})\n\ ($\{MANPAGE_GEN\} IS_NEWER_THAN $\{MANPAGE_OUT\})\n\
)\n\ )\n\
execute_process(COMMAND $\{MANPAGE_GEN\} --blender $\{BLENDER_BIN\} --output $\{MANPAGE_OUT\})\n\ set(\n\
ENV{ASAN_OPTIONS}\n\
\"$ENV{ASAN_OPTIONS}:exitcode=0:check_initialization_order=0:strict_init_order=0\"\n\
)\n\
execute_process(\n\
OUTPUT_QUIET\n\
ERROR_QUIET\n\
COMMAND $\{BLENDER_BIN\}\n\
--background\n\
--factory-startup\n\
--python $\{MANPAGE_GEN\}\n\
--\n\
--output $\{MANPAGE_OUT\}\n\
)\n\
endif()\n\ endif()\n\
" "
DEPENDS blender DEPENDS blender