Fix #115056: man-page fails to build with non-portable install
This effectively reverts [0] which required the installed Blender
along with the `bpy` module to load for the install target to generate
the man-page. It turns out running a full Blender at install time can be
quite involved.
Now the man-page generator runs `blender --help` & `blender --version`,
converting the output into a man-page from a Python script.
Some minor changes have also been made:
- Use PYTHON_EXECUTABLE so the systems Python
(which may not be compatible) isn't used.
- Remove leading blender version from the description
which was unintentionally being included.
[0]: 61d99d450e
This commit is contained in:
parent
9dda667fd4
commit
5486c70aae
|
@ -7,7 +7,7 @@
|
|||
This script generates the blender.1 man page, embedding the help text
|
||||
from the Blender executable itself. Invoke it as follows:
|
||||
|
||||
./blender.bin -b --python doc/manpage/blender.1.py -- --output <output-filename>
|
||||
blender.1.py --blender <path-to-blender> --output <output-filename>
|
||||
|
||||
where <path-to-blender> is the path to the Blender executable,
|
||||
and <output-filename> is where to write the generated man page.
|
||||
|
@ -15,8 +15,8 @@ and <output-filename> is where to write the generated man page.
|
|||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
|
||||
from typing import (
|
||||
Dict,
|
||||
|
@ -30,28 +30,77 @@ def man_format(data: str) -> str:
|
|||
return data
|
||||
|
||||
|
||||
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
|
||||
def blender_extract_info(blender_bin: str) -> Dict[str, str]:
|
||||
blender_env = {
|
||||
"ASAN_OPTIONS": (
|
||||
os.environ.get("ASAN_OPTIONS", "") +
|
||||
":exitcode=0:check_initialization_order=0:strict_init_order=0"
|
||||
).lstrip(":"),
|
||||
}
|
||||
|
||||
if blender_build_date_text == b'Unknown':
|
||||
# NOTE: in some ways it's more elegant to use `bpy.app.help_text()` which was done but had to be reverted.
|
||||
# however - this requires Blender to run with a full environment (initializing it's Python environment).
|
||||
# See #115056 & !115320 for details.
|
||||
|
||||
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 = ""
|
||||
|
||||
# The full text (use to manipulate `blender_version_text`).
|
||||
blender_version_text = ""
|
||||
|
||||
for l in blender_version_output.split("\n"):
|
||||
if l.startswith("Blender "):
|
||||
if blender_version_text == "":
|
||||
blender_version_text = l
|
||||
# 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
|
||||
|
||||
# The `--help` text also contains the version, skip it so as not to include it twice.
|
||||
if blender_version_text:
|
||||
i = blender_help.find(blender_version_text)
|
||||
if i != -1:
|
||||
blender_help = blender_help[i + len(blender_version_text) + 1:]
|
||||
del i
|
||||
|
||||
if not blender_date:
|
||||
# Happens when built without WITH_BUILD_INFO e.g.
|
||||
blender_date = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
|
||||
date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
|
||||
else:
|
||||
blender_date = time.strftime("%B %d, %Y", time.strptime(blender_build_date_text.decode(), "%Y-%m-%d"))
|
||||
date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
|
||||
|
||||
return {
|
||||
"help": blender_help_text,
|
||||
"version": blender_version_text,
|
||||
"date": blender_date,
|
||||
"help": blender_help,
|
||||
"version": blender_version,
|
||||
"date": date_string,
|
||||
}
|
||||
|
||||
|
||||
def man_page_from_blender_help(fh: TextIO, verbose: bool) -> None:
|
||||
blender_info = blender_extract_info()
|
||||
def man_page_from_blender_help(fh: TextIO, blender_bin: str, verbose: bool) -> None:
|
||||
if verbose:
|
||||
print("Extracting help text:", blender_bin)
|
||||
blender_info = blender_extract_info(blender_bin)
|
||||
|
||||
# Header Content.
|
||||
fh.write(
|
||||
|
@ -144,6 +193,11 @@ def create_argparse() -> argparse.ArgumentParser:
|
|||
required=True,
|
||||
help="The man page to write to."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--blender",
|
||||
required=True,
|
||||
help="Path to the blender binary."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--verbose",
|
||||
default=False,
|
||||
|
@ -156,15 +210,15 @@ def create_argparse() -> argparse.ArgumentParser:
|
|||
|
||||
|
||||
def main() -> None:
|
||||
argv = sys.argv[sys.argv.index("--") + 1:]
|
||||
parser = create_argparse()
|
||||
args = parser.parse_args(argv)
|
||||
args = parser.parse_args()
|
||||
|
||||
output_filename = args.output
|
||||
blender_bin = args.blender
|
||||
verbose = args.verbose
|
||||
|
||||
with open(output_filename, "w", encoding="utf-8") as fh:
|
||||
man_page_from_blender_help(fh, verbose)
|
||||
man_page_from_blender_help(fh, blender_bin, verbose)
|
||||
if verbose:
|
||||
print("Written:", output_filename)
|
||||
|
||||
|
|
|
@ -1805,6 +1805,7 @@ if(UNIX AND NOT APPLE)
|
|||
install(
|
||||
CODE "\
|
||||
set(BLENDER_BIN \"${CMAKE_INSTALL_PREFIX}/${BLENDER_BIN}\")\n\
|
||||
set(PYTHON_EXECUTABLE \"${PYTHON_EXECUTABLE}\")\n\
|
||||
set(MANPAGE_GEN \"${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py\")\n\
|
||||
set(MANPAGE_OUT \"${CMAKE_CURRENT_BINARY_DIR}/blender.1\")\n\
|
||||
if(\n\
|
||||
|
@ -1818,12 +1819,9 @@ strict_init_order=0:\
|
|||
detect_leaks=0\"\n\
|
||||
)\n\
|
||||
execute_process(\n\
|
||||
OUTPUT_QUIET\n\
|
||||
COMMAND $\{BLENDER_BIN\}\n\
|
||||
--background\n\
|
||||
--factory-startup\n\
|
||||
--python $\{MANPAGE_GEN\}\n\
|
||||
--\n\
|
||||
COMMAND\n\
|
||||
$\{PYTHON_EXECUTABLE\} $\{MANPAGE_GEN\}\n\
|
||||
--blender $\{BLENDER_BIN\}\n\
|
||||
--output $\{MANPAGE_OUT\}\n\
|
||||
)\n\
|
||||
endif()\n\
|
||||
|
|
Loading…
Reference in New Issue