summary refs log tree commit diff
path: root/src/tests/check-potfile-list.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/check-potfile-list.py')
-rwxr-xr-xsrc/tests/check-potfile-list.py137
1 files changed, 137 insertions, 0 deletions
diff --git a/src/tests/check-potfile-list.py b/src/tests/check-potfile-list.py
new file mode 100755
index 00000000..b89743ed
--- /dev/null
+++ b/src/tests/check-potfile-list.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2025 Red Hat, Inc.
+
+from pathlib import Path
+import subprocess
+import itertools
+import shutil
+import sys
+import os
+
+xgettext = shutil.which("xgettext")
+
+# Paths relative to repo root which should be
+# excluded from pot->tree, or tree->pot checks.
+ignore_in_potfiles = ()
+ignore_in_tree = ()
+
+
+def err(s):
+    print(f"ERR: {s}", file=sys.stderr)
+
+
+def read_potfile_list(root, path):
+    with open(root / path, "r") as f:
+        for line in f.readlines():
+            stripped = line.strip()
+            if stripped.startswith("#") or len(stripped) == 0:
+                continue
+            if stripped in ignore_in_potfiles:
+                continue
+            yield (root / stripped).resolve()
+
+
+def get_existing_entries(root):
+    in_list = read_potfile_list(root, "po/POTFILES.in")
+    skip_list = read_potfile_list(root, "po/POTFILES.skip")
+    return set(itertools.chain(in_list, skip_list))
+
+
+def check_exists_in_tree(root, paths):
+    is_ok = True
+
+    for path in paths:
+        if not path.exists():
+            err(
+                f"{path.relative_to(root)} exists in POTFILES.in or POTFILES.skip, but missing in sources"
+            )
+            is_ok = False
+
+    return is_ok
+
+
+def get_gettext_args(root):
+    arg_name = "XGETTEXT_OPTIONS ="
+    with open(root / "po" / "Makevars", "r") as f:
+        for line in f.readlines():
+            if line.startswith(arg_name):
+                return line[len(arg_name) :].strip().split(" ")
+
+    raise Exception("could not get gettext args")
+
+
+def list_c_sources(root):
+    for path, _, files in os.walk(root / "src"):
+        for file in files:
+            relpath_str = str(Path(path).relative_to(root) / file)
+            extension = file.replace(".in", "").split(".").pop()
+            if extension in ["c", "h"] and relpath_str not in ignore_in_tree:
+                full_path = root / path / file
+                yield full_path.resolve()
+
+
+def gettext_dry_run(root, paths):
+    args = get_gettext_args(root)
+    process = subprocess.Popen(
+        [xgettext, "--files-from=-", "--output=-"] + args,
+        stdin=subprocess.PIPE,
+        stdout=subprocess.PIPE,
+        text=True,
+    )
+
+    for line in paths:
+        process.stdin.write(f"{line.resolve()}\n")
+    process.stdin.close()
+
+    seen = set()
+    for line in process.stdout:
+        if line.startswith("#: "):
+            out_path = Path(line.replace("#: ", "").split(":")[0])
+            if out_path not in seen:
+                seen.add(out_path)
+                yield out_path
+
+    process.stdout.close()
+    assert process.wait() == 0
+
+
+def check_exists_in_potfiles(root, pot_paths):
+    unseen_paths = filter(lambda path: path not in pot_paths, list_c_sources(root))
+
+    is_ok = True
+    for path in gettext_dry_run(root, unseen_paths):
+        err(
+            f"{path.relative_to(root)} code contains gettext macros, but missing in POTFILES.in or POTFILES.skip"
+        )
+        is_ok = False
+
+    return is_ok
+
+
+def check_potfiles():
+    root = (Path(os.path.dirname(__file__)) / "../../").resolve()
+    file_entries = get_existing_entries(root)
+    is_ok = True
+
+    # Let's first check that all the files that we
+    # have in POTFILES.* actually exist.
+    is_ok &= check_exists_in_tree(root, file_entries)
+
+    # Now the other direction -- check that all sources
+    # that should be included in POTFILES, are included.
+    is_ok &= check_exists_in_potfiles(root, file_entries)
+
+    return is_ok
+
+
+if __name__ == "__main__":
+    if xgettext is None:
+        raise Exception("xgettext is missing")
+
+    out_msg = "POTFILES consistency check: %s"
+    if check_potfiles():
+        print(out_msg % "ok")
+    else:
+        print(out_msg % "failed", file=sys.stderr)
+        sys.exit(1)