Files
test2/doc/manpage/blender.1.py
Bastien Montagne 2c9ab53273 Add 'system python' validation for some py scripts.
The goal of this test is to try to import some critical py scripts with the
system python of the building machine.
The main target is to ensure that these py scripts remain usable by all
buildbot machines, as some of them are using fairly outdated python
versions.

Current status:
* Scripts in `build_files` and `docs` are checked.
* Some python scripts in `build_files` were 'reverted' to be compatible
  with older required python version currently (3.6).
* A few scripts are excluded from the test, mostly because they use Blender's
  `bpy` module, which means they are only intended to be ran with Blender's
  python anyway.
* The test is only enabled for Linux buildbots currently, as they use the
  oldest Python by far.

Notes:
* Some more scripts are likely to be moved around in the future.
* Whether these tests need to be enabled on windows or macos platforms remains
  an open question.

Pull Request: https://projects.blender.org/blender/blender/pulls/130746
2024-12-24 11:55:29 +01:00

231 lines
6.6 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2010-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
"""
This script generates the blender.1 man page, embedding the help text
from the Blender executable itself. Invoke it as follows:
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.
"""
import argparse
import os
import subprocess
import time
from typing import (
TextIO,
Dict,
)
def man_format(data: str) -> str:
data = data.replace("-", "\\-")
data = data.replace("\t", " ")
# Single quotes prevent text rendering when found at the beginning of lines.
data = data.replace("'", "\\(aq")
return data
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(":"),
}
# 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.
date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
else:
date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
return {
"help": blender_help,
"version": blender_version,
"date": date_string,
}
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(
'.TH "BLENDER" "1" "{:s}" "Blender {:s}"\n'.format(
blender_info["date"], blender_info["version"].replace(".", "\\&.")
)
)
fh.write(r"""
.SH NAME
blender \- a full-featured 3D application""")
fh.write(r"""
.SH SYNOPSIS
.B blender [args ...] [file] [args ...]""")
fh.write(r"""
.br
.SH DESCRIPTION
.PP
.B blender
is a full-featured 3D application. It supports the entirety of the 3D pipeline - """
"""modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
Use Blender to create 3D images and animations, films and commercials, content for games, """
r"""architectural and industrial visualizations, and scientific visualizations.
https://www.blender.org""")
fh.write(r"""
.SH OPTIONS""")
fh.write("\n\n")
# Body Content.
lines = [line.rstrip() for line in blender_info["help"].split("\n")]
while lines:
l = lines.pop(0)
if l.startswith("Environment Variables:"):
fh.write('.SH "ENVIRONMENT VARIABLES"\n')
elif l.endswith(":"): # One line.
fh.write('.SS "{:s}"\n\n'.format(l))
elif l.startswith("-") or l.startswith("/"): # Can be multi line.
fh.write('.TP\n')
fh.write('.B {:s}\n'.format(man_format(l)))
while lines:
# line with no
if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]): # No white space.
break
if not l: # Second blank line.
fh.write('.IP\n')
else:
fh.write('.br\n')
l = lines.pop(0)
if l:
assert l.startswith('\t')
l = l[1:] # Remove first white-space (tab).
fh.write('{:s}\n'.format(man_format(l)))
else:
if not l.strip():
fh.write('.br\n')
else:
fh.write('{:s}\n'.format(man_format(l)))
# Footer Content.
fh.write(r"""
.br
.SH SEE ALSO
.B luxrender(1)
.br
.SH AUTHORS
This manpage was written for a Debian GNU/Linux system by Daniel Mester
<mester@uni-bremen.de> and updated by Cyril Brulebois
<cyril.brulebois@enst-bretagne.fr> and Dan Eicher <dan@trollwerks.org>.
""")
def create_argparse() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument(
"--output",
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,
required=False,
action='store_true',
help="Print additional progress."
)
return parser
def main() -> None:
parser = create_argparse()
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, blender_bin, verbose)
if verbose:
print("Written:", output_filename)
if __name__ == "__main__":
main()