1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
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)
|