diff --git a/doc/cool-uris.yaml b/doc/cool-uris.yaml
index cfbde266..4f38cf2c 100644
--- a/doc/cool-uris.yaml
+++ b/doc/cool-uris.yaml
@@ -1,12 +1,17 @@
# WARNING: This file is autogenerated by: scripts/ensure-stable-doc-urls.py
# Do not edit manually.
+__optional__:
+- doxygen_crawl.html
+- meson_options.html
annotated.html: []
classes.html: []
+debugging.html: []
deprecated.html: []
dir_63ce773eee1f9b680e6e312b48cc99ca.html: []
dir_891596f32582d3133e8915e72908625f.html: []
dir_d44c64559bbebec7f509842c48db8b23.html: []
dir_e68e8157741866f444e17edd764ebbae.html: []
+doxygen_crawl.html: []
error-index.html: []
files.html: []
functions.html: []
@@ -33,8 +38,9 @@ group__x11.html: []
index.html: []
keymap-text-format-v1.html:
- md_doc_keymap_format_text_v1.html
-md_doc_quick_guide.html: []
-modules.html: []
+md_doc_2quick-guide.html:
+- md_doc_quick_guide.html
+meson_options.html: []
pages.html: []
rule-file-format.html:
- md_doc_rules_format.html
@@ -54,6 +60,8 @@ structxkb__keymap.html: []
structxkb__rule__names.html: []
structxkb__state.html: []
todo.html: []
+topics.html:
+- modules.html
user-configuration.html:
- md_doc_user_configuration.html
xkb-intro.html: []
diff --git a/scripts/ensure-stable-doc-urls.py b/scripts/ensure-stable-doc-urls.py
index f40356b9..45d72431 100755
--- a/scripts/ensure-stable-doc-urls.py
+++ b/scripts/ensure-stable-doc-urls.py
@@ -28,12 +28,15 @@ class ExitCode(IntFlag):
NORMAL = 0
INVALID_UPDATES = 1 << 4
MISSING_UPDATES = 1 << 5
+ NON_UNIQUE_DIRECTIONS = 1 << 6
THIS_SCRIPT_PATH = Path(__file__)
RELATIVE_SCRIPT_PATH = THIS_SCRIPT_PATH.relative_to(THIS_SCRIPT_PATH.parent.parent)
REDIRECTION_DELAY = 6 # in seconds. Note: at least 6s for accessibility
+REDIRECTION_TITLE = "xkbcommon: Page Redirection"
+OPTIONAL_ENTRY = "__optional__"
# NOTE: The redirection works with the HTML tag: .
# See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#http-equiv
@@ -50,7 +53,7 @@ class ExitCode(IntFlag):
-
xkbcommon: Page Redirection
+ ${title}
@@ -87,6 +90,14 @@ def parse_page_update(update: str) -> Update:
return updateʹ
+def is_page_redirection(path: Path):
+ with path.open("rt", encoding="utf-8") as fd:
+ for line in fd:
+ if REDIRECTION_TITLE in line:
+ return True
+ return False
+
+
def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]):
"""
Update the URL registry by:
@@ -95,21 +106,35 @@ def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]):
"""
# Parse updates
updates_ = dict(map(parse_page_update, updates))
+ # Update
+ invalid_updates = set(updates_)
# Load previous registry
with registry_path.open("rt", encoding="utf-8") as fd:
- registry = yaml.safe_load(fd) or {}
+ registry: dict[str, list[str]] = yaml.safe_load(fd) or {}
+ registryʹ = dict(
+ (canonical, aliases)
+ for canonical, aliases in registry.items()
+ if canonical != OPTIONAL_ENTRY
+ )
# Expected updates
- missing_updates = set(file for file in registry if not (doc_dir / file).is_file())
- # Update
- invalid_updates = set(updates_)
- redirections = frozenset(chain(*registry.values()))
+ missing_updates = set(
+ canonical for canonical in registryʹ if not (doc_dir / canonical).is_file()
+ )
+ # Ensure each page is unique
+ for d, rs in registryʹ.items():
+ if clashes := frozenset(rs).intersection(registry):
+ print(
+ f"[ERROR] The following redirections of “{d}”",
+ f"clash with canonical directions: {clashes}",
+ )
+ exit(ExitCode.NON_UNIQUE_DIRECTIONS)
+ redirections = frozenset(chain.from_iterable(registryʹ.values()))
for file in glob.iglob("**/*.html", root_dir=doc_dir, recursive=True):
# Skip redirection pages
if file in redirections:
continue
# Get previous entry and potential update
- old = updates_.get(file)
- if old:
+ if old := updates_.get(file):
# Update old entry
invalid_updates.remove(file)
entry = registry.get(old)
@@ -137,8 +162,28 @@ def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]):
exit_code |= ExitCode.INVALID_UPDATES
if missing_updates:
for old in missing_updates:
- print(f"[ERROR] “{old}” not found and has no update.")
- exit_code |= ExitCode.MISSING_UPDATES
+ # Handle older Doxygen versions
+ if old in registry.get(OPTIONAL_ENTRY, []):
+ print(
+ "[WARNING] Handling old Doxygen version:",
+ f"skip optional “{old}”",
+ )
+ missing_updates.remove(old)
+ continue
+ old_redirections = registry[old]
+ for r in old_redirections:
+ path = doc_dir / r
+ if path.is_file() and not is_page_redirection(path):
+ print(
+ "[WARNING] Handling old Doxygen version:",
+ f"use “{r}” instead of “{old}” for the canonical direction",
+ )
+ missing_updates.remove(old)
+ break
+ else:
+ print(f"[ERROR] “{old}” not found and has no update.")
+ if missing_updates:
+ exit_code |= ExitCode.MISSING_UPDATES
if exit_code:
print("[ERROR] Processing interrupted: please fix the errors above.")
exit(exit_code.value)
@@ -146,10 +191,7 @@ def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]):
with registry_path.open("wt", encoding="utf-8") as fd:
fd.write(f"# WARNING: This file is autogenerated by: {RELATIVE_SCRIPT_PATH}\n")
fd.write("# Do not edit manually.\n")
- yaml.dump(
- registry,
- fd,
- )
+ yaml.dump(registry, fd)
def generate_redirections(registry_path: Path, doc_dir: Path):
@@ -159,22 +201,47 @@ def generate_redirections(registry_path: Path, doc_dir: Path):
cool = True
# Load registry
with registry_path.open("rt", encoding="utf-8") as fd:
- registry = yaml.safe_load(fd) or {}
- for canonical, aliases in registry.items():
+ registry: dict[str, list[str]] = yaml.safe_load(fd) or {}
+ registryʹ = dict(
+ (canonical, aliases)
+ for canonical, aliases in registry.items()
+ if canonical != OPTIONAL_ENTRY
+ )
+ for canonical, aliases in registryʹ.items():
# Check canonical path is up-to-date
if not (doc_dir / canonical).is_file():
- cool = False
- print(
- f"ERROR: missing canonical documentation page “{canonical}”. "
- f"Please update “{registry_path}” using {RELATIVE_SCRIPT_PATH}”."
- )
+ # Handle older Doxygen versions
+ if canonical in registry.get(OPTIONAL_ENTRY, []):
+ print(
+ "[WARNING] Handling old Doxygen version:",
+ f"skip optional “{canonical}”",
+ )
+ continue
+ for r in aliases:
+ path = doc_dir / r
+ if path.is_file() and not is_page_redirection(path):
+ print(
+ "[WARNING] Handling old Doxygen version:",
+ f"use “{r}” instead of “{canonical}” for the canonical direction",
+ )
+ canonical = r
+ aliases.remove(r)
+ break
+ else:
+ cool = False
+ print(
+ f"ERROR: missing canonical documentation page “{canonical}”. "
+ f"Please update “{registry_path}” using {RELATIVE_SCRIPT_PATH}”."
+ )
# Add a redirection page
for alias in aliases:
path = doc_dir / alias
with path.open("wt", encoding="utf-8") as fd:
fd.write(
REDIRECTION_PAGE_TEMPLATE.substitute(
- canonical=canonical, delay=REDIRECTION_DELAY
+ canonical=canonical,
+ delay=REDIRECTION_DELAY,
+ title=REDIRECTION_TITLE,
)
)
if not cool: