diff --git a/doc/python_api/sphinx_doc_update.py b/doc/python_api/sphinx_doc_update.py new file mode 100755 index 00000000000..c93f1676d52 --- /dev/null +++ b/doc/python_api/sphinx_doc_update.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 + +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Bastien Montagne +# +# ##### END GPL LICENSE BLOCK ##### + +# + +""" +This is a helper script to generate Blender Python API documentation (using Sphinx), and update server data using rsync. + +You'll need to specify your user login and password, obviously. + +Example usage: + + ./sphinx_doc_update.py --mirror ../../../docs/remote_api_backup/ --source ../.. --blender ../../../build_cmake/bin/blender --user foobar --password barfoo + +""" + +import os +import shutil +import subprocess +import sys +import tempfile +import zipfile + + +DEFAULT_RSYNC_SERVER = "www.blender.org" +DEFAULT_RSYNC_ROOT = "/api/" +DEFAULT_SYMLINK_ROOT = "/data/www/vhosts/www.blender.org/api" + + +def argparse_create(): + import argparse + global __doc__ + + # When --help or no args are given, print this help + usage_text = __doc__ + + parser = argparse.ArgumentParser(description=usage_text, + formatter_class=argparse.RawDescriptionHelpFormatter) + + parser.add_argument( + "--mirror", dest="mirror_dir", + metavar='PATH', required=True, + help="Path to local rsync mirror of api doc server") + parser.add_argument( + "--source", dest="source_dir", + metavar='PATH', required=True, + help="Path to Blender git repository") + parser.add_argument( + "--blender", dest="blender", + metavar='PATH', required=True, + help="Path to Blender executable") + parser.add_argument( + "--rsync-server", dest="rsync_server", default=DEFAULT_RSYNC_SERVER, + metavar='RSYNCSERVER', type=str, required=False, + help=("rsync server address")) + parser.add_argument( + "--rsync-root", dest="rsync_root", default=DEFAULT_RSYNC_ROOT, + metavar='RSYNCROOT', type=str, required=False, + help=("Root path of API doc on rsync server")) + parser.add_argument( + "--user", dest="user", + metavar='USER', type=str, required=True, + help=("User to login on rsync server")) + parser.add_argument( + "--password", dest="password", + metavar='PASSWORD', type=str, required=True, + help=("Password to login on rsync server")) + + return parser + + +def main(): + # ---------- + # Parse Args + + args = argparse_create().parse_args() + + rsync_base = "rsync://%s@%s:%s" % (args.user, args.rsync_server, args.rsync_root) + + # I) Update local mirror using rsync. + rsync_mirror_cmd = ("rsync", "--delete-after", "-avzz", rsync_base, args.mirror_dir) + subprocess.run(rsync_mirror_cmd, env=dict(os.environ, RSYNC_PASSWORD=args.password)) + + with tempfile.TemporaryDirectory() as tmp_dir: + # II) Generate doc source in temp dir. + doc_gen_cmd = (args.blender, "--background", "-noaudio", "--factory-startup", "--python-exit-code", "1", + "--python", "%s/doc/python_api/sphinx_doc_gen.py" % args.source_dir, "--", + "--output", tmp_dir) + subprocess.run(doc_gen_cmd) + + # III) Get Blender version info. + blenver = "" + getver_file = os.path.join(tmp_dir, "blendver.txt") + getver_script = ("" + "import sys, bpy\n" + "with open(sys.argv[-1], 'w') as f:\n" + " f.write('%d_%d%s_release' % (bpy.app.version[0], bpy.app.version[1], bpy.app.version_char)\n" + " if bpy.app.version_cycle in {'rc', 'release'} else '%d_%d_%d' % bpy.app.version)\n") + get_ver_cmd = (args.blender, "--background", "-noaudio", "--factory-startup", "--python-exit-code", "1", + "--python-expr", getver_script, "--", getver_file) + subprocess.run(get_ver_cmd) + with open(getver_file) as f: + blenver = f.read() + os.remove(getver_file) + + # IV) Build doc. + curr_dir = os.getcwd() + os.chdir(tmp_dir) + sphinx_cmd = ("sphinx-build", "-b", "html", "sphinx-in", "sphinx-out") + subprocess.run(sphinx_cmd) + shutil.rmtree(os.path.join("sphinx-out", ".doctrees")) + os.chdir(curr_dir) + + # V) Cleanup existing matching dir in server mirror (if any), and copy new doc. + api_name = "blender_python_api_%s" % blenver + api_dir = os.path.join(args.mirror_dir, api_name) + if os.path.exists(api_dir): + shutil.rmtree(api_dir) + os.rename(os.path.join(tmp_dir, "sphinx-out"), api_dir) + + # VI) Create zip archive. + zip_name = "blender_python_reference_%s" % blenver + zip_path = os.path.join(args.mirror_dir, zip_name) + with zipfile.ZipFile(zip_path, 'w') as zf: + for de in os.scandir(api_dir): + zf.write(de.path, arcname=os.path.join(zip_name, de.name)) + os.rename(zip_path, os.path.join(api_dir, "%s.zip" % zip_name)) + + # VII) Create symlinks and html redirects. + #~ os.symlink(os.path.join(DEFAULT_SYMLINK_ROOT, api_name, "contents.html"), os.path.join(api_dir, "index.html")) + os.symlink("./contents.html", os.path.join(api_dir, "index.html")) + if blenver.endswith("release"): + symlink = os.path.join(args.mirror_dir, "blender_python_api_current") + os.remove(symlink) + os.symlink("./%s" % api_name, symlink) + with open(os.path.join(args.mirror_dir, "250PythonDoc/index.html"), 'w') as f: + f.write("Redirecting...Redirecting..." % api_name) + else: + symlink = os.path.join(args.mirror_dir, "blender_python_api_master") + os.remove(symlink) + os.symlink("./%s" % api_name, symlink) + with open(os.path.join(args.mirror_dir, "blender_python_api/index.html"), 'w') as f: + f.write("Redirecting...Redirecting..." % api_name) + + # VIII) Upload (first do a dry-run so user can ensure everything is OK). + print("Doc generated in local mirror %s, please check it before uploading " + "(hit [Enter] to continue, [Ctrl-C] to exit):" % api_dir) + sys.stdin.read(1) + + rsync_mirror_cmd = ("rsync", "--dry-run", "--delete-after", "-avzz", args.mirror_dir, rsync_base) + subprocess.run(rsync_mirror_cmd, env=dict(os.environ, RSYNC_PASSWORD=args.password)) + + print("Rsync upload simulated, please check every thing is OK (hit [Enter] to continue, [Ctrl-C] to exit):") + sys.stdin.read(1) + + rsync_mirror_cmd = ("rsync", "--delete-after", "-avzz", args.mirror_dir, rsync_base) + subprocess.run(rsync_mirror_cmd, env=dict(os.environ, RSYNC_PASSWORD=args.password)) + + +if __name__ == "__main__": + main()