| import argparse |
| import logging |
| import os |
| import subprocess |
| import sys |
| |
| here = os.path.dirname(__file__) |
| wpt_root = os.path.abspath(os.path.join(here, "..")) |
| |
| # Directories relative to the wpt root that we want to include in the docs |
| # Sphinx doesn't support including files outside of docs/ so we temporarily symlink |
| # these directories under docs/ whilst running the build. |
| link_dirs = [ |
| "tools/wptserve", |
| "tools/certs", |
| "tools/wptrunner", |
| "tools/webtransport", |
| "tools/third_party/pywebsocket3", |
| ] |
| |
| logger = logging.getLogger() |
| |
| |
| def link_source_dirs(): |
| created = set() |
| failed = [] |
| for rel_path in link_dirs: |
| rel_path = rel_path.replace("/", os.path.sep) |
| src = os.path.join(wpt_root, rel_path) |
| dest = os.path.join(here, rel_path) |
| try: |
| dest_dir = os.path.dirname(dest) |
| if not os.path.exists(dest_dir): |
| os.makedirs(dest_dir) |
| created.add(dest_dir) |
| if not os.path.exists(dest): |
| os.symlink(src, dest, target_is_directory=True) |
| else: |
| if (not os.path.islink(dest) or |
| os.path.join(os.path.dirname(dest), os.readlink(dest)) != src): |
| # The file exists but it isn't a link or points at the wrong target |
| raise OSError("File exists") |
| except Exception as e: |
| failed.append((dest, e)) |
| else: |
| created.add(dest) |
| return created, failed |
| |
| |
| def unlink_source_dirs(created): |
| # Sort backwards in length to remove all files before getting to directory |
| for path in sorted(created, key=lambda x: -len(x)): |
| # This will also remove empty parent directories |
| if not os.path.islink(path) and os.path.isdir(path): |
| os.removedirs(path) |
| else: |
| os.unlink(path) |
| |
| |
| def get_parser(): |
| p = argparse.ArgumentParser() |
| p.add_argument("--type", default="html", help="Output type (default: html)") |
| p.add_argument("--docker", action="store_true", help="Run inside the docs docker image") |
| p.add_argument("--serve", default=None, nargs="?", const=8000, |
| type=int, help="Run a server on the specified port (default: 8000)") |
| return p |
| |
| |
| def docker_build(tag="wpt:docs"): |
| subprocess.check_call(["docker", |
| "build", |
| "--pull", |
| "--tag", tag, |
| here]) |
| |
| def docker_run(**kwargs): |
| cmd = ["docker", "run"] |
| cmd.extend(["--mount", |
| "type=bind,source=%s,target=/app/web-platform-tests" % wpt_root]) |
| if kwargs["serve"] is not None: |
| serve = str(kwargs["serve"]) |
| cmd.extend(["--expose", serve, "--publish", f"{serve}:{serve}"]) |
| cmd.extend(["-w", "/app/web-platform-tests"]) |
| if os.isatty(os.isatty(sys.stdout.fileno())): |
| cmd.append("-it") |
| cmd.extend(["wpt:docs", "./wpt"]) |
| # /app/venv is created during docker build and is always active inside the |
| # container. |
| cmd.extend(["--venv", "/app/venv", "--skip-venv-setup"]) |
| cmd.extend(["build-docs", "--type", kwargs["type"]]) |
| if kwargs["serve"] is not None: |
| cmd.extend(["--serve", str(kwargs["serve"])]) |
| logger.debug(" ".join(cmd)) |
| return subprocess.call(cmd) |
| |
| |
| def build(_venv, **kwargs): |
| if kwargs["docker"]: |
| docker_build() |
| return docker_run(**kwargs) |
| |
| out_dir = os.path.join(here, "_build") |
| try: |
| created, failed = link_source_dirs() |
| if failed: |
| failure_msg = "\n".join(f"{dest}: {err}" for (dest, err) in failed) |
| logger.error(f"Failed to create source symlinks:\n{failure_msg}") |
| sys.exit(1) |
| if kwargs["serve"] is not None: |
| executable = "sphinx-autobuild" |
| extras = ["--port", str(kwargs["serve"]), |
| "--host", "0.0.0.0", |
| "--watch", os.path.abspath(os.path.join(here, os.pardir, "resources")), |
| # Ignore changes to files specified with glob pattern |
| "--ignore", "**/flycheck_*", |
| "--ignore", "**/.*", |
| "--ignore", "**/#*", |
| "--ignore", "docs/frontend.py", |
| "--ignore", "docs/Dockerfile"] |
| else: |
| executable = "sphinx-build" |
| extras = [] |
| cmd = [executable, "-n", "-v", "-b", kwargs["type"], "-j", "auto"] + extras + [here, out_dir] |
| logger.debug(" ".join(cmd)) |
| subprocess.check_call(cmd) |
| finally: |
| unlink_source_dirs(created) |