Fix T91534: GPencil interpolate Sequence fails if stroke has 0 points
[blender.git] / doc / manpage / blender.1.py
1 #!/usr/bin/env python3
2
3 # ##### BEGIN GPL LICENSE BLOCK #####
4 #
5 #  This program is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU General Public License
7 #  as published by the Free Software Foundation; either version 2
8 #  of the License, or (at your option) any later version.
9 #
10 #  This program is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #  GNU General Public License for more details.
14 #
15 #  You should have received a copy of the GNU General Public License
16 #  along with this program; if not, write to the Free Software Foundation,
17 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 #
19 # ##### END GPL LICENSE BLOCK #####
20
21 '''
22 This script generates the blender.1 man page, embedding the help text
23 from the Blender executable itself. Invoke it as follows:
24
25     blender.1.py --blender <path-to-blender> --output <output-filename>
26
27 where <path-to-blender> is the path to the Blender executable,
28 and <output-filename> is where to write the generated man page.
29 '''
30
31 # <pep8 compliant>
32
33 import argparse
34 import os
35 import subprocess
36 import sys
37 import time
38
39 from typing import (
40     Dict,
41     TextIO,
42 )
43
44
45 def man_format(data: str) -> str:
46     data = data.replace("-", "\\-")
47     data = data.replace("\t", "  ")
48     return data
49
50
51 def blender_extract_info(blender_bin: str) -> Dict[str, str]:
52
53     blender_env = {
54         "ASAN_OPTIONS": "exitcode=0:" + os.environ.get("ASAN_OPTIONS", ""),
55     }
56
57     blender_help = subprocess.run(
58         [blender_bin, "--help"],
59         env=blender_env,
60         check=True,
61         stdout=subprocess.PIPE,
62     ).stdout.decode(encoding="utf-8")
63
64     blender_version_ouput = subprocess.run(
65         [blender_bin, "--version"],
66         env=blender_env,
67         check=True,
68         stdout=subprocess.PIPE,
69     ).stdout.decode(encoding="utf-8")
70
71     # Extract information from the version string.
72     # Note that some internal modules may print errors (e.g. color management),
73     # check for each lines prefix to ensure these aren't included.
74     blender_version = ""
75     blender_date = ""
76     for l in blender_version_ouput.split("\n"):
77         if l.startswith("Blender "):
78             # Remove 'Blender' prefix.
79             blender_version = l.split(" ", 1)[1].strip()
80         elif l.lstrip().startswith("build date:"):
81             # Remove 'build date:' prefix.
82             blender_date = l.split(":", 1)[1].strip()
83         if blender_version and blender_date:
84             break
85
86     if not blender_date:
87         # Happens when built without WITH_BUILD_INFO e.g.
88         date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
89     else:
90         date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
91
92     return {
93         "help": blender_help,
94         "version": blender_version,
95         "date": date_string,
96     }
97
98
99 def man_page_from_blender_help(fh: TextIO, blender_bin: str) -> None:
100     blender_info = blender_extract_info(blender_bin)
101
102     # Header Content.
103     fh.write(
104         '.TH "BLENDER" "1" "%s" "Blender %s"\n' %
105         (blender_info["date"], blender_info["version"].replace(".", "\\&."))
106     )
107
108     fh.write(r'''
109 .SH NAME
110 blender \- a full-featured 3D application''')
111
112     fh.write(r'''
113 .SH SYNOPSIS
114 .B blender [args ...] [file] [args ...]''')
115
116     fh.write(r'''
117 .br
118 .SH DESCRIPTION
119 .PP
120 .B blender
121 is a full-featured 3D application. It supports the entirety of the 3D pipeline - '''
122 '''modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
123
124 Use Blender to create 3D images and animations, films and commercials, content for games, '''
125 r'''architectural and industrial visualizations, and scientific visualizations.
126
127 https://www.blender.org''')
128
129     fh.write(r'''
130 .SH OPTIONS''')
131
132     fh.write("\n\n")
133
134     # Body Content.
135
136     lines = [line.rstrip() for line in blender_info["help"].split("\n")]
137
138     while lines:
139         l = lines.pop(0)
140         if l.startswith("Environment Variables:"):
141             fh.write('.SH "ENVIRONMENT VARIABLES"\n')
142         elif l.endswith(":"):  # One line.
143             fh.write('.SS "%s"\n\n' % l)
144         elif l.startswith("-") or l.startswith("/"):  # Can be multi line.
145             fh.write('.TP\n')
146             fh.write('.B %s\n' % man_format(l))
147
148             while lines:
149                 # line with no
150                 if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]):  # No white space.
151                     break
152
153                 if not l:  # Second blank line.
154                     fh.write('.IP\n')
155                 else:
156                     fh.write('.br\n')
157
158                 l = lines.pop(0)
159                 if l:
160                     assert(l.startswith('\t'))
161                     l = l[1:]  # Remove first white-space (tab).
162
163                 fh.write('%s\n' % man_format(l))
164
165         else:
166             if not l.strip():
167                 fh.write('.br\n')
168             else:
169                 fh.write('%s\n' % man_format(l))
170
171     # Footer Content.
172
173     fh.write(r'''
174 .br
175 .SH SEE ALSO
176 .B luxrender(1)
177
178 .br
179 .SH AUTHORS
180 This manpage was written for a Debian GNU/Linux system by Daniel Mester
181 <mester@uni-bremen.de> and updated by Cyril Brulebois
182 <cyril.brulebois@enst-bretagne.fr> and Dan Eicher <dan@trollwerks.org>.
183 ''')
184
185
186 def create_argparse() -> argparse.ArgumentParser:
187     parser = argparse.ArgumentParser()
188     parser.add_argument(
189         "--output",
190         required=True,
191         help="The man page to write to."
192     )
193     parser.add_argument(
194         "--blender",
195         required=True,
196         help="Path to the blender binary."
197     )
198
199     return parser
200
201
202 def main() -> None:
203     parser = create_argparse()
204     args = parser.parse_args()
205
206     blender_bin = args.blender
207     output_filename = args.output
208
209     with open(output_filename, "w", encoding="utf-8") as fh:
210         man_page_from_blender_help(fh, blender_bin)
211
212
213 if __name__ == "__main__":
214     main()