xref: /linux/scripts/sphinx-pre-install (revision c6e23912855d4848883080200e09551b6dcbc7df)
1ca9087f5SMauro Carvalho Chehab#!/usr/bin/env python3
2ca9087f5SMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0-or-later
3ca9087f5SMauro Carvalho Chehab# Copyright (c) 2017-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
4ca9087f5SMauro Carvalho Chehab#
58b45effaSMauro Carvalho Chehab# pylint: disable=C0103,C0114,C0115,C0116,C0301,C0302
68b45effaSMauro Carvalho Chehab# pylint: disable=R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,E1121
7ca9087f5SMauro Carvalho Chehab
8728648b6SMauro Carvalho Chehab# Note: this script requires at least Python 3.6 to run.
9728648b6SMauro Carvalho Chehab# Don't add changes not compatible with it, it is meant to report
10728648b6SMauro Carvalho Chehab# incompatible python versions.
11ca9087f5SMauro Carvalho Chehab
126d5f4f3dSMauro Carvalho Chehab"""
136d5f4f3dSMauro Carvalho ChehabDependency checker for Sphinx documentation Kernel build.
146d5f4f3dSMauro Carvalho Chehab
156d5f4f3dSMauro Carvalho ChehabThis module provides tools to check for all required dependencies needed to
166d5f4f3dSMauro Carvalho Chehabbuild documentation using Sphinx, including system packages, Python modules
176d5f4f3dSMauro Carvalho Chehaband LaTeX packages for PDF generation.
186d5f4f3dSMauro Carvalho Chehab
196d5f4f3dSMauro Carvalho ChehabIt detect packages for a subset of Linux distributions used by Kernel
206d5f4f3dSMauro Carvalho Chehabmaintainers, showing hints and missing dependencies.
216d5f4f3dSMauro Carvalho Chehab
226d5f4f3dSMauro Carvalho ChehabThe main class SphinxDependencyChecker handles the dependency checking logic
236d5f4f3dSMauro Carvalho Chehaband provides recommendations for installing missing packages. It supports both
246d5f4f3dSMauro Carvalho Chehabsystem package installations and  Python virtual environments. By default,
256d5f4f3dSMauro Carvalho Chehabsystem pacage install is recommended.
266d5f4f3dSMauro Carvalho Chehab"""
276d5f4f3dSMauro Carvalho Chehab
28ca9087f5SMauro Carvalho Chehabimport argparse
29ca9087f5SMauro Carvalho Chehabimport os
30ca9087f5SMauro Carvalho Chehabimport re
31ca9087f5SMauro Carvalho Chehabimport subprocess
32ca9087f5SMauro Carvalho Chehabimport sys
33ca9087f5SMauro Carvalho Chehabfrom glob import glob
34ca9087f5SMauro Carvalho Chehab
35ca9087f5SMauro Carvalho Chehab
36ca9087f5SMauro Carvalho Chehabdef parse_version(version):
37ca9087f5SMauro Carvalho Chehab    """Convert a major.minor.patch version into a tuple"""
38ca9087f5SMauro Carvalho Chehab    return tuple(int(x) for x in version.split("."))
39ca9087f5SMauro Carvalho Chehab
40ca9087f5SMauro Carvalho Chehab
41ca9087f5SMauro Carvalho Chehabdef ver_str(version):
42ca9087f5SMauro Carvalho Chehab    """Returns a version tuple as major.minor.patch"""
43ca9087f5SMauro Carvalho Chehab
44ca9087f5SMauro Carvalho Chehab    return ".".join([str(x) for x in version])
45ca9087f5SMauro Carvalho Chehab
46ca9087f5SMauro Carvalho Chehab
47ca9087f5SMauro Carvalho ChehabRECOMMENDED_VERSION = parse_version("3.4.3")
48728648b6SMauro Carvalho ChehabMIN_PYTHON_VERSION = parse_version("3.7")
49ca9087f5SMauro Carvalho Chehab
50ca9087f5SMauro Carvalho Chehab
512cab00fbSMauro Carvalho Chehabclass DepManager:
52f477c6d7SMauro Carvalho Chehab    """
53f477c6d7SMauro Carvalho Chehab    Manage package dependencies. There are three types of dependencies:
549ecda2e1SMauro Carvalho Chehab
55f477c6d7SMauro Carvalho Chehab    - System: dependencies required for docs build;
56f477c6d7SMauro Carvalho Chehab    - Python: python dependencies for a native distro Sphinx install;
57f477c6d7SMauro Carvalho Chehab    - PDF: dependencies needed by PDF builds.
58f477c6d7SMauro Carvalho Chehab
59f477c6d7SMauro Carvalho Chehab    Each dependency can be mandatory or optional. Not installing an optional
60f477c6d7SMauro Carvalho Chehab    dependency won't break the build, but will cause degradation at the
61f477c6d7SMauro Carvalho Chehab    docs output.
62f477c6d7SMauro Carvalho Chehab    """
63f477c6d7SMauro Carvalho Chehab
64f477c6d7SMauro Carvalho Chehab    # Internal types of dependencies. Don't use them outside DepManager class.
659ecda2e1SMauro Carvalho Chehab    _SYS_TYPE = 0
669ecda2e1SMauro Carvalho Chehab    _PHY_TYPE = 1
679ecda2e1SMauro Carvalho Chehab    _PDF_TYPE = 2
689ecda2e1SMauro Carvalho Chehab
69f477c6d7SMauro Carvalho Chehab    # Dependencies visible outside the class.
70f477c6d7SMauro Carvalho Chehab    # The keys are tuple with: (type, is_mandatory flag).
71f477c6d7SMauro Carvalho Chehab    #
72f477c6d7SMauro Carvalho Chehab    # Currently we're not using all optional dep types. Yet, we'll keep all
73f477c6d7SMauro Carvalho Chehab    # possible combinations here. They're not many, and that makes easier
74f477c6d7SMauro Carvalho Chehab    # if later needed and for the name() method below
759ecda2e1SMauro Carvalho Chehab
769ecda2e1SMauro Carvalho Chehab    SYSTEM_MANDATORY = (_SYS_TYPE, True)
779ecda2e1SMauro Carvalho Chehab    PYTHON_MANDATORY = (_PHY_TYPE, True)
789ecda2e1SMauro Carvalho Chehab    PDF_MANDATORY = (_PDF_TYPE, True)
799ecda2e1SMauro Carvalho Chehab
809ecda2e1SMauro Carvalho Chehab    SYSTEM_OPTIONAL = (_SYS_TYPE, False)
819ecda2e1SMauro Carvalho Chehab    PYTHON_OPTIONAL = (_PHY_TYPE, False)
829ecda2e1SMauro Carvalho Chehab    PDF_OPTIONAL = (_PDF_TYPE, True)
839ecda2e1SMauro Carvalho Chehab
842cab00fbSMauro Carvalho Chehab    def __init__(self, pdf):
85f477c6d7SMauro Carvalho Chehab        """
86f477c6d7SMauro Carvalho Chehab        Initialize internal vars:
87f477c6d7SMauro Carvalho Chehab
88f477c6d7SMauro Carvalho Chehab        - missing: missing dependencies list, containing a distro-independent
89f477c6d7SMauro Carvalho Chehab                   name for a missing dependency and its type.
90f477c6d7SMauro Carvalho Chehab        - missing_pkg: ancillary dict containing missing dependencies in
91f477c6d7SMauro Carvalho Chehab                       distro namespace, organized by type.
92f477c6d7SMauro Carvalho Chehab        - need: total number of needed dependencies. Never cleaned.
93f477c6d7SMauro Carvalho Chehab        - optional: total number of optional dependencies. Never cleaned.
946d5f4f3dSMauro Carvalho Chehab        - pdf: Is PDF support enabled?
95f477c6d7SMauro Carvalho Chehab        """
962cab00fbSMauro Carvalho Chehab        self.missing = {}
97f477c6d7SMauro Carvalho Chehab        self.missing_pkg = {}
982cab00fbSMauro Carvalho Chehab        self.need = 0
992cab00fbSMauro Carvalho Chehab        self.optional = 0
1002cab00fbSMauro Carvalho Chehab        self.pdf = pdf
1012cab00fbSMauro Carvalho Chehab
1029ecda2e1SMauro Carvalho Chehab    @staticmethod
1039ecda2e1SMauro Carvalho Chehab    def name(dtype):
104f477c6d7SMauro Carvalho Chehab        """
105f477c6d7SMauro Carvalho Chehab        Ancillary routine to output a warn/error message reporting
106f477c6d7SMauro Carvalho Chehab        missing dependencies.
107f477c6d7SMauro Carvalho Chehab        """
1082cab00fbSMauro Carvalho Chehab        if dtype[0] == DepManager._SYS_TYPE:
1099ecda2e1SMauro Carvalho Chehab            msg = "build"
1102cab00fbSMauro Carvalho Chehab        elif dtype[0] == DepManager._PHY_TYPE:
1119ecda2e1SMauro Carvalho Chehab            msg = "Python"
1129ecda2e1SMauro Carvalho Chehab        else:
1139ecda2e1SMauro Carvalho Chehab            msg = "PDF"
1149ecda2e1SMauro Carvalho Chehab
1159ecda2e1SMauro Carvalho Chehab        if dtype[1]:
1169ecda2e1SMauro Carvalho Chehab            return f"ERROR: {msg} mandatory deps missing"
1179ecda2e1SMauro Carvalho Chehab        else:
1188b45effaSMauro Carvalho Chehab            return f"Warning: {msg} optional deps missing"
1199ecda2e1SMauro Carvalho Chehab
1209ecda2e1SMauro Carvalho Chehab    @staticmethod
1219ecda2e1SMauro Carvalho Chehab    def is_optional(dtype):
122f477c6d7SMauro Carvalho Chehab        """Ancillary routine to report if a dependency is optional"""
1239ecda2e1SMauro Carvalho Chehab        return not dtype[1]
1249ecda2e1SMauro Carvalho Chehab
1259ecda2e1SMauro Carvalho Chehab    @staticmethod
1269ecda2e1SMauro Carvalho Chehab    def is_pdf(dtype):
127f477c6d7SMauro Carvalho Chehab        """Ancillary routine to report if a dependency is for PDF generation"""
1288b45effaSMauro Carvalho Chehab        if dtype[0] == DepManager._PDF_TYPE:
1299ecda2e1SMauro Carvalho Chehab            return True
1309ecda2e1SMauro Carvalho Chehab
1319ecda2e1SMauro Carvalho Chehab        return False
1329ecda2e1SMauro Carvalho Chehab
1332cab00fbSMauro Carvalho Chehab    def add_package(self, package, dtype):
134f477c6d7SMauro Carvalho Chehab        """
135f477c6d7SMauro Carvalho Chehab        Add a package at the self.missing() dictionary.
136f477c6d7SMauro Carvalho Chehab        Doesn't update missing_pkg.
137f477c6d7SMauro Carvalho Chehab        """
1382cab00fbSMauro Carvalho Chehab        is_optional = DepManager.is_optional(dtype)
1392cab00fbSMauro Carvalho Chehab        self.missing[package] = dtype
1402cab00fbSMauro Carvalho Chehab        if is_optional:
1412cab00fbSMauro Carvalho Chehab            self.optional += 1
1422cab00fbSMauro Carvalho Chehab        else:
1432cab00fbSMauro Carvalho Chehab            self.need += 1
1442cab00fbSMauro Carvalho Chehab
1452cab00fbSMauro Carvalho Chehab    def del_package(self, package):
146f477c6d7SMauro Carvalho Chehab        """
147f477c6d7SMauro Carvalho Chehab        Remove a package at the self.missing() dictionary.
148f477c6d7SMauro Carvalho Chehab        Doesn't update missing_pkg.
149f477c6d7SMauro Carvalho Chehab        """
1502cab00fbSMauro Carvalho Chehab        if package in self.missing:
1512cab00fbSMauro Carvalho Chehab            del self.missing[package]
1522cab00fbSMauro Carvalho Chehab
1532cab00fbSMauro Carvalho Chehab    def clear_deps(self):
1542cab00fbSMauro Carvalho Chehab        """
1552cab00fbSMauro Carvalho Chehab        Clear dependencies without changing needed/optional.
1562cab00fbSMauro Carvalho Chehab
1572cab00fbSMauro Carvalho Chehab        This is an ackward way to have a separate section to recommend
1582cab00fbSMauro Carvalho Chehab        a package after system main dependencies.
1592cab00fbSMauro Carvalho Chehab
160f477c6d7SMauro Carvalho Chehab        TODO: rework the logic to prevent needing it.
1612cab00fbSMauro Carvalho Chehab        """
1622cab00fbSMauro Carvalho Chehab
1632cab00fbSMauro Carvalho Chehab        self.missing = {}
164f477c6d7SMauro Carvalho Chehab        self.missing_pkg = {}
1652cab00fbSMauro Carvalho Chehab
1662cab00fbSMauro Carvalho Chehab    def check_missing(self, progs):
167f477c6d7SMauro Carvalho Chehab        """
168f477c6d7SMauro Carvalho Chehab        Update self.missing_pkg, using progs dict to convert from the
169f477c6d7SMauro Carvalho Chehab        agnostic package name to distro-specific one.
170f477c6d7SMauro Carvalho Chehab
171f477c6d7SMauro Carvalho Chehab        Returns an string with the packages to be installed, sorted and
172f477c6d7SMauro Carvalho Chehab        with eventual duplicates removed.
173f477c6d7SMauro Carvalho Chehab        """
174f477c6d7SMauro Carvalho Chehab
175f477c6d7SMauro Carvalho Chehab        self.missing_pkg = {}
1762cab00fbSMauro Carvalho Chehab
1772cab00fbSMauro Carvalho Chehab        for prog, dtype in sorted(self.missing.items()):
1782cab00fbSMauro Carvalho Chehab            # At least on some LTS distros like CentOS 7, texlive doesn't
1792cab00fbSMauro Carvalho Chehab            # provide all packages we need. When such distros are
1802cab00fbSMauro Carvalho Chehab            # detected, we have to disable PDF output.
1812cab00fbSMauro Carvalho Chehab            #
1822cab00fbSMauro Carvalho Chehab            # So, we need to ignore the packages that distros would
1832cab00fbSMauro Carvalho Chehab            # need for LaTeX to work
1842cab00fbSMauro Carvalho Chehab            if DepManager.is_pdf(dtype) and not self.pdf:
1852cab00fbSMauro Carvalho Chehab                self.optional -= 1
1862cab00fbSMauro Carvalho Chehab                continue
1872cab00fbSMauro Carvalho Chehab
188f477c6d7SMauro Carvalho Chehab            if not dtype in self.missing_pkg:
189f477c6d7SMauro Carvalho Chehab                self.missing_pkg[dtype] = []
1902cab00fbSMauro Carvalho Chehab
191f477c6d7SMauro Carvalho Chehab            self.missing_pkg[dtype].append(progs.get(prog, prog))
1922cab00fbSMauro Carvalho Chehab
1932cab00fbSMauro Carvalho Chehab        install = []
1948b45effaSMauro Carvalho Chehab        for dtype, pkgs in self.missing_pkg.items():
1958b45effaSMauro Carvalho Chehab            install += pkgs
1962cab00fbSMauro Carvalho Chehab
1972cab00fbSMauro Carvalho Chehab        return " ".join(sorted(set(install)))
1982cab00fbSMauro Carvalho Chehab
1992cab00fbSMauro Carvalho Chehab    def warn_install(self):
200f477c6d7SMauro Carvalho Chehab        """
201f477c6d7SMauro Carvalho Chehab        Emit warnings/errors related to missing packages.
202f477c6d7SMauro Carvalho Chehab        """
2032cab00fbSMauro Carvalho Chehab
2042cab00fbSMauro Carvalho Chehab        output_msg = ""
2052cab00fbSMauro Carvalho Chehab
206f477c6d7SMauro Carvalho Chehab        for dtype in sorted(self.missing_pkg.keys()):
207f477c6d7SMauro Carvalho Chehab            progs = " ".join(sorted(set(self.missing_pkg[dtype])))
2082cab00fbSMauro Carvalho Chehab
2092cab00fbSMauro Carvalho Chehab            try:
2102cab00fbSMauro Carvalho Chehab                name = DepManager.name(dtype)
2112cab00fbSMauro Carvalho Chehab                output_msg += f'{name}:\t{progs}\n'
2122cab00fbSMauro Carvalho Chehab            except KeyError:
2132cab00fbSMauro Carvalho Chehab                raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}")
2142cab00fbSMauro Carvalho Chehab
2152cab00fbSMauro Carvalho Chehab        if output_msg:
21624a34b3bSMauro Carvalho Chehab            print(f"\n{output_msg}")
2179ecda2e1SMauro Carvalho Chehab
2189bb5f0dcSMauro Carvalho Chehabclass AncillaryMethods:
2191e9ba3b6SMauro Carvalho Chehab    """
2201e9ba3b6SMauro Carvalho Chehab    Ancillary methods that checks for missing dependencies for different
2211e9ba3b6SMauro Carvalho Chehab    types of types, like binaries, python modules, rpm deps, etc.
2221e9ba3b6SMauro Carvalho Chehab    """
223ca9087f5SMauro Carvalho Chehab
224ca9087f5SMauro Carvalho Chehab    @staticmethod
225ca9087f5SMauro Carvalho Chehab    def which(prog):
226f477c6d7SMauro Carvalho Chehab        """
227f477c6d7SMauro Carvalho Chehab        Our own implementation of which(). We could instead use
228f477c6d7SMauro Carvalho Chehab        shutil.which(), but this function is simple enough.
229f477c6d7SMauro Carvalho Chehab        Probably faster to use this implementation than to import shutil.
230f477c6d7SMauro Carvalho Chehab        """
231ca9087f5SMauro Carvalho Chehab        for path in os.environ.get("PATH", "").split(":"):
232ca9087f5SMauro Carvalho Chehab            full_path = os.path.join(path, prog)
233ca9087f5SMauro Carvalho Chehab            if os.access(full_path, os.X_OK):
234ca9087f5SMauro Carvalho Chehab                return full_path
235ca9087f5SMauro Carvalho Chehab
236ca9087f5SMauro Carvalho Chehab        return None
237ca9087f5SMauro Carvalho Chehab
238ca9087f5SMauro Carvalho Chehab    @staticmethod
239728648b6SMauro Carvalho Chehab    def get_python_version(cmd):
240f477c6d7SMauro Carvalho Chehab        """
241f477c6d7SMauro Carvalho Chehab        Get python version from a Python binary. As we need to detect if
242f477c6d7SMauro Carvalho Chehab        are out there newer python binaries, we can't rely on sys.release here.
243f477c6d7SMauro Carvalho Chehab        """
244728648b6SMauro Carvalho Chehab
245728648b6SMauro Carvalho Chehab        result = SphinxDependencyChecker.run([cmd, "--version"],
246728648b6SMauro Carvalho Chehab                                            capture_output=True, text=True)
247728648b6SMauro Carvalho Chehab        version = result.stdout.strip()
248728648b6SMauro Carvalho Chehab
249728648b6SMauro Carvalho Chehab        match = re.search(r"(\d+\.\d+\.\d+)", version)
250728648b6SMauro Carvalho Chehab        if match:
251728648b6SMauro Carvalho Chehab            return parse_version(match.group(1))
252728648b6SMauro Carvalho Chehab
253728648b6SMauro Carvalho Chehab        print(f"Can't parse version {version}")
254728648b6SMauro Carvalho Chehab        return (0, 0, 0)
255728648b6SMauro Carvalho Chehab
256728648b6SMauro Carvalho Chehab    @staticmethod
257728648b6SMauro Carvalho Chehab    def find_python():
258f477c6d7SMauro Carvalho Chehab        """
259f477c6d7SMauro Carvalho Chehab        Detect if are out there any python 3.xy version newer than the
260f477c6d7SMauro Carvalho Chehab        current one.
261728648b6SMauro Carvalho Chehab
262f477c6d7SMauro Carvalho Chehab        Note: this routine is limited to up to 2 digits for python3. We
263f477c6d7SMauro Carvalho Chehab        may need to update it one day, hopefully on a distant future.
264f477c6d7SMauro Carvalho Chehab        """
265728648b6SMauro Carvalho Chehab        patterns = [
266728648b6SMauro Carvalho Chehab            "python3.[0-9]",
267728648b6SMauro Carvalho Chehab            "python3.[0-9][0-9]",
268728648b6SMauro Carvalho Chehab        ]
269728648b6SMauro Carvalho Chehab
270728648b6SMauro Carvalho Chehab        # Seek for a python binary newer than MIN_PYTHON_VERSION
271728648b6SMauro Carvalho Chehab        for path in os.getenv("PATH", "").split(":"):
272728648b6SMauro Carvalho Chehab            for pattern in patterns:
273728648b6SMauro Carvalho Chehab                for cmd in glob(os.path.join(path, pattern)):
274728648b6SMauro Carvalho Chehab                    if os.path.isfile(cmd) and os.access(cmd, os.X_OK):
275728648b6SMauro Carvalho Chehab                        version = SphinxDependencyChecker.get_python_version(cmd)
276728648b6SMauro Carvalho Chehab                        if version >= MIN_PYTHON_VERSION:
2778b45effaSMauro Carvalho Chehab                            return cmd
278728648b6SMauro Carvalho Chehab
279728648b6SMauro Carvalho Chehab    @staticmethod
280728648b6SMauro Carvalho Chehab    def check_python():
281f477c6d7SMauro Carvalho Chehab        """
282f477c6d7SMauro Carvalho Chehab        Check if the current python binary satisfies our minimal requirement
283f477c6d7SMauro Carvalho Chehab        for Sphinx build. If not, re-run with a newer version if found.
284f477c6d7SMauro Carvalho Chehab        """
285728648b6SMauro Carvalho Chehab        cur_ver = sys.version_info[:3]
286728648b6SMauro Carvalho Chehab        if cur_ver >= MIN_PYTHON_VERSION:
287637fa6b3SMauro Carvalho Chehab            ver = ver_str(cur_ver)
288637fa6b3SMauro Carvalho Chehab            print(f"Python version: {ver}")
289637fa6b3SMauro Carvalho Chehab
290637fa6b3SMauro Carvalho Chehab            # This could be useful for debugging purposes
291637fa6b3SMauro Carvalho Chehab            if SphinxDependencyChecker.which("docutils"):
292637fa6b3SMauro Carvalho Chehab                result = SphinxDependencyChecker.run(["docutils", "--version"],
293637fa6b3SMauro Carvalho Chehab                                                    capture_output=True, text=True)
294637fa6b3SMauro Carvalho Chehab                ver = result.stdout.strip()
295637fa6b3SMauro Carvalho Chehab                match = re.search(r"(\d+\.\d+\.\d+)", ver)
296637fa6b3SMauro Carvalho Chehab                if match:
297637fa6b3SMauro Carvalho Chehab                    ver = match.group(1)
298637fa6b3SMauro Carvalho Chehab
299637fa6b3SMauro Carvalho Chehab                print(f"Docutils version: {ver}")
300637fa6b3SMauro Carvalho Chehab
301728648b6SMauro Carvalho Chehab            return
302728648b6SMauro Carvalho Chehab
303728648b6SMauro Carvalho Chehab        python_ver = ver_str(cur_ver)
304728648b6SMauro Carvalho Chehab
305728648b6SMauro Carvalho Chehab        new_python_cmd = SphinxDependencyChecker.find_python()
306728648b6SMauro Carvalho Chehab        if not new_python_cmd:
3078b45effaSMauro Carvalho Chehab            print(f"ERROR: Python version {python_ver} is not spported anymore\n")
3088b45effaSMauro Carvalho Chehab            print("       Can't find a new version. This script may fail")
309728648b6SMauro Carvalho Chehab            return
310728648b6SMauro Carvalho Chehab
311728648b6SMauro Carvalho Chehab        # Restart script using the newer version
312728648b6SMauro Carvalho Chehab        script_path = os.path.abspath(sys.argv[0])
313728648b6SMauro Carvalho Chehab        args = [new_python_cmd, script_path] + sys.argv[1:]
314728648b6SMauro Carvalho Chehab
315728648b6SMauro Carvalho Chehab        print(f"Python {python_ver} not supported. Changing to {new_python_cmd}")
316728648b6SMauro Carvalho Chehab
317728648b6SMauro Carvalho Chehab        try:
318728648b6SMauro Carvalho Chehab            os.execv(new_python_cmd, args)
319728648b6SMauro Carvalho Chehab        except OSError as e:
320728648b6SMauro Carvalho Chehab            sys.exit(f"Failed to restart with {new_python_cmd}: {e}")
321728648b6SMauro Carvalho Chehab
322728648b6SMauro Carvalho Chehab    @staticmethod
323ca9087f5SMauro Carvalho Chehab    def run(*args, **kwargs):
324f477c6d7SMauro Carvalho Chehab        """
325f477c6d7SMauro Carvalho Chehab        Excecute a command, hiding its output by default.
326f477c6d7SMauro Carvalho Chehab        Preserve comatibility with older Python versions.
327f477c6d7SMauro Carvalho Chehab        """
328ca9087f5SMauro Carvalho Chehab
32956a87677SMauro Carvalho Chehab        capture_output = kwargs.pop('capture_output', False)
33056a87677SMauro Carvalho Chehab
33156a87677SMauro Carvalho Chehab        if capture_output:
33256a87677SMauro Carvalho Chehab            if 'stdout' not in kwargs:
33356a87677SMauro Carvalho Chehab                kwargs['stdout'] = subprocess.PIPE
33456a87677SMauro Carvalho Chehab            if 'stderr' not in kwargs:
33556a87677SMauro Carvalho Chehab                kwargs['stderr'] = subprocess.PIPE
33656a87677SMauro Carvalho Chehab        else:
337ca9087f5SMauro Carvalho Chehab            if 'stdout' not in kwargs:
338ca9087f5SMauro Carvalho Chehab                kwargs['stdout'] = subprocess.DEVNULL
339ca9087f5SMauro Carvalho Chehab            if 'stderr' not in kwargs:
340ca9087f5SMauro Carvalho Chehab                kwargs['stderr'] = subprocess.DEVNULL
341ca9087f5SMauro Carvalho Chehab
34256a87677SMauro Carvalho Chehab        # Don't break with older Python versions
34356a87677SMauro Carvalho Chehab        if 'text' in kwargs and sys.version_info < (3, 7):
34456a87677SMauro Carvalho Chehab            kwargs['universal_newlines'] = kwargs.pop('text')
34556a87677SMauro Carvalho Chehab
346ca9087f5SMauro Carvalho Chehab        return subprocess.run(*args, **kwargs)
347ca9087f5SMauro Carvalho Chehab
3489bb5f0dcSMauro Carvalho Chehabclass MissingCheckers(AncillaryMethods):
349f477c6d7SMauro Carvalho Chehab    """
350f477c6d7SMauro Carvalho Chehab    Contains some ancillary checkers for different types of binaries and
351f477c6d7SMauro Carvalho Chehab    package managers.
352f477c6d7SMauro Carvalho Chehab    """
3531e9ba3b6SMauro Carvalho Chehab
3549bb5f0dcSMauro Carvalho Chehab    def __init__(self, args, texlive):
355f477c6d7SMauro Carvalho Chehab        """
356f477c6d7SMauro Carvalho Chehab        Initialize its internal variables
357f477c6d7SMauro Carvalho Chehab        """
3581e9ba3b6SMauro Carvalho Chehab        self.pdf = args.pdf
3591e9ba3b6SMauro Carvalho Chehab        self.virtualenv = args.virtualenv
3601e9ba3b6SMauro Carvalho Chehab        self.version_check = args.version_check
3619bb5f0dcSMauro Carvalho Chehab        self.texlive = texlive
3621e9ba3b6SMauro Carvalho Chehab
3638b45effaSMauro Carvalho Chehab        self.min_version = (0, 0, 0)
3648b45effaSMauro Carvalho Chehab        self.cur_version = (0, 0, 0)
3658b45effaSMauro Carvalho Chehab
3661e9ba3b6SMauro Carvalho Chehab        self.deps = DepManager(self.pdf)
3671e9ba3b6SMauro Carvalho Chehab
3681e9ba3b6SMauro Carvalho Chehab        self.need_symlink = 0
3691e9ba3b6SMauro Carvalho Chehab        self.need_sphinx = 0
3709bb5f0dcSMauro Carvalho Chehab
3711e9ba3b6SMauro Carvalho Chehab        self.verbose_warn_install = 1
3721e9ba3b6SMauro Carvalho Chehab
3731e9ba3b6SMauro Carvalho Chehab        self.virtenv_dir = ""
3749bb5f0dcSMauro Carvalho Chehab        self.install = ""
3758b45effaSMauro Carvalho Chehab        self.python_cmd = ""
3768b45effaSMauro Carvalho Chehab
3778b45effaSMauro Carvalho Chehab        self.virtenv_prefix = ["sphinx_", "Sphinx_" ]
3781e9ba3b6SMauro Carvalho Chehab
3799ecda2e1SMauro Carvalho Chehab    def check_missing_file(self, files, package, dtype):
380f477c6d7SMauro Carvalho Chehab        """
381f477c6d7SMauro Carvalho Chehab        Does the file exists? If not, add it to missing dependencies.
382f477c6d7SMauro Carvalho Chehab        """
383ca9087f5SMauro Carvalho Chehab        for f in files:
384ca9087f5SMauro Carvalho Chehab            if os.path.exists(f):
385ca9087f5SMauro Carvalho Chehab                return
3862cab00fbSMauro Carvalho Chehab        self.deps.add_package(package, dtype)
387ca9087f5SMauro Carvalho Chehab
3889ecda2e1SMauro Carvalho Chehab    def check_program(self, prog, dtype):
389f477c6d7SMauro Carvalho Chehab        """
390f477c6d7SMauro Carvalho Chehab        Does the program exists and it is at the PATH?
391f477c6d7SMauro Carvalho Chehab        If not, add it to missing dependencies.
392f477c6d7SMauro Carvalho Chehab        """
393ca9087f5SMauro Carvalho Chehab        found = self.which(prog)
394ca9087f5SMauro Carvalho Chehab        if found:
395ca9087f5SMauro Carvalho Chehab            return found
396ca9087f5SMauro Carvalho Chehab
3972cab00fbSMauro Carvalho Chehab        self.deps.add_package(prog, dtype)
398ca9087f5SMauro Carvalho Chehab
399ca9087f5SMauro Carvalho Chehab        return None
400ca9087f5SMauro Carvalho Chehab
4019ecda2e1SMauro Carvalho Chehab    def check_perl_module(self, prog, dtype):
402f477c6d7SMauro Carvalho Chehab        """
403f477c6d7SMauro Carvalho Chehab        Does perl have a dependency? Is it available?
404f477c6d7SMauro Carvalho Chehab        If not, add it to missing dependencies.
405f477c6d7SMauro Carvalho Chehab
406f477c6d7SMauro Carvalho Chehab        Right now, we still need Perl for doc build, as it is required
407f477c6d7SMauro Carvalho Chehab        by some tools called at docs or kernel build time, like:
408f477c6d7SMauro Carvalho Chehab
409f477c6d7SMauro Carvalho Chehab            scripts/documentation-file-ref-check
410f477c6d7SMauro Carvalho Chehab
411f477c6d7SMauro Carvalho Chehab        Also, checkpatch is on Perl.
412f477c6d7SMauro Carvalho Chehab        """
413f477c6d7SMauro Carvalho Chehab
414ca9087f5SMauro Carvalho Chehab        # While testing with lxc download template, one of the
415ca9087f5SMauro Carvalho Chehab        # distros (Oracle) didn't have perl - nor even an option to install
416ca9087f5SMauro Carvalho Chehab        # before installing oraclelinux-release-el9 package.
417ca9087f5SMauro Carvalho Chehab        #
418ca9087f5SMauro Carvalho Chehab        # Check it before running an error. If perl is not there,
419ca9087f5SMauro Carvalho Chehab        # add it as a mandatory package, as some parts of the doc builder
420ca9087f5SMauro Carvalho Chehab        # needs it.
421ca9087f5SMauro Carvalho Chehab        if not self.which("perl"):
4222cab00fbSMauro Carvalho Chehab            self.deps.add_package("perl", DepManager.SYSTEM_MANDATORY)
4232cab00fbSMauro Carvalho Chehab            self.deps.add_package(prog, dtype)
424ca9087f5SMauro Carvalho Chehab            return
425ca9087f5SMauro Carvalho Chehab
426ca9087f5SMauro Carvalho Chehab        try:
427ca9087f5SMauro Carvalho Chehab            self.run(["perl", f"-M{prog}", "-e", "1"], check=True)
428ca9087f5SMauro Carvalho Chehab        except subprocess.CalledProcessError:
4292cab00fbSMauro Carvalho Chehab            self.deps.add_package(prog, dtype)
430ca9087f5SMauro Carvalho Chehab
4319ecda2e1SMauro Carvalho Chehab    def check_python_module(self, module, is_optional=False):
432f477c6d7SMauro Carvalho Chehab        """
433f477c6d7SMauro Carvalho Chehab        Does a python module exists outside venv? If not, add it to missing
434f477c6d7SMauro Carvalho Chehab        dependencies.
435f477c6d7SMauro Carvalho Chehab        """
4369ecda2e1SMauro Carvalho Chehab        if is_optional:
4372cab00fbSMauro Carvalho Chehab            dtype = DepManager.PYTHON_OPTIONAL
4389ecda2e1SMauro Carvalho Chehab        else:
4392cab00fbSMauro Carvalho Chehab            dtype = DepManager.PYTHON_MANDATORY
440ca9087f5SMauro Carvalho Chehab
441ca9087f5SMauro Carvalho Chehab        try:
442ca9087f5SMauro Carvalho Chehab            self.run([self.python_cmd, "-c", f"import {module}"], check=True)
443ca9087f5SMauro Carvalho Chehab        except subprocess.CalledProcessError:
4442cab00fbSMauro Carvalho Chehab            self.deps.add_package(module, dtype)
445ca9087f5SMauro Carvalho Chehab
4469ecda2e1SMauro Carvalho Chehab    def check_rpm_missing(self, pkgs, dtype):
447f477c6d7SMauro Carvalho Chehab        """
448f477c6d7SMauro Carvalho Chehab        Does a rpm package exists? If not, add it to missing dependencies.
449f477c6d7SMauro Carvalho Chehab        """
450ca9087f5SMauro Carvalho Chehab        for prog in pkgs:
451ca9087f5SMauro Carvalho Chehab            try:
452ca9087f5SMauro Carvalho Chehab                self.run(["rpm", "-q", prog], check=True)
453ca9087f5SMauro Carvalho Chehab            except subprocess.CalledProcessError:
4542cab00fbSMauro Carvalho Chehab                self.deps.add_package(prog, dtype)
455ca9087f5SMauro Carvalho Chehab
4569ecda2e1SMauro Carvalho Chehab    def check_pacman_missing(self, pkgs, dtype):
457f477c6d7SMauro Carvalho Chehab        """
458f477c6d7SMauro Carvalho Chehab        Does a pacman package exists? If not, add it to missing dependencies.
459f477c6d7SMauro Carvalho Chehab        """
460ca9087f5SMauro Carvalho Chehab        for prog in pkgs:
461ca9087f5SMauro Carvalho Chehab            try:
462ca9087f5SMauro Carvalho Chehab                self.run(["pacman", "-Q", prog], check=True)
463ca9087f5SMauro Carvalho Chehab            except subprocess.CalledProcessError:
4642cab00fbSMauro Carvalho Chehab                self.deps.add_package(prog, dtype)
465ca9087f5SMauro Carvalho Chehab
4669ecda2e1SMauro Carvalho Chehab    def check_missing_tex(self, is_optional=False):
467f477c6d7SMauro Carvalho Chehab        """
468f477c6d7SMauro Carvalho Chehab        Does a LaTeX package exists? If not, add it to missing dependencies.
469f477c6d7SMauro Carvalho Chehab        """
4709ecda2e1SMauro Carvalho Chehab        if is_optional:
4712cab00fbSMauro Carvalho Chehab            dtype = DepManager.PDF_OPTIONAL
4729ecda2e1SMauro Carvalho Chehab        else:
4732cab00fbSMauro Carvalho Chehab            dtype = DepManager.PDF_MANDATORY
4749ecda2e1SMauro Carvalho Chehab
475ca9087f5SMauro Carvalho Chehab        kpsewhich = self.which("kpsewhich")
476ca9087f5SMauro Carvalho Chehab        for prog, package in self.texlive.items():
477ca9087f5SMauro Carvalho Chehab
478ca9087f5SMauro Carvalho Chehab            # If kpsewhich is not there, just add it to deps
479ca9087f5SMauro Carvalho Chehab            if not kpsewhich:
4802cab00fbSMauro Carvalho Chehab                self.deps.add_package(package, dtype)
481ca9087f5SMauro Carvalho Chehab                continue
482ca9087f5SMauro Carvalho Chehab
483ca9087f5SMauro Carvalho Chehab            # Check if the package is needed
484ca9087f5SMauro Carvalho Chehab            try:
485ca9087f5SMauro Carvalho Chehab                result = self.run(
486ca9087f5SMauro Carvalho Chehab                    [kpsewhich, prog], stdout=subprocess.PIPE, text=True, check=True
487ca9087f5SMauro Carvalho Chehab                )
488ca9087f5SMauro Carvalho Chehab
489ca9087f5SMauro Carvalho Chehab                # Didn't find. Add it
490ca9087f5SMauro Carvalho Chehab                if not result.stdout.strip():
4912cab00fbSMauro Carvalho Chehab                    self.deps.add_package(package, dtype)
492ca9087f5SMauro Carvalho Chehab
493ca9087f5SMauro Carvalho Chehab            except subprocess.CalledProcessError:
494ca9087f5SMauro Carvalho Chehab                # kpsewhich returned an error. Add it, just in case
4952cab00fbSMauro Carvalho Chehab                self.deps.add_package(package, dtype)
496ca9087f5SMauro Carvalho Chehab
497ca9087f5SMauro Carvalho Chehab    def get_sphinx_fname(self):
498f477c6d7SMauro Carvalho Chehab        """
499f477c6d7SMauro Carvalho Chehab        Gets the binary filename for sphinx-build.
500f477c6d7SMauro Carvalho Chehab        """
501ca9087f5SMauro Carvalho Chehab        if "SPHINXBUILD" in os.environ:
502ca9087f5SMauro Carvalho Chehab            return os.environ["SPHINXBUILD"]
503ca9087f5SMauro Carvalho Chehab
504ca9087f5SMauro Carvalho Chehab        fname = "sphinx-build"
505ca9087f5SMauro Carvalho Chehab        if self.which(fname):
506ca9087f5SMauro Carvalho Chehab            return fname
507ca9087f5SMauro Carvalho Chehab
508ca9087f5SMauro Carvalho Chehab        fname = "sphinx-build-3"
509ca9087f5SMauro Carvalho Chehab        if self.which(fname):
510ca9087f5SMauro Carvalho Chehab            self.need_symlink = 1
511ca9087f5SMauro Carvalho Chehab            return fname
512ca9087f5SMauro Carvalho Chehab
513ca9087f5SMauro Carvalho Chehab        return ""
514ca9087f5SMauro Carvalho Chehab
515ca9087f5SMauro Carvalho Chehab    def get_sphinx_version(self, cmd):
516f477c6d7SMauro Carvalho Chehab        """
517f477c6d7SMauro Carvalho Chehab        Gets sphinx-build version.
518f477c6d7SMauro Carvalho Chehab        """
519ca9087f5SMauro Carvalho Chehab        try:
520ca9087f5SMauro Carvalho Chehab            result = self.run([cmd, "--version"],
521ca9087f5SMauro Carvalho Chehab                              stdout=subprocess.PIPE,
522ca9087f5SMauro Carvalho Chehab                              stderr=subprocess.STDOUT,
523ca9087f5SMauro Carvalho Chehab                              text=True, check=True)
524ca9087f5SMauro Carvalho Chehab        except (subprocess.CalledProcessError, FileNotFoundError):
525ca9087f5SMauro Carvalho Chehab            return None
526ca9087f5SMauro Carvalho Chehab
527ca9087f5SMauro Carvalho Chehab        for line in result.stdout.split("\n"):
528ca9087f5SMauro Carvalho Chehab            match = re.match(r"^sphinx-build\s+([\d\.]+)(?:\+(?:/[\da-f]+)|b\d+)?\s*$", line)
529ca9087f5SMauro Carvalho Chehab            if match:
530ca9087f5SMauro Carvalho Chehab                return parse_version(match.group(1))
531ca9087f5SMauro Carvalho Chehab
532ca9087f5SMauro Carvalho Chehab            match = re.match(r"^Sphinx.*\s+([\d\.]+)\s*$", line)
533ca9087f5SMauro Carvalho Chehab            if match:
534ca9087f5SMauro Carvalho Chehab                return parse_version(match.group(1))
535ca9087f5SMauro Carvalho Chehab
5369bb5f0dcSMauro Carvalho Chehab    def check_sphinx(self, conf):
537f477c6d7SMauro Carvalho Chehab        """
538f477c6d7SMauro Carvalho Chehab        Checks Sphinx minimal requirements
539f477c6d7SMauro Carvalho Chehab        """
540ca9087f5SMauro Carvalho Chehab        try:
5419bb5f0dcSMauro Carvalho Chehab            with open(conf, "r", encoding="utf-8") as f:
542ca9087f5SMauro Carvalho Chehab                for line in f:
543ca9087f5SMauro Carvalho Chehab                    match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line)
544ca9087f5SMauro Carvalho Chehab                    if match:
545ca9087f5SMauro Carvalho Chehab                        self.min_version = parse_version(match.group(1))
546ca9087f5SMauro Carvalho Chehab                        break
547ca9087f5SMauro Carvalho Chehab        except IOError:
5488b45effaSMauro Carvalho Chehab            sys.exit(f"Can't open {conf}")
549ca9087f5SMauro Carvalho Chehab
550ca9087f5SMauro Carvalho Chehab        if not self.min_version:
5518b45effaSMauro Carvalho Chehab            sys.exit(f"Can't get needs_sphinx version from {conf}")
552ca9087f5SMauro Carvalho Chehab
553ca9087f5SMauro Carvalho Chehab        self.virtenv_dir = self.virtenv_prefix[0] + "latest"
554ca9087f5SMauro Carvalho Chehab
555ca9087f5SMauro Carvalho Chehab        sphinx = self.get_sphinx_fname()
556ca9087f5SMauro Carvalho Chehab        if not sphinx:
557ca9087f5SMauro Carvalho Chehab            self.need_sphinx = 1
558ca9087f5SMauro Carvalho Chehab            return
559ca9087f5SMauro Carvalho Chehab
560ca9087f5SMauro Carvalho Chehab        self.cur_version = self.get_sphinx_version(sphinx)
561ca9087f5SMauro Carvalho Chehab        if not self.cur_version:
562ca9087f5SMauro Carvalho Chehab            sys.exit(f"{sphinx} didn't return its version")
563ca9087f5SMauro Carvalho Chehab
564ca9087f5SMauro Carvalho Chehab        if self.cur_version < self.min_version:
565ca9087f5SMauro Carvalho Chehab            curver = ver_str(self.cur_version)
566ca9087f5SMauro Carvalho Chehab            minver = ver_str(self.min_version)
567ca9087f5SMauro Carvalho Chehab
568ca9087f5SMauro Carvalho Chehab            print(f"ERROR: Sphinx version is {curver}. It should be >= {minver}")
569ca9087f5SMauro Carvalho Chehab            self.need_sphinx = 1
570ca9087f5SMauro Carvalho Chehab            return
571ca9087f5SMauro Carvalho Chehab
572ca9087f5SMauro Carvalho Chehab        # On version check mode, just assume Sphinx has all mandatory deps
573ca9087f5SMauro Carvalho Chehab        if self.version_check and self.cur_version >= RECOMMENDED_VERSION:
574ca9087f5SMauro Carvalho Chehab            sys.exit(0)
575ca9087f5SMauro Carvalho Chehab
576ca9087f5SMauro Carvalho Chehab    def catcheck(self, filename):
577f477c6d7SMauro Carvalho Chehab        """
578f477c6d7SMauro Carvalho Chehab        Reads a file if it exists, returning as string.
579f477c6d7SMauro Carvalho Chehab        If not found, returns an empty string.
580f477c6d7SMauro Carvalho Chehab        """
581ca9087f5SMauro Carvalho Chehab        if os.path.exists(filename):
582ca9087f5SMauro Carvalho Chehab            with open(filename, "r", encoding="utf-8") as f:
583ca9087f5SMauro Carvalho Chehab                return f.read().strip()
584ca9087f5SMauro Carvalho Chehab        return ""
585ca9087f5SMauro Carvalho Chehab
586fb22e438SMauro Carvalho Chehab    def get_system_release(self):
587fb22e438SMauro Carvalho Chehab        """
588fb22e438SMauro Carvalho Chehab        Determine the system type. There's no unique way that would work
589fb22e438SMauro Carvalho Chehab        with all distros with a minimal package install. So, several
590fb22e438SMauro Carvalho Chehab        methods are used here.
591fb22e438SMauro Carvalho Chehab
592fb22e438SMauro Carvalho Chehab        By default, it will use lsb_release function. If not available, it will
593fb22e438SMauro Carvalho Chehab        fail back to reading the known different places where the distro name
594fb22e438SMauro Carvalho Chehab        is stored.
595fb22e438SMauro Carvalho Chehab
596fb22e438SMauro Carvalho Chehab        Several modern distros now have /etc/os-release, which usually have
597fb22e438SMauro Carvalho Chehab        a decent coverage.
598fb22e438SMauro Carvalho Chehab        """
599fb22e438SMauro Carvalho Chehab
600fb22e438SMauro Carvalho Chehab        system_release = ""
601fb22e438SMauro Carvalho Chehab
602fb22e438SMauro Carvalho Chehab        if self.which("lsb_release"):
603fb22e438SMauro Carvalho Chehab            result = self.run(["lsb_release", "-d"], capture_output=True, text=True)
604fb22e438SMauro Carvalho Chehab            system_release = result.stdout.replace("Description:", "").strip()
605fb22e438SMauro Carvalho Chehab
606fb22e438SMauro Carvalho Chehab        release_files = [
607fb22e438SMauro Carvalho Chehab            "/etc/system-release",
608fb22e438SMauro Carvalho Chehab            "/etc/redhat-release",
609fb22e438SMauro Carvalho Chehab            "/etc/lsb-release",
610fb22e438SMauro Carvalho Chehab            "/etc/gentoo-release",
611fb22e438SMauro Carvalho Chehab        ]
612fb22e438SMauro Carvalho Chehab
613fb22e438SMauro Carvalho Chehab        if not system_release:
614fb22e438SMauro Carvalho Chehab            for f in release_files:
615fb22e438SMauro Carvalho Chehab                system_release = self.catcheck(f)
616fb22e438SMauro Carvalho Chehab                if system_release:
617fb22e438SMauro Carvalho Chehab                    break
618fb22e438SMauro Carvalho Chehab
619fb22e438SMauro Carvalho Chehab        # This seems more common than LSB these days
620fb22e438SMauro Carvalho Chehab        if not system_release:
621fb22e438SMauro Carvalho Chehab            os_var = {}
622fb22e438SMauro Carvalho Chehab            try:
623fb22e438SMauro Carvalho Chehab                with open("/etc/os-release", "r", encoding="utf-8") as f:
624fb22e438SMauro Carvalho Chehab                    for line in f:
625fb22e438SMauro Carvalho Chehab                        match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line)
626fb22e438SMauro Carvalho Chehab                        if match:
627fb22e438SMauro Carvalho Chehab                            os_var[match.group(1)] = match.group(2)
628fb22e438SMauro Carvalho Chehab
629fb22e438SMauro Carvalho Chehab                system_release = os_var.get("NAME", "")
630fb22e438SMauro Carvalho Chehab                if "VERSION_ID" in os_var:
631fb22e438SMauro Carvalho Chehab                    system_release += " " + os_var["VERSION_ID"]
632fb22e438SMauro Carvalho Chehab                elif "VERSION" in os_var:
633fb22e438SMauro Carvalho Chehab                    system_release += " " + os_var["VERSION"]
634fb22e438SMauro Carvalho Chehab            except IOError:
635fb22e438SMauro Carvalho Chehab                pass
636fb22e438SMauro Carvalho Chehab
637fb22e438SMauro Carvalho Chehab        if not system_release:
638fb22e438SMauro Carvalho Chehab            system_release = self.catcheck("/etc/issue")
639fb22e438SMauro Carvalho Chehab
640fb22e438SMauro Carvalho Chehab        system_release = system_release.strip()
641fb22e438SMauro Carvalho Chehab
642fb22e438SMauro Carvalho Chehab        return system_release
643fb22e438SMauro Carvalho Chehab
6449bb5f0dcSMauro Carvalho Chehabclass SphinxDependencyChecker(MissingCheckers):
6456d5f4f3dSMauro Carvalho Chehab    """
6466d5f4f3dSMauro Carvalho Chehab    Main class for checking Sphinx documentation build dependencies.
6479bb5f0dcSMauro Carvalho Chehab
6486d5f4f3dSMauro Carvalho Chehab    - Check for missing system packages;
6496d5f4f3dSMauro Carvalho Chehab    - Check for missing Python modules;
6506d5f4f3dSMauro Carvalho Chehab    - Check for missing LaTeX packages needed by PDF generation;
6516d5f4f3dSMauro Carvalho Chehab    - Propose Sphinx install via Python Virtual environment;
6526d5f4f3dSMauro Carvalho Chehab    - Propose Sphinx install via distro-specific package install.
6536d5f4f3dSMauro Carvalho Chehab    """
6549bb5f0dcSMauro Carvalho Chehab    def __init__(self, args):
6556d5f4f3dSMauro Carvalho Chehab        """Initialize checker variables"""
6566d5f4f3dSMauro Carvalho Chehab
6579bb5f0dcSMauro Carvalho Chehab        # List of required texlive packages on Fedora and OpenSuse
6589bb5f0dcSMauro Carvalho Chehab        texlive = {
6599bb5f0dcSMauro Carvalho Chehab            "amsfonts.sty":       "texlive-amsfonts",
6609bb5f0dcSMauro Carvalho Chehab            "amsmath.sty":        "texlive-amsmath",
6619bb5f0dcSMauro Carvalho Chehab            "amssymb.sty":        "texlive-amsfonts",
6629bb5f0dcSMauro Carvalho Chehab            "amsthm.sty":         "texlive-amscls",
6639bb5f0dcSMauro Carvalho Chehab            "anyfontsize.sty":    "texlive-anyfontsize",
6649bb5f0dcSMauro Carvalho Chehab            "atbegshi.sty":       "texlive-oberdiek",
6659bb5f0dcSMauro Carvalho Chehab            "bm.sty":             "texlive-tools",
6669bb5f0dcSMauro Carvalho Chehab            "capt-of.sty":        "texlive-capt-of",
6679bb5f0dcSMauro Carvalho Chehab            "cmap.sty":           "texlive-cmap",
6689bb5f0dcSMauro Carvalho Chehab            "ctexhook.sty":       "texlive-ctex",
6699bb5f0dcSMauro Carvalho Chehab            "ecrm1000.tfm":       "texlive-ec",
6709bb5f0dcSMauro Carvalho Chehab            "eqparbox.sty":       "texlive-eqparbox",
6719bb5f0dcSMauro Carvalho Chehab            "eu1enc.def":         "texlive-euenc",
6729bb5f0dcSMauro Carvalho Chehab            "fancybox.sty":       "texlive-fancybox",
6739bb5f0dcSMauro Carvalho Chehab            "fancyvrb.sty":       "texlive-fancyvrb",
6749bb5f0dcSMauro Carvalho Chehab            "float.sty":          "texlive-float",
6759bb5f0dcSMauro Carvalho Chehab            "fncychap.sty":       "texlive-fncychap",
6769bb5f0dcSMauro Carvalho Chehab            "footnote.sty":       "texlive-mdwtools",
6779bb5f0dcSMauro Carvalho Chehab            "framed.sty":         "texlive-framed",
6789bb5f0dcSMauro Carvalho Chehab            "luatex85.sty":       "texlive-luatex85",
6799bb5f0dcSMauro Carvalho Chehab            "multirow.sty":       "texlive-multirow",
6809bb5f0dcSMauro Carvalho Chehab            "needspace.sty":      "texlive-needspace",
6819bb5f0dcSMauro Carvalho Chehab            "palatino.sty":       "texlive-psnfss",
6829bb5f0dcSMauro Carvalho Chehab            "parskip.sty":        "texlive-parskip",
6839bb5f0dcSMauro Carvalho Chehab            "polyglossia.sty":    "texlive-polyglossia",
6849bb5f0dcSMauro Carvalho Chehab            "tabulary.sty":       "texlive-tabulary",
6859bb5f0dcSMauro Carvalho Chehab            "threeparttable.sty": "texlive-threeparttable",
6869bb5f0dcSMauro Carvalho Chehab            "titlesec.sty":       "texlive-titlesec",
6879bb5f0dcSMauro Carvalho Chehab            "ucs.sty":            "texlive-ucs",
6889bb5f0dcSMauro Carvalho Chehab            "upquote.sty":        "texlive-upquote",
6899bb5f0dcSMauro Carvalho Chehab            "wrapfig.sty":        "texlive-wrapfig",
6909bb5f0dcSMauro Carvalho Chehab        }
6919bb5f0dcSMauro Carvalho Chehab
6929bb5f0dcSMauro Carvalho Chehab        super().__init__(args, texlive)
6939bb5f0dcSMauro Carvalho Chehab
6949f51a1d6SMauro Carvalho Chehab        self.need_pip = False
6959bb5f0dcSMauro Carvalho Chehab        self.rec_sphinx_upgrade = 0
6969bb5f0dcSMauro Carvalho Chehab
697fb22e438SMauro Carvalho Chehab        self.system_release = self.get_system_release()
6989bb5f0dcSMauro Carvalho Chehab        self.activate_cmd = ""
6999bb5f0dcSMauro Carvalho Chehab
7009bb5f0dcSMauro Carvalho Chehab        # Some distros may not have a Sphinx shipped package compatible with
7019bb5f0dcSMauro Carvalho Chehab        # our minimal requirements
7029bb5f0dcSMauro Carvalho Chehab        self.package_supported = True
7039bb5f0dcSMauro Carvalho Chehab
7049bb5f0dcSMauro Carvalho Chehab        # Recommend a new python version
7059bb5f0dcSMauro Carvalho Chehab        self.recommend_python = None
7069bb5f0dcSMauro Carvalho Chehab
7079bb5f0dcSMauro Carvalho Chehab        # Certain hints are meant to be shown only once
70824a34b3bSMauro Carvalho Chehab        self.distro_msg = None
7099bb5f0dcSMauro Carvalho Chehab
7109bb5f0dcSMauro Carvalho Chehab        self.latest_avail_ver = (0, 0, 0)
7119bb5f0dcSMauro Carvalho Chehab        self.venv_ver = (0, 0, 0)
7129bb5f0dcSMauro Carvalho Chehab
7139bb5f0dcSMauro Carvalho Chehab        prefix = os.environ.get("srctree", ".") + "/"
7149bb5f0dcSMauro Carvalho Chehab
7159bb5f0dcSMauro Carvalho Chehab        self.conf = prefix + "Documentation/conf.py"
7169bb5f0dcSMauro Carvalho Chehab        self.requirement_file = prefix + "Documentation/sphinx/requirements.txt"
7172cab00fbSMauro Carvalho Chehab
71824a34b3bSMauro Carvalho Chehab    def get_install_progs(self, progs, cmd, extra=None):
71924a34b3bSMauro Carvalho Chehab        """
72024a34b3bSMauro Carvalho Chehab        Check for missing dependencies using the provided program mapping.
72124a34b3bSMauro Carvalho Chehab
72224a34b3bSMauro Carvalho Chehab        The actual distro-specific programs are mapped via progs argument.
72324a34b3bSMauro Carvalho Chehab        """
72424a34b3bSMauro Carvalho Chehab        install = self.deps.check_missing(progs)
72524a34b3bSMauro Carvalho Chehab
72624a34b3bSMauro Carvalho Chehab        if self.verbose_warn_install:
72724a34b3bSMauro Carvalho Chehab            self.deps.warn_install()
72824a34b3bSMauro Carvalho Chehab
72924a34b3bSMauro Carvalho Chehab        if not install:
73024a34b3bSMauro Carvalho Chehab            return
73124a34b3bSMauro Carvalho Chehab
73224a34b3bSMauro Carvalho Chehab        if cmd:
73324a34b3bSMauro Carvalho Chehab            if self.verbose_warn_install:
73424a34b3bSMauro Carvalho Chehab                msg = "You should run:"
73524a34b3bSMauro Carvalho Chehab            else:
73624a34b3bSMauro Carvalho Chehab                msg = ""
73724a34b3bSMauro Carvalho Chehab
73824a34b3bSMauro Carvalho Chehab            if extra:
73924a34b3bSMauro Carvalho Chehab                msg += "\n\t" + extra.replace("\n", "\n\t")
74024a34b3bSMauro Carvalho Chehab
74124a34b3bSMauro Carvalho Chehab            return(msg + "\n\tsudo " + cmd + " " + install)
74224a34b3bSMauro Carvalho Chehab
74324a34b3bSMauro Carvalho Chehab        return None
74424a34b3bSMauro Carvalho Chehab
745ca9087f5SMauro Carvalho Chehab    #
746ca9087f5SMauro Carvalho Chehab    # Distro-specific hints methods
747ca9087f5SMauro Carvalho Chehab    #
748ca9087f5SMauro Carvalho Chehab
749ca9087f5SMauro Carvalho Chehab    def give_debian_hints(self):
7506d5f4f3dSMauro Carvalho Chehab        """
7516d5f4f3dSMauro Carvalho Chehab        Provide package installation hints for Debian-based distros.
7526d5f4f3dSMauro Carvalho Chehab        """
753ca9087f5SMauro Carvalho Chehab        progs = {
754ca9087f5SMauro Carvalho Chehab            "Pod::Usage":    "perl-modules",
755ca9087f5SMauro Carvalho Chehab            "convert":       "imagemagick",
756ca9087f5SMauro Carvalho Chehab            "dot":           "graphviz",
757ca9087f5SMauro Carvalho Chehab            "ensurepip":     "python3-venv",
758ca9087f5SMauro Carvalho Chehab            "python-sphinx": "python3-sphinx",
759ca9087f5SMauro Carvalho Chehab            "rsvg-convert":  "librsvg2-bin",
760ca9087f5SMauro Carvalho Chehab            "virtualenv":    "virtualenv",
761ca9087f5SMauro Carvalho Chehab            "xelatex":       "texlive-xetex",
762ca9087f5SMauro Carvalho Chehab            "yaml":          "python3-yaml",
763ca9087f5SMauro Carvalho Chehab        }
764ca9087f5SMauro Carvalho Chehab
765ca9087f5SMauro Carvalho Chehab        if self.pdf:
766ca9087f5SMauro Carvalho Chehab            pdf_pkgs = {
767ca9087f5SMauro Carvalho Chehab                "fonts-dejavu": [
768ca9087f5SMauro Carvalho Chehab                    "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
769ca9087f5SMauro Carvalho Chehab                ],
770ca9087f5SMauro Carvalho Chehab                "fonts-noto-cjk": [
771ca9087f5SMauro Carvalho Chehab                    "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
772ca9087f5SMauro Carvalho Chehab                    "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
773ca9087f5SMauro Carvalho Chehab                    "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc",
774ca9087f5SMauro Carvalho Chehab                ],
7754e9a563fSMauro Carvalho Chehab                "tex-gyre": [
7764e9a563fSMauro Carvalho Chehab                    "/usr/share/texmf/tex/latex/tex-gyre/tgtermes.sty"
7774e9a563fSMauro Carvalho Chehab                ],
7784e9a563fSMauro Carvalho Chehab                "texlive-fonts-recommended": [
7794e9a563fSMauro Carvalho Chehab                    "/usr/share/texlive/texmf-dist/fonts/tfm/adobe/zapfding/pzdr.tfm",
7804e9a563fSMauro Carvalho Chehab                ],
7814e9a563fSMauro Carvalho Chehab                "texlive-lang-chinese": [
7824e9a563fSMauro Carvalho Chehab                    "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty",
7834e9a563fSMauro Carvalho Chehab                ],
784ca9087f5SMauro Carvalho Chehab            }
785ca9087f5SMauro Carvalho Chehab
786ca9087f5SMauro Carvalho Chehab            for package, files in pdf_pkgs.items():
7872cab00fbSMauro Carvalho Chehab                self.check_missing_file(files, package, DepManager.PDF_MANDATORY)
788ca9087f5SMauro Carvalho Chehab
7892cab00fbSMauro Carvalho Chehab            self.check_program("dvipng", DepManager.PDF_MANDATORY)
790ca9087f5SMauro Carvalho Chehab
791491a9951SMauro Carvalho Chehab        if not self.distro_msg:
792491a9951SMauro Carvalho Chehab            self.distro_msg = \
793491a9951SMauro Carvalho Chehab                "Note: ImageMagick is broken on some distros, affecting PDF output. For more details:\n" \
794491a9951SMauro Carvalho Chehab                "\thttps://askubuntu.com/questions/1158894/imagemagick-still-broken-using-with-usr-bin-convert"
795491a9951SMauro Carvalho Chehab
79624a34b3bSMauro Carvalho Chehab        return self.get_install_progs(progs, "apt-get install")
797ca9087f5SMauro Carvalho Chehab
798ca9087f5SMauro Carvalho Chehab    def give_redhat_hints(self):
7996d5f4f3dSMauro Carvalho Chehab        """
8006d5f4f3dSMauro Carvalho Chehab        Provide package installation hints for RedHat-based distros
8016d5f4f3dSMauro Carvalho Chehab        (Fedora, RHEL and RHEL-based variants).
8026d5f4f3dSMauro Carvalho Chehab        """
803ca9087f5SMauro Carvalho Chehab        progs = {
804ca9087f5SMauro Carvalho Chehab            "Pod::Usage":       "perl-Pod-Usage",
805ca9087f5SMauro Carvalho Chehab            "convert":          "ImageMagick",
806ca9087f5SMauro Carvalho Chehab            "dot":              "graphviz",
807ca9087f5SMauro Carvalho Chehab            "python-sphinx":    "python3-sphinx",
808ca9087f5SMauro Carvalho Chehab            "rsvg-convert":     "librsvg2-tools",
809ca9087f5SMauro Carvalho Chehab            "virtualenv":       "python3-virtualenv",
810ca9087f5SMauro Carvalho Chehab            "xelatex":          "texlive-xetex-bin",
811ca9087f5SMauro Carvalho Chehab            "yaml":             "python3-pyyaml",
812ca9087f5SMauro Carvalho Chehab        }
813ca9087f5SMauro Carvalho Chehab
814ca9087f5SMauro Carvalho Chehab        fedora_tex_pkgs = [
815ca9087f5SMauro Carvalho Chehab            "dejavu-sans-fonts",
816ca9087f5SMauro Carvalho Chehab            "dejavu-sans-mono-fonts",
817ca9087f5SMauro Carvalho Chehab            "dejavu-serif-fonts",
818ca9087f5SMauro Carvalho Chehab            "texlive-collection-fontsrecommended",
819ca9087f5SMauro Carvalho Chehab            "texlive-collection-latex",
820ca9087f5SMauro Carvalho Chehab            "texlive-xecjk",
821ca9087f5SMauro Carvalho Chehab        ]
822ca9087f5SMauro Carvalho Chehab
823272f5e03SMauro Carvalho Chehab        fedora = False
824ca9087f5SMauro Carvalho Chehab        rel = None
825ca9087f5SMauro Carvalho Chehab
826ca9087f5SMauro Carvalho Chehab        match = re.search(r"(release|Linux)\s+(\d+)", self.system_release)
827ca9087f5SMauro Carvalho Chehab        if match:
828ca9087f5SMauro Carvalho Chehab            rel = int(match.group(2))
829ca9087f5SMauro Carvalho Chehab
830ca9087f5SMauro Carvalho Chehab        if not rel:
831ca9087f5SMauro Carvalho Chehab            print("Couldn't identify release number")
8328b45effaSMauro Carvalho Chehab            noto_sans_redhat = None
833ca9087f5SMauro Carvalho Chehab            self.pdf = False
834ca9087f5SMauro Carvalho Chehab        elif re.search("Fedora", self.system_release):
835ca9087f5SMauro Carvalho Chehab            # Fedora 38 and upper use this CJK font
836ca9087f5SMauro Carvalho Chehab
837ca9087f5SMauro Carvalho Chehab            noto_sans_redhat = "google-noto-sans-cjk-fonts"
838272f5e03SMauro Carvalho Chehab            fedora = True
839ca9087f5SMauro Carvalho Chehab        else:
840ca9087f5SMauro Carvalho Chehab            # Almalinux, CentOS, RHEL, ...
841ca9087f5SMauro Carvalho Chehab
842ca9087f5SMauro Carvalho Chehab            # at least up to version 9 (and Fedora < 38), that's the CJK font
843ca9087f5SMauro Carvalho Chehab            noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"
844ca9087f5SMauro Carvalho Chehab
845ca9087f5SMauro Carvalho Chehab            progs["virtualenv"] = "python-virtualenv"
846ca9087f5SMauro Carvalho Chehab
8478f6f54c4SMauro Carvalho Chehab            if not rel or rel < 8:
8488f6f54c4SMauro Carvalho Chehab                print("ERROR: Distro not supported. Too old?")
8498f6f54c4SMauro Carvalho Chehab                return
850ca9087f5SMauro Carvalho Chehab
8518cd25652SMauro Carvalho Chehab            # RHEL 8 uses Python 3.6, which is not compatible with
8528cd25652SMauro Carvalho Chehab            # the build system anymore. Suggest Python 3.11
8538cd25652SMauro Carvalho Chehab            if rel == 8:
8549f51a1d6SMauro Carvalho Chehab                self.check_program("python3.9", DepManager.SYSTEM_MANDATORY)
8559f51a1d6SMauro Carvalho Chehab                progs["python3.9"] = "python39"
8569f51a1d6SMauro Carvalho Chehab                progs["yaml"] = "python39-pyyaml"
8579f51a1d6SMauro Carvalho Chehab
8588cd25652SMauro Carvalho Chehab                self.recommend_python = True
8598f6f54c4SMauro Carvalho Chehab
8609f51a1d6SMauro Carvalho Chehab                # There's no python39-sphinx package. Only pip is supported
8619f51a1d6SMauro Carvalho Chehab                self.package_supported = False
8629f51a1d6SMauro Carvalho Chehab
86324a34b3bSMauro Carvalho Chehab            if not self.distro_msg:
86424a34b3bSMauro Carvalho Chehab                self.distro_msg = \
86524a34b3bSMauro Carvalho Chehab                    "Note: RHEL-based distros typically require extra repositories.\n" \
8668f6f54c4SMauro Carvalho Chehab                    "For most, enabling epel and crb are enough:\n" \
86724a34b3bSMauro Carvalho Chehab                    "\tsudo dnf install -y epel-release\n" \
8688f6f54c4SMauro Carvalho Chehab                    "\tsudo dnf config-manager --set-enabled crb\n" \
869e53e6d39SMauro Carvalho Chehab                    "Yet, some may have other required repositories. Those commands could be useful:\n" \
870e53e6d39SMauro Carvalho Chehab                    "\tsudo dnf repolist all\n" \
87124a34b3bSMauro Carvalho Chehab                    "\tsudo dnf repoquery --available --info <pkgs>\n" \
87224a34b3bSMauro Carvalho Chehab                    "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want"
873ca9087f5SMauro Carvalho Chehab
874ca9087f5SMauro Carvalho Chehab        if self.pdf:
875ca9087f5SMauro Carvalho Chehab            pdf_pkgs = [
876ca9087f5SMauro Carvalho Chehab                "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
877ca9087f5SMauro Carvalho Chehab                "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc",
878ca9087f5SMauro Carvalho Chehab            ]
879ca9087f5SMauro Carvalho Chehab
8802cab00fbSMauro Carvalho Chehab            self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepManager.PDF_MANDATORY)
881ca9087f5SMauro Carvalho Chehab
8822cab00fbSMauro Carvalho Chehab            self.check_rpm_missing(fedora_tex_pkgs, DepManager.PDF_MANDATORY)
883ca9087f5SMauro Carvalho Chehab
8842cab00fbSMauro Carvalho Chehab            self.check_missing_tex(DepManager.PDF_MANDATORY)
885272f5e03SMauro Carvalho Chehab
886272f5e03SMauro Carvalho Chehab            # There's no texlive-ctex on RHEL 8 repositories. This will
887272f5e03SMauro Carvalho Chehab            # likely affect CJK pdf build only.
888272f5e03SMauro Carvalho Chehab            if not fedora and rel == 8:
8892cab00fbSMauro Carvalho Chehab                self.deps.del_package("texlive-ctex")
890272f5e03SMauro Carvalho Chehab
89124a34b3bSMauro Carvalho Chehab        return self.get_install_progs(progs, "dnf install")
892ca9087f5SMauro Carvalho Chehab
893ca9087f5SMauro Carvalho Chehab    def give_opensuse_hints(self):
8946d5f4f3dSMauro Carvalho Chehab        """
8956d5f4f3dSMauro Carvalho Chehab        Provide package installation hints for openSUSE-based distros
8966d5f4f3dSMauro Carvalho Chehab        (Leap and Tumbleweed).
8976d5f4f3dSMauro Carvalho Chehab        """
898ca9087f5SMauro Carvalho Chehab        progs = {
899ca9087f5SMauro Carvalho Chehab            "Pod::Usage":    "perl-Pod-Usage",
900ca9087f5SMauro Carvalho Chehab            "convert":       "ImageMagick",
901ca9087f5SMauro Carvalho Chehab            "dot":           "graphviz",
902ca9087f5SMauro Carvalho Chehab            "python-sphinx": "python3-sphinx",
903ca9087f5SMauro Carvalho Chehab            "virtualenv":    "python3-virtualenv",
904b2d5d61cSMauro Carvalho Chehab            "xelatex":       "texlive-xetex-bin texlive-dejavu",
905ca9087f5SMauro Carvalho Chehab            "yaml":          "python3-pyyaml",
906ca9087f5SMauro Carvalho Chehab        }
907ca9087f5SMauro Carvalho Chehab
908ca9087f5SMauro Carvalho Chehab        suse_tex_pkgs = [
909ca9087f5SMauro Carvalho Chehab            "texlive-babel-english",
910ca9087f5SMauro Carvalho Chehab            "texlive-caption",
911ca9087f5SMauro Carvalho Chehab            "texlive-colortbl",
912ca9087f5SMauro Carvalho Chehab            "texlive-courier",
913ca9087f5SMauro Carvalho Chehab            "texlive-dvips",
914ca9087f5SMauro Carvalho Chehab            "texlive-helvetic",
915ca9087f5SMauro Carvalho Chehab            "texlive-makeindex",
916ca9087f5SMauro Carvalho Chehab            "texlive-metafont",
917ca9087f5SMauro Carvalho Chehab            "texlive-metapost",
918ca9087f5SMauro Carvalho Chehab            "texlive-palatino",
919ca9087f5SMauro Carvalho Chehab            "texlive-preview",
920ca9087f5SMauro Carvalho Chehab            "texlive-times",
921ca9087f5SMauro Carvalho Chehab            "texlive-zapfchan",
922ca9087f5SMauro Carvalho Chehab            "texlive-zapfding",
923ca9087f5SMauro Carvalho Chehab        ]
924ca9087f5SMauro Carvalho Chehab
925ca9087f5SMauro Carvalho Chehab        progs["latexmk"] = "texlive-latexmk-bin"
926ca9087f5SMauro Carvalho Chehab
9272cb4877bSMauro Carvalho Chehab        match = re.search(r"(Leap)\s+(\d+).(\d)", self.system_release)
9282cb4877bSMauro Carvalho Chehab        if match:
9292cb4877bSMauro Carvalho Chehab            rel = int(match.group(2))
9302cb4877bSMauro Carvalho Chehab
9312cb4877bSMauro Carvalho Chehab            # Leap 15.x uses Python 3.6, which is not compatible with
9322cb4877bSMauro Carvalho Chehab            # the build system anymore. Suggest Python 3.11
9332cb4877bSMauro Carvalho Chehab            if rel == 15:
9342cb4877bSMauro Carvalho Chehab                if not self.which(self.python_cmd):
9359f51a1d6SMauro Carvalho Chehab                    self.check_program("python3.11", DepManager.SYSTEM_MANDATORY)
9369f51a1d6SMauro Carvalho Chehab                    progs["python3.11"] = "python311"
9378cd25652SMauro Carvalho Chehab                    self.recommend_python = True
9382cb4877bSMauro Carvalho Chehab
9392cb4877bSMauro Carvalho Chehab                progs.update({
940b2d5d61cSMauro Carvalho Chehab                    "python-sphinx": "python311-Sphinx python311-Sphinx-latex",
9412cb4877bSMauro Carvalho Chehab                    "virtualenv":    "python311-virtualenv",
942c5ffae0fSMauro Carvalho Chehab                    "yaml":          "python311-PyYAML",
9432cb4877bSMauro Carvalho Chehab                })
94494a161d9SMauro Carvalho Chehab        else:
94594a161d9SMauro Carvalho Chehab            # Tumbleweed defaults to Python 3.11
94694a161d9SMauro Carvalho Chehab
94794a161d9SMauro Carvalho Chehab            progs.update({
948b2d5d61cSMauro Carvalho Chehab                "python-sphinx": "python313-Sphinx python313-Sphinx-latex",
94994a161d9SMauro Carvalho Chehab                "virtualenv":    "python313-virtualenv",
95094a161d9SMauro Carvalho Chehab                "yaml":          "python313-PyYAML",
95194a161d9SMauro Carvalho Chehab            })
9522cb4877bSMauro Carvalho Chehab
953ca9087f5SMauro Carvalho Chehab        # FIXME: add support for installing CJK fonts
954ca9087f5SMauro Carvalho Chehab        #
955ca9087f5SMauro Carvalho Chehab        # I tried hard, but was unable to find a way to install
956ca9087f5SMauro Carvalho Chehab        # "Noto Sans CJK SC" on openSUSE
957ca9087f5SMauro Carvalho Chehab
958ca9087f5SMauro Carvalho Chehab        if self.pdf:
9592cab00fbSMauro Carvalho Chehab            self.check_rpm_missing(suse_tex_pkgs, DepManager.PDF_MANDATORY)
960ca9087f5SMauro Carvalho Chehab        if self.pdf:
9619ecda2e1SMauro Carvalho Chehab            self.check_missing_tex()
962ca9087f5SMauro Carvalho Chehab
96324a34b3bSMauro Carvalho Chehab        return self.get_install_progs(progs, "zypper install --no-recommends")
964ca9087f5SMauro Carvalho Chehab
965ca9087f5SMauro Carvalho Chehab    def give_mageia_hints(self):
9666d5f4f3dSMauro Carvalho Chehab        """
9676d5f4f3dSMauro Carvalho Chehab        Provide package installation hints for Mageia and OpenMandriva.
9686d5f4f3dSMauro Carvalho Chehab        """
969ca9087f5SMauro Carvalho Chehab        progs = {
970ca9087f5SMauro Carvalho Chehab            "Pod::Usage":    "perl-Pod-Usage",
971ca9087f5SMauro Carvalho Chehab            "convert":       "ImageMagick",
972ca9087f5SMauro Carvalho Chehab            "dot":           "graphviz",
973ca9087f5SMauro Carvalho Chehab            "python-sphinx": "python3-sphinx",
974ca9087f5SMauro Carvalho Chehab            "rsvg-convert":  "librsvg2",
975ca9087f5SMauro Carvalho Chehab            "virtualenv":    "python3-virtualenv",
976ca9087f5SMauro Carvalho Chehab            "xelatex":       "texlive",
977ca9087f5SMauro Carvalho Chehab            "yaml":          "python3-yaml",
978ca9087f5SMauro Carvalho Chehab        }
979ca9087f5SMauro Carvalho Chehab
980ca9087f5SMauro Carvalho Chehab        tex_pkgs = [
981ca9087f5SMauro Carvalho Chehab            "texlive-fontsextra",
982c71c5d6dSMauro Carvalho Chehab            "texlive-fonts-asian",
983c71c5d6dSMauro Carvalho Chehab            "fonts-ttf-dejavu",
984ca9087f5SMauro Carvalho Chehab        ]
985ca9087f5SMauro Carvalho Chehab
986ca9087f5SMauro Carvalho Chehab        if re.search(r"OpenMandriva", self.system_release):
987ca9087f5SMauro Carvalho Chehab            packager_cmd = "dnf install"
988ca9087f5SMauro Carvalho Chehab            noto_sans = "noto-sans-cjk-fonts"
989b51f8c12SMauro Carvalho Chehab            tex_pkgs = [
990b51f8c12SMauro Carvalho Chehab                "texlive-collection-basic",
991b51f8c12SMauro Carvalho Chehab                "texlive-collection-langcjk",
992b51f8c12SMauro Carvalho Chehab                "texlive-collection-fontsextra",
993b51f8c12SMauro Carvalho Chehab                "texlive-collection-fontsrecommended"
994b51f8c12SMauro Carvalho Chehab            ]
995fb08659bSMauro Carvalho Chehab
996fb08659bSMauro Carvalho Chehab            # Tested on OpenMandriva Lx 4.3
997fb08659bSMauro Carvalho Chehab            progs["convert"] = "imagemagick"
998fb08659bSMauro Carvalho Chehab            progs["yaml"] = "python-pyyaml"
9996170b1eaSMauro Carvalho Chehab            progs["python-virtualenv"] = "python-virtualenv"
10006170b1eaSMauro Carvalho Chehab            progs["python-sphinx"] = "python-sphinx"
1001b51f8c12SMauro Carvalho Chehab            progs["xelatex"] = "texlive"
10026170b1eaSMauro Carvalho Chehab
10036170b1eaSMauro Carvalho Chehab            self.check_program("python-virtualenv", DepManager.PYTHON_MANDATORY)
10046170b1eaSMauro Carvalho Chehab
10056170b1eaSMauro Carvalho Chehab            # On my tests with openMandriva LX 4.0 docker image, upgraded
10066170b1eaSMauro Carvalho Chehab            # to 4.3, python-virtualenv package is broken: it is missing
10076170b1eaSMauro Carvalho Chehab            # ensurepip. Without it, the alternative would be to run:
10086170b1eaSMauro Carvalho Chehab            # python3 -m venv --without-pip ~/sphinx_latest, but running
10096170b1eaSMauro Carvalho Chehab            # pip there won't install sphinx at venv.
10106170b1eaSMauro Carvalho Chehab            #
10116170b1eaSMauro Carvalho Chehab            # Add a note about that.
10126170b1eaSMauro Carvalho Chehab
10136170b1eaSMauro Carvalho Chehab            if not self.distro_msg:
10146170b1eaSMauro Carvalho Chehab                self.distro_msg = \
1015b51f8c12SMauro Carvalho Chehab                    "Notes:\n"\
1016b51f8c12SMauro Carvalho Chehab                    "1. for venv, ensurepip could be broken, preventing its install method.\n" \
1017b51f8c12SMauro Carvalho Chehab                    "2. at least on OpenMandriva LX 4.3, texlive packages seem broken"
1018fb08659bSMauro Carvalho Chehab
1019ca9087f5SMauro Carvalho Chehab        else:
1020ca9087f5SMauro Carvalho Chehab            packager_cmd = "urpmi"
1021ca9087f5SMauro Carvalho Chehab            noto_sans = "google-noto-sans-cjk-ttc-fonts"
1022ca9087f5SMauro Carvalho Chehab
1023ca9087f5SMauro Carvalho Chehab        progs["latexmk"] = "texlive-collection-basic"
1024ca9087f5SMauro Carvalho Chehab
1025ca9087f5SMauro Carvalho Chehab        if self.pdf:
1026ca9087f5SMauro Carvalho Chehab            pdf_pkgs = [
1027ca9087f5SMauro Carvalho Chehab                "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
1028ca9087f5SMauro Carvalho Chehab                "/usr/share/fonts/TTF/NotoSans-Regular.ttf",
1029ca9087f5SMauro Carvalho Chehab            ]
1030ca9087f5SMauro Carvalho Chehab
10312cab00fbSMauro Carvalho Chehab            self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY)
10322cab00fbSMauro Carvalho Chehab            self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY)
1033ca9087f5SMauro Carvalho Chehab
103424a34b3bSMauro Carvalho Chehab        return self.get_install_progs(progs, packager_cmd)
1035ca9087f5SMauro Carvalho Chehab
1036ca9087f5SMauro Carvalho Chehab    def give_arch_linux_hints(self):
10376d5f4f3dSMauro Carvalho Chehab        """
10386d5f4f3dSMauro Carvalho Chehab        Provide package installation hints for ArchLinux.
10396d5f4f3dSMauro Carvalho Chehab        """
1040ca9087f5SMauro Carvalho Chehab        progs = {
1041ca9087f5SMauro Carvalho Chehab            "convert":      "imagemagick",
1042ca9087f5SMauro Carvalho Chehab            "dot":          "graphviz",
1043ca9087f5SMauro Carvalho Chehab            "latexmk":      "texlive-core",
1044ca9087f5SMauro Carvalho Chehab            "rsvg-convert": "extra/librsvg",
1045ca9087f5SMauro Carvalho Chehab            "virtualenv":   "python-virtualenv",
1046ca9087f5SMauro Carvalho Chehab            "xelatex":      "texlive-xetex",
1047ca9087f5SMauro Carvalho Chehab            "yaml":         "python-yaml",
1048ca9087f5SMauro Carvalho Chehab        }
1049ca9087f5SMauro Carvalho Chehab
1050ca9087f5SMauro Carvalho Chehab        archlinux_tex_pkgs = [
1051*c6e23912SMauro Carvalho Chehab            "texlive-basic",
1052*c6e23912SMauro Carvalho Chehab            "texlive-binextra",
1053ca9087f5SMauro Carvalho Chehab            "texlive-core",
1054*c6e23912SMauro Carvalho Chehab            "texlive-fontsrecommended",
1055*c6e23912SMauro Carvalho Chehab            "texlive-langchinese",
1056*c6e23912SMauro Carvalho Chehab            "texlive-langcjk",
1057ca9087f5SMauro Carvalho Chehab            "texlive-latexextra",
1058ca9087f5SMauro Carvalho Chehab            "ttf-dejavu",
1059ca9087f5SMauro Carvalho Chehab        ]
1060ca9087f5SMauro Carvalho Chehab
1061ca9087f5SMauro Carvalho Chehab        if self.pdf:
106224a34b3bSMauro Carvalho Chehab            self.check_pacman_missing(archlinux_tex_pkgs,
106324a34b3bSMauro Carvalho Chehab                                      DepManager.PDF_MANDATORY)
1064ca9087f5SMauro Carvalho Chehab
106524a34b3bSMauro Carvalho Chehab            self.check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"],
1066ca9087f5SMauro Carvalho Chehab                                    "noto-fonts-cjk",
106724a34b3bSMauro Carvalho Chehab                                    DepManager.PDF_MANDATORY)
1068ca9087f5SMauro Carvalho Chehab
10692cab00fbSMauro Carvalho Chehab
107024a34b3bSMauro Carvalho Chehab        return self.get_install_progs(progs, "pacman -S")
1071ca9087f5SMauro Carvalho Chehab
1072ca9087f5SMauro Carvalho Chehab    def give_gentoo_hints(self):
10736d5f4f3dSMauro Carvalho Chehab        """
10746d5f4f3dSMauro Carvalho Chehab        Provide package installation hints for Gentoo.
10756d5f4f3dSMauro Carvalho Chehab        """
10769ff5c2f5SMauro Carvalho Chehab        texlive_deps = [
10774509d36cSMauro Carvalho Chehab            "dev-texlive/texlive-fontsrecommended",
10789ff5c2f5SMauro Carvalho Chehab            "dev-texlive/texlive-latexextra",
10799ff5c2f5SMauro Carvalho Chehab            "dev-texlive/texlive-xetex",
10809ff5c2f5SMauro Carvalho Chehab            "media-fonts/dejavu",
10819ff5c2f5SMauro Carvalho Chehab        ]
10829ff5c2f5SMauro Carvalho Chehab
1083ca9087f5SMauro Carvalho Chehab        progs = {
1084ca9087f5SMauro Carvalho Chehab            "convert":       "media-gfx/imagemagick",
1085ca9087f5SMauro Carvalho Chehab            "dot":           "media-gfx/graphviz",
1086ca9087f5SMauro Carvalho Chehab            "rsvg-convert":  "gnome-base/librsvg",
1087ca9087f5SMauro Carvalho Chehab            "virtualenv":    "dev-python/virtualenv",
10889ff5c2f5SMauro Carvalho Chehab            "xelatex":       " ".join(texlive_deps),
1089ca9087f5SMauro Carvalho Chehab            "yaml":          "dev-python/pyyaml",
1090582b0f95SMauro Carvalho Chehab            "python-sphinx": "dev-python/sphinx",
1091ca9087f5SMauro Carvalho Chehab        }
1092ca9087f5SMauro Carvalho Chehab
1093ca9087f5SMauro Carvalho Chehab        if self.pdf:
1094ca9087f5SMauro Carvalho Chehab            pdf_pkgs = {
1095ca9087f5SMauro Carvalho Chehab                "media-fonts/dejavu": [
1096ca9087f5SMauro Carvalho Chehab                    "/usr/share/fonts/dejavu/DejaVuSans.ttf",
1097ca9087f5SMauro Carvalho Chehab                ],
1098ca9087f5SMauro Carvalho Chehab                "media-fonts/noto-cjk": [
1099ca9087f5SMauro Carvalho Chehab                    "/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf",
1100ca9087f5SMauro Carvalho Chehab                    "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc",
1101ca9087f5SMauro Carvalho Chehab                ],
1102ca9087f5SMauro Carvalho Chehab            }
1103ca9087f5SMauro Carvalho Chehab            for package, files in pdf_pkgs.items():
11042cab00fbSMauro Carvalho Chehab                self.check_missing_file(files, package, DepManager.PDF_MANDATORY)
1105ca9087f5SMauro Carvalho Chehab
11061a7da749SMauro Carvalho Chehab        # Handling dependencies is a nightmare, as Gentoo refuses to emerge
11071a7da749SMauro Carvalho Chehab        # some packages if there's no package.use file describing them.
11081a7da749SMauro Carvalho Chehab        # To make it worse, compilation flags shall also be present there
11091a7da749SMauro Carvalho Chehab        # for some packages. If USE is not perfect, error/warning messages
11101a7da749SMauro Carvalho Chehab        #   like those are shown:
11111a7da749SMauro Carvalho Chehab        #
11121a7da749SMauro Carvalho Chehab        #   !!! The following binary packages have been ignored due to non matching USE:
11131a7da749SMauro Carvalho Chehab        #
11141a7da749SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg
11151a7da749SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg
11161a7da749SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg
11171a7da749SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg
11181a7da749SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 python_single_target_python3_12 -python_single_target_python3_13 qt6 svg
11191a7da749SMauro Carvalho Chehab        #    =media-fonts/noto-cjk-20190416 X
11201a7da749SMauro Carvalho Chehab        #    =app-text/texlive-core-2024-r1 X cjk -xetex
11211a7da749SMauro Carvalho Chehab        #    =app-text/texlive-core-2024-r1 X -xetex
11221a7da749SMauro Carvalho Chehab        #    =app-text/texlive-core-2024-r1 -xetex
11231a7da749SMauro Carvalho Chehab        #    =dev-libs/zziplib-0.13.79-r1 sdl
11241a7da749SMauro Carvalho Chehab        #
11251a7da749SMauro Carvalho Chehab        # And will ignore such packages, installing the remaining ones. That
11261a7da749SMauro Carvalho Chehab        # affects mostly the image extension and PDF generation.
1127ca9087f5SMauro Carvalho Chehab
11281a7da749SMauro Carvalho Chehab        # Package dependencies and the minimal needed args:
11291a7da749SMauro Carvalho Chehab        portages = {
11301a7da749SMauro Carvalho Chehab            "graphviz": "media-gfx/graphviz",
11311a7da749SMauro Carvalho Chehab            "imagemagick": "media-gfx/imagemagick",
11321a7da749SMauro Carvalho Chehab            "media-libs": "media-libs/harfbuzz icu",
11331a7da749SMauro Carvalho Chehab            "media-fonts": "media-fonts/noto-cjk",
11341a7da749SMauro Carvalho Chehab            "texlive": "app-text/texlive-core xetex",
11351a7da749SMauro Carvalho Chehab            "zziblib": "dev-libs/zziplib sdl",
11361a7da749SMauro Carvalho Chehab        }
1137582b0f95SMauro Carvalho Chehab
113824a34b3bSMauro Carvalho Chehab        extra_cmds = ""
113924a34b3bSMauro Carvalho Chehab        if not self.distro_msg:
114024a34b3bSMauro Carvalho Chehab            self.distro_msg = "Note: Gentoo requires package.use to be adjusted before emerging packages"
114124a34b3bSMauro Carvalho Chehab
11421a7da749SMauro Carvalho Chehab            use_base = "/etc/portage/package.use"
11431a7da749SMauro Carvalho Chehab            files = glob(f"{use_base}/*")
11441a7da749SMauro Carvalho Chehab
11451a7da749SMauro Carvalho Chehab            for fname, portage in portages.items():
11461a7da749SMauro Carvalho Chehab                install = False
11471a7da749SMauro Carvalho Chehab
11488b45effaSMauro Carvalho Chehab                while install is False:
11491a7da749SMauro Carvalho Chehab                    if not files:
11501a7da749SMauro Carvalho Chehab                        # No files under package.usage. Install all
11511a7da749SMauro Carvalho Chehab                        install = True
11521a7da749SMauro Carvalho Chehab                        break
11531a7da749SMauro Carvalho Chehab
11541a7da749SMauro Carvalho Chehab                    args = portage.split(" ")
11551a7da749SMauro Carvalho Chehab
11561a7da749SMauro Carvalho Chehab                    name = args.pop(0)
11571a7da749SMauro Carvalho Chehab
11581a7da749SMauro Carvalho Chehab                    cmd = ["grep", "-l", "-E", rf"^{name}\b" ] + files
11591a7da749SMauro Carvalho Chehab                    result = self.run(cmd, stdout=subprocess.PIPE, text=True)
11601a7da749SMauro Carvalho Chehab                    if result.returncode or not result.stdout.strip():
11611a7da749SMauro Carvalho Chehab                        # File containing portage name not found
11621a7da749SMauro Carvalho Chehab                        install = True
11631a7da749SMauro Carvalho Chehab                        break
11641a7da749SMauro Carvalho Chehab
11651a7da749SMauro Carvalho Chehab                    # Ensure that needed USE flags are present
11661a7da749SMauro Carvalho Chehab                    if args:
11671a7da749SMauro Carvalho Chehab                        match_fname = result.stdout.strip()
11681a7da749SMauro Carvalho Chehab                        with open(match_fname, 'r', encoding='utf8',
11691a7da749SMauro Carvalho Chehab                                errors='backslashreplace') as fp:
11701a7da749SMauro Carvalho Chehab                            for line in fp:
11711a7da749SMauro Carvalho Chehab                                for arg in args:
11721a7da749SMauro Carvalho Chehab                                    if arg.startswith("-"):
11731a7da749SMauro Carvalho Chehab                                        continue
11741a7da749SMauro Carvalho Chehab
11751a7da749SMauro Carvalho Chehab                                if not re.search(rf"\s*{arg}\b", line):
11761a7da749SMauro Carvalho Chehab                                    # Needed file argument not found
11771a7da749SMauro Carvalho Chehab                                    install = True
11781a7da749SMauro Carvalho Chehab                                    break
11791a7da749SMauro Carvalho Chehab
11801a7da749SMauro Carvalho Chehab                    # Everything looks ok, don't install
11811a7da749SMauro Carvalho Chehab                    break
11821a7da749SMauro Carvalho Chehab
11831a7da749SMauro Carvalho Chehab                # emit a code to setup missing USE
11841a7da749SMauro Carvalho Chehab                if install:
118524a34b3bSMauro Carvalho Chehab                    extra_cmds += (f"sudo su -c 'echo \"{portage}\" > {use_base}/{fname}'\n")
118612bdcf89SMauro Carvalho Chehab
11871a7da749SMauro Carvalho Chehab        # Now, we can use emerge and let it respect USE
118824a34b3bSMauro Carvalho Chehab        return self.get_install_progs(progs,
118924a34b3bSMauro Carvalho Chehab                                      "emerge --ask --changed-use --binpkg-respect-use=y",
119024a34b3bSMauro Carvalho Chehab                                      extra_cmds)
1191ca9087f5SMauro Carvalho Chehab
119224a34b3bSMauro Carvalho Chehab    def get_install(self):
11936d5f4f3dSMauro Carvalho Chehab        """
119424a34b3bSMauro Carvalho Chehab        OS-specific hints logic. Seeks for a hinter. If found, use it to
119524a34b3bSMauro Carvalho Chehab        provide package-manager specific install commands.
11966d5f4f3dSMauro Carvalho Chehab
119724a34b3bSMauro Carvalho Chehab        Otherwise, outputs install instructions for the meta-packages.
119824a34b3bSMauro Carvalho Chehab
119924a34b3bSMauro Carvalho Chehab        Returns a string with the command to be executed to install the
120024a34b3bSMauro Carvalho Chehab        the needed packages, if distro found. Otherwise, return just a
120124a34b3bSMauro Carvalho Chehab        list of packages that require installation.
12026d5f4f3dSMauro Carvalho Chehab        """
1203ca9087f5SMauro Carvalho Chehab        os_hints = {
1204ca9087f5SMauro Carvalho Chehab            re.compile("Red Hat Enterprise Linux"):   self.give_redhat_hints,
1205ca9087f5SMauro Carvalho Chehab            re.compile("Fedora"):                     self.give_redhat_hints,
1206ca9087f5SMauro Carvalho Chehab            re.compile("AlmaLinux"):                  self.give_redhat_hints,
1207ca9087f5SMauro Carvalho Chehab            re.compile("Amazon Linux"):               self.give_redhat_hints,
1208ca9087f5SMauro Carvalho Chehab            re.compile("CentOS"):                     self.give_redhat_hints,
1209ca9087f5SMauro Carvalho Chehab            re.compile("openEuler"):                  self.give_redhat_hints,
1210ca9087f5SMauro Carvalho Chehab            re.compile("Oracle Linux Server"):        self.give_redhat_hints,
1211ca9087f5SMauro Carvalho Chehab            re.compile("Rocky Linux"):                self.give_redhat_hints,
1212ca9087f5SMauro Carvalho Chehab            re.compile("Springdale Open Enterprise"): self.give_redhat_hints,
1213ca9087f5SMauro Carvalho Chehab
1214ca9087f5SMauro Carvalho Chehab            re.compile("Ubuntu"):                     self.give_debian_hints,
1215ca9087f5SMauro Carvalho Chehab            re.compile("Debian"):                     self.give_debian_hints,
1216ca9087f5SMauro Carvalho Chehab            re.compile("Devuan"):                     self.give_debian_hints,
1217ca9087f5SMauro Carvalho Chehab            re.compile("Kali"):                       self.give_debian_hints,
1218ca9087f5SMauro Carvalho Chehab            re.compile("Mint"):                       self.give_debian_hints,
1219ca9087f5SMauro Carvalho Chehab
1220ca9087f5SMauro Carvalho Chehab            re.compile("openSUSE"):                   self.give_opensuse_hints,
1221ca9087f5SMauro Carvalho Chehab
1222ca9087f5SMauro Carvalho Chehab            re.compile("Mageia"):                     self.give_mageia_hints,
1223ca9087f5SMauro Carvalho Chehab            re.compile("OpenMandriva"):               self.give_mageia_hints,
1224ca9087f5SMauro Carvalho Chehab
1225ca9087f5SMauro Carvalho Chehab            re.compile("Arch Linux"):                 self.give_arch_linux_hints,
1226ca9087f5SMauro Carvalho Chehab            re.compile("Gentoo"):                     self.give_gentoo_hints,
1227ca9087f5SMauro Carvalho Chehab        }
1228ca9087f5SMauro Carvalho Chehab
1229ca9087f5SMauro Carvalho Chehab        # If the OS is detected, use per-OS hint logic
1230ca9087f5SMauro Carvalho Chehab        for regex, os_hint in os_hints.items():
1231ca9087f5SMauro Carvalho Chehab            if regex.search(self.system_release):
123224a34b3bSMauro Carvalho Chehab                return os_hint()
1233ca9087f5SMauro Carvalho Chehab
1234ca9087f5SMauro Carvalho Chehab        #
1235ca9087f5SMauro Carvalho Chehab        # Fall-back to generic hint code for other distros
1236ca9087f5SMauro Carvalho Chehab        # That's far from ideal, specially for LaTeX dependencies.
1237ca9087f5SMauro Carvalho Chehab        #
1238ca9087f5SMauro Carvalho Chehab        progs = {"sphinx-build": "sphinx"}
1239ca9087f5SMauro Carvalho Chehab        if self.pdf:
12409ecda2e1SMauro Carvalho Chehab            self.check_missing_tex()
1241ca9087f5SMauro Carvalho Chehab
124224a34b3bSMauro Carvalho Chehab        self.distro_msg = \
124324a34b3bSMauro Carvalho Chehab            f"I don't know distro {self.system_release}.\n" \
124424a34b3bSMauro Carvalho Chehab            "So, I can't provide you a hint with the install procedure.\n" \
1245491a9951SMauro Carvalho Chehab            "There are likely missing dependencies."
1246ca9087f5SMauro Carvalho Chehab
124724a34b3bSMauro Carvalho Chehab        return self.get_install_progs(progs, None)
1248ca9087f5SMauro Carvalho Chehab
1249ca9087f5SMauro Carvalho Chehab    #
1250ca9087f5SMauro Carvalho Chehab    # Common dependencies
1251ca9087f5SMauro Carvalho Chehab    #
1252ca9087f5SMauro Carvalho Chehab    def deactivate_help(self):
12536d5f4f3dSMauro Carvalho Chehab        """
12546d5f4f3dSMauro Carvalho Chehab        Print a helper message to disable a virtual environment.
12556d5f4f3dSMauro Carvalho Chehab        """
12566d5f4f3dSMauro Carvalho Chehab
1257ca9087f5SMauro Carvalho Chehab        print("\n    If you want to exit the virtualenv, you can use:")
1258ca9087f5SMauro Carvalho Chehab        print("\tdeactivate")
1259ca9087f5SMauro Carvalho Chehab
1260ca9087f5SMauro Carvalho Chehab    def get_virtenv(self):
12616d5f4f3dSMauro Carvalho Chehab        """
12626d5f4f3dSMauro Carvalho Chehab        Give a hint about how to activate an already-existing virtual
12636d5f4f3dSMauro Carvalho Chehab        environment containing sphinx-build.
12646d5f4f3dSMauro Carvalho Chehab
12656d5f4f3dSMauro Carvalho Chehab        Returns a tuble with (activate_cmd_path, sphinx_version) with
12666d5f4f3dSMauro Carvalho Chehab        the newest available virtual env.
12676d5f4f3dSMauro Carvalho Chehab        """
12686d5f4f3dSMauro Carvalho Chehab
1269ca9087f5SMauro Carvalho Chehab        cwd = os.getcwd()
1270ca9087f5SMauro Carvalho Chehab
1271ca9087f5SMauro Carvalho Chehab        activates = []
1272ca9087f5SMauro Carvalho Chehab
1273ca9087f5SMauro Carvalho Chehab        # Add all sphinx prefixes with possible version numbers
1274ca9087f5SMauro Carvalho Chehab        for p in self.virtenv_prefix:
1275ca9087f5SMauro Carvalho Chehab            activates += glob(f"{cwd}/{p}[0-9]*/bin/activate")
1276ca9087f5SMauro Carvalho Chehab
1277ca9087f5SMauro Carvalho Chehab        activates.sort(reverse=True, key=str.lower)
1278ca9087f5SMauro Carvalho Chehab
1279ca9087f5SMauro Carvalho Chehab        # Place sphinx_latest first, if it exists
1280ca9087f5SMauro Carvalho Chehab        for p in self.virtenv_prefix:
1281ca9087f5SMauro Carvalho Chehab            activates = glob(f"{cwd}/{p}*latest/bin/activate") + activates
1282ca9087f5SMauro Carvalho Chehab
1283ca9087f5SMauro Carvalho Chehab        ver = (0, 0, 0)
1284ca9087f5SMauro Carvalho Chehab        for f in activates:
1285ca9087f5SMauro Carvalho Chehab            # Discard too old Sphinx virtual environments
1286ca9087f5SMauro Carvalho Chehab            match = re.search(r"(\d+)\.(\d+)\.(\d+)", f)
1287ca9087f5SMauro Carvalho Chehab            if match:
1288ca9087f5SMauro Carvalho Chehab                ver = (int(match.group(1)), int(match.group(2)), int(match.group(3)))
1289ca9087f5SMauro Carvalho Chehab
1290ca9087f5SMauro Carvalho Chehab                if ver < self.min_version:
1291ca9087f5SMauro Carvalho Chehab                    continue
1292ca9087f5SMauro Carvalho Chehab
1293ca9087f5SMauro Carvalho Chehab            sphinx_cmd = f.replace("activate", "sphinx-build")
1294ca9087f5SMauro Carvalho Chehab            if not os.path.isfile(sphinx_cmd):
1295ca9087f5SMauro Carvalho Chehab                continue
1296ca9087f5SMauro Carvalho Chehab
1297ca9087f5SMauro Carvalho Chehab            ver = self.get_sphinx_version(sphinx_cmd)
1298ca9087f5SMauro Carvalho Chehab
1299ca9087f5SMauro Carvalho Chehab            if not ver:
1300ca9087f5SMauro Carvalho Chehab                venv_dir = f.replace("/bin/activate", "")
1301ca9087f5SMauro Carvalho Chehab                print(f"Warning: virtual environment {venv_dir} is not working.\n" \
1302ca9087f5SMauro Carvalho Chehab                      "Python version upgrade? Remove it with:\n\n" \
1303ca9087f5SMauro Carvalho Chehab                      "\trm -rf {venv_dir}\n\n")
1304ca9087f5SMauro Carvalho Chehab            else:
1305ca9087f5SMauro Carvalho Chehab                if self.need_sphinx and ver >= self.min_version:
1306ca9087f5SMauro Carvalho Chehab                    return (f, ver)
1307ca9087f5SMauro Carvalho Chehab                elif parse_version(ver) > self.cur_version:
1308ca9087f5SMauro Carvalho Chehab                    return (f, ver)
1309ca9087f5SMauro Carvalho Chehab
1310ca9087f5SMauro Carvalho Chehab        return ("", ver)
1311ca9087f5SMauro Carvalho Chehab
1312ca9087f5SMauro Carvalho Chehab    def recommend_sphinx_upgrade(self):
13136d5f4f3dSMauro Carvalho Chehab        """
13146d5f4f3dSMauro Carvalho Chehab        Check if Sphinx needs to be upgraded.
13156d5f4f3dSMauro Carvalho Chehab
13166d5f4f3dSMauro Carvalho Chehab        Returns a tuple with the higest available Sphinx version if found.
13176d5f4f3dSMauro Carvalho Chehab        Otherwise, returns None to indicate either that no upgrade is needed
13186d5f4f3dSMauro Carvalho Chehab        or no venv was found.
13196d5f4f3dSMauro Carvalho Chehab        """
13206d5f4f3dSMauro Carvalho Chehab
1321ca9087f5SMauro Carvalho Chehab        # Avoid running sphinx-builds from venv if cur_version is good
1322ca9087f5SMauro Carvalho Chehab        if self.cur_version and self.cur_version >= RECOMMENDED_VERSION:
1323ca9087f5SMauro Carvalho Chehab            self.latest_avail_ver = self.cur_version
1324ca9087f5SMauro Carvalho Chehab            return None
1325ca9087f5SMauro Carvalho Chehab
1326ca9087f5SMauro Carvalho Chehab        # Get the highest version from sphinx_*/bin/sphinx-build and the
1327ca9087f5SMauro Carvalho Chehab        # corresponding command to activate the venv/virtenv
1328ca9087f5SMauro Carvalho Chehab        self.activate_cmd, self.venv_ver = self.get_virtenv()
1329ca9087f5SMauro Carvalho Chehab
1330ca9087f5SMauro Carvalho Chehab        # Store the highest version from Sphinx existing virtualenvs
1331ca9087f5SMauro Carvalho Chehab        if self.activate_cmd and self.venv_ver > self.cur_version:
1332ca9087f5SMauro Carvalho Chehab            self.latest_avail_ver = self.venv_ver
1333ca9087f5SMauro Carvalho Chehab        else:
1334ca9087f5SMauro Carvalho Chehab            if self.cur_version:
1335ca9087f5SMauro Carvalho Chehab                self.latest_avail_ver = self.cur_version
1336ca9087f5SMauro Carvalho Chehab            else:
1337ca9087f5SMauro Carvalho Chehab                self.latest_avail_ver = (0, 0, 0)
1338ca9087f5SMauro Carvalho Chehab
1339ca9087f5SMauro Carvalho Chehab        # As we don't know package version of Sphinx, and there's no
1340ca9087f5SMauro Carvalho Chehab        # virtual environments, don't check if upgrades are needed
1341ca9087f5SMauro Carvalho Chehab        if not self.virtualenv:
1342ca9087f5SMauro Carvalho Chehab            if not self.latest_avail_ver:
1343ca9087f5SMauro Carvalho Chehab                return None
1344ca9087f5SMauro Carvalho Chehab
1345ca9087f5SMauro Carvalho Chehab            return self.latest_avail_ver
1346ca9087f5SMauro Carvalho Chehab
1347ca9087f5SMauro Carvalho Chehab        # Either there are already a virtual env or a new one should be created
13489f51a1d6SMauro Carvalho Chehab        self.need_pip = True
1349ca9087f5SMauro Carvalho Chehab
1350ca9087f5SMauro Carvalho Chehab        if not self.latest_avail_ver:
1351ca9087f5SMauro Carvalho Chehab            return None
1352ca9087f5SMauro Carvalho Chehab
1353ca9087f5SMauro Carvalho Chehab        # Return if the reason is due to an upgrade or not
1354ca9087f5SMauro Carvalho Chehab        if self.latest_avail_ver != (0, 0, 0):
1355ca9087f5SMauro Carvalho Chehab            if self.latest_avail_ver < RECOMMENDED_VERSION:
1356ca9087f5SMauro Carvalho Chehab                self.rec_sphinx_upgrade = 1
1357ca9087f5SMauro Carvalho Chehab
1358ca9087f5SMauro Carvalho Chehab        return self.latest_avail_ver
1359ca9087f5SMauro Carvalho Chehab
13601ad72e9dSMauro Carvalho Chehab    def recommend_package(self):
13616d5f4f3dSMauro Carvalho Chehab        """
13626d5f4f3dSMauro Carvalho Chehab        Recommend installing Sphinx as a distro-specific package.
13636d5f4f3dSMauro Carvalho Chehab        """
13641ad72e9dSMauro Carvalho Chehab
13651ad72e9dSMauro Carvalho Chehab        print("\n2) As a package with:")
13661ad72e9dSMauro Carvalho Chehab
13672cab00fbSMauro Carvalho Chehab        old_need = self.deps.need
13682cab00fbSMauro Carvalho Chehab        old_optional = self.deps.optional
13692cab00fbSMauro Carvalho Chehab
13701ad72e9dSMauro Carvalho Chehab        self.pdf = False
13718b45effaSMauro Carvalho Chehab        self.deps.optional = 0
13721ad72e9dSMauro Carvalho Chehab        old_verbose = self.verbose_warn_install
13731ad72e9dSMauro Carvalho Chehab        self.verbose_warn_install = 0
13741ad72e9dSMauro Carvalho Chehab
13752cab00fbSMauro Carvalho Chehab        self.deps.clear_deps()
13762cab00fbSMauro Carvalho Chehab
13772cab00fbSMauro Carvalho Chehab        self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY)
13781ad72e9dSMauro Carvalho Chehab
137924a34b3bSMauro Carvalho Chehab        cmd = self.get_install()
138024a34b3bSMauro Carvalho Chehab        if cmd:
138124a34b3bSMauro Carvalho Chehab            print(cmd)
13821ad72e9dSMauro Carvalho Chehab
13838b45effaSMauro Carvalho Chehab        self.deps.need = old_need
13848b45effaSMauro Carvalho Chehab        self.deps.optional = old_optional
13851ad72e9dSMauro Carvalho Chehab        self.verbose_warn_install = old_verbose
13861ad72e9dSMauro Carvalho Chehab
1387ca9087f5SMauro Carvalho Chehab    def recommend_sphinx_version(self, virtualenv_cmd):
13886d5f4f3dSMauro Carvalho Chehab        """
13896d5f4f3dSMauro Carvalho Chehab        Provide recommendations for installing or upgrading Sphinx based
13906d5f4f3dSMauro Carvalho Chehab        on current version.
13916d5f4f3dSMauro Carvalho Chehab
13926d5f4f3dSMauro Carvalho Chehab        The logic here is complex, as it have to deal with different versions:
13936d5f4f3dSMauro Carvalho Chehab
13946d5f4f3dSMauro Carvalho Chehab        - minimal supported version;
13956d5f4f3dSMauro Carvalho Chehab        - minimal PDF version;
13966d5f4f3dSMauro Carvalho Chehab        - recommended version.
13976d5f4f3dSMauro Carvalho Chehab
13986d5f4f3dSMauro Carvalho Chehab        It also needs to work fine with both distro's package and
13996d5f4f3dSMauro Carvalho Chehab        venv/virtualenv
14006d5f4f3dSMauro Carvalho Chehab        """
1401ca9087f5SMauro Carvalho Chehab
14028cd25652SMauro Carvalho Chehab        if self.recommend_python:
14039f51a1d6SMauro Carvalho Chehab            cur_ver = sys.version_info[:3]
14049f51a1d6SMauro Carvalho Chehab            if cur_ver < MIN_PYTHON_VERSION:
14059f51a1d6SMauro Carvalho Chehab                print(f"\nPython version {cur_ver} is incompatible with doc build.\n" \
14068cd25652SMauro Carvalho Chehab                    "Please upgrade it and re-run.\n")
14078cd25652SMauro Carvalho Chehab                return
14088cd25652SMauro Carvalho Chehab
1409ca9087f5SMauro Carvalho Chehab        # Version is OK. Nothing to do.
1410ca9087f5SMauro Carvalho Chehab        if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION:
1411ca9087f5SMauro Carvalho Chehab            return
1412ca9087f5SMauro Carvalho Chehab
1413cccc5389SMauro Carvalho Chehab        if self.latest_avail_ver:
1414cccc5389SMauro Carvalho Chehab            latest_avail_ver = ver_str(self.latest_avail_ver)
1415cccc5389SMauro Carvalho Chehab
1416ca9087f5SMauro Carvalho Chehab        if not self.need_sphinx:
1417ca9087f5SMauro Carvalho Chehab            # sphinx-build is present and its version is >= $min_version
1418ca9087f5SMauro Carvalho Chehab
1419ca9087f5SMauro Carvalho Chehab            # only recommend enabling a newer virtenv version if makes sense.
1420ca9087f5SMauro Carvalho Chehab            if self.latest_avail_ver and self.latest_avail_ver > self.cur_version:
1421cccc5389SMauro Carvalho Chehab                print(f"\nYou may also use the newer Sphinx version {latest_avail_ver} with:")
1422ca9087f5SMauro Carvalho Chehab                if f"{self.virtenv_prefix}" in os.getcwd():
1423ca9087f5SMauro Carvalho Chehab                    print("\tdeactivate")
1424ca9087f5SMauro Carvalho Chehab                print(f"\t. {self.activate_cmd}")
1425ca9087f5SMauro Carvalho Chehab                self.deactivate_help()
1426ca9087f5SMauro Carvalho Chehab                return
1427ca9087f5SMauro Carvalho Chehab
1428ca9087f5SMauro Carvalho Chehab            if self.latest_avail_ver and self.latest_avail_ver >= RECOMMENDED_VERSION:
1429ca9087f5SMauro Carvalho Chehab                return
1430ca9087f5SMauro Carvalho Chehab
1431ca9087f5SMauro Carvalho Chehab        if not self.virtualenv:
1432ca9087f5SMauro Carvalho Chehab            # No sphinx either via package or via virtenv. As we can't
1433ca9087f5SMauro Carvalho Chehab            # Compare the versions here, just return, recommending the
1434ca9087f5SMauro Carvalho Chehab            # user to install it from the package distro.
1435ca9087f5SMauro Carvalho Chehab            if not self.latest_avail_ver or self.latest_avail_ver == (0, 0, 0):
1436ca9087f5SMauro Carvalho Chehab                return
1437ca9087f5SMauro Carvalho Chehab
1438ca9087f5SMauro Carvalho Chehab            # User doesn't want a virtenv recommendation, but he already
1439ca9087f5SMauro Carvalho Chehab            # installed one via virtenv with a newer version.
1440ca9087f5SMauro Carvalho Chehab            # So, print commands to enable it
1441ca9087f5SMauro Carvalho Chehab            if self.latest_avail_ver > self.cur_version:
1442cccc5389SMauro Carvalho Chehab                print(f"\nYou may also use the Sphinx virtualenv version {latest_avail_ver} with:")
1443ca9087f5SMauro Carvalho Chehab                if f"{self.virtenv_prefix}" in os.getcwd():
1444ca9087f5SMauro Carvalho Chehab                    print("\tdeactivate")
1445ca9087f5SMauro Carvalho Chehab                print(f"\t. {self.activate_cmd}")
1446ca9087f5SMauro Carvalho Chehab                self.deactivate_help()
1447ca9087f5SMauro Carvalho Chehab                return
1448ca9087f5SMauro Carvalho Chehab            print("\n")
1449ca9087f5SMauro Carvalho Chehab        else:
1450ca9087f5SMauro Carvalho Chehab            if self.need_sphinx:
14512cab00fbSMauro Carvalho Chehab                self.deps.need += 1
1452ca9087f5SMauro Carvalho Chehab
1453ca9087f5SMauro Carvalho Chehab        # Suggest newer versions if current ones are too old
1454ca9087f5SMauro Carvalho Chehab        if self.latest_avail_ver and self.latest_avail_ver >= self.min_version:
1455ca9087f5SMauro Carvalho Chehab            if self.latest_avail_ver >= RECOMMENDED_VERSION:
1456cccc5389SMauro Carvalho Chehab                print(f"\nNeed to activate Sphinx (version {latest_avail_ver}) on virtualenv with:")
1457ca9087f5SMauro Carvalho Chehab                print(f"\t. {self.activate_cmd}")
1458ca9087f5SMauro Carvalho Chehab                self.deactivate_help()
1459ca9087f5SMauro Carvalho Chehab                return
1460ca9087f5SMauro Carvalho Chehab
1461ca9087f5SMauro Carvalho Chehab            # Version is above the minimal required one, but may be
1462ca9087f5SMauro Carvalho Chehab            # below the recommended one. So, print warnings/notes
1463ca9087f5SMauro Carvalho Chehab            if self.latest_avail_ver < RECOMMENDED_VERSION:
1464ca9087f5SMauro Carvalho Chehab                print(f"Warning: It is recommended at least Sphinx version {RECOMMENDED_VERSION}.")
1465ca9087f5SMauro Carvalho Chehab
1466ca9087f5SMauro Carvalho Chehab        # At this point, either it needs Sphinx or upgrade is recommended,
1467ca9087f5SMauro Carvalho Chehab        # both via pip
1468ca9087f5SMauro Carvalho Chehab
1469ca9087f5SMauro Carvalho Chehab        if self.rec_sphinx_upgrade:
1470ca9087f5SMauro Carvalho Chehab            if not self.virtualenv:
1471ca9087f5SMauro Carvalho Chehab                print("Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n")
1472ca9087f5SMauro Carvalho Chehab            else:
1473ca9087f5SMauro Carvalho Chehab                print("To upgrade Sphinx, use:\n\n")
1474ca9087f5SMauro Carvalho Chehab        else:
1475ca9087f5SMauro Carvalho Chehab            print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n")
1476ca9087f5SMauro Carvalho Chehab
1477f25bf12aSMauro Carvalho Chehab        if not virtualenv_cmd:
1478f25bf12aSMauro Carvalho Chehab            print("   Currently not possible.\n")
1479f25bf12aSMauro Carvalho Chehab            print("   Please upgrade Python to a newer version and run this script again")
1480f25bf12aSMauro Carvalho Chehab        else:
1481ca9087f5SMauro Carvalho Chehab            print(f"\t{virtualenv_cmd} {self.virtenv_dir}")
1482ca9087f5SMauro Carvalho Chehab            print(f"\t. {self.virtenv_dir}/bin/activate")
1483ca9087f5SMauro Carvalho Chehab            print(f"\tpip install -r {self.requirement_file}")
1484ca9087f5SMauro Carvalho Chehab            self.deactivate_help()
1485ca9087f5SMauro Carvalho Chehab
14861ad72e9dSMauro Carvalho Chehab        if self.package_supported:
14871ad72e9dSMauro Carvalho Chehab            self.recommend_package()
1488ca9087f5SMauro Carvalho Chehab
1489ca9087f5SMauro Carvalho Chehab        print("\n" \
14906db1d397SMauro Carvalho Chehab              "   Please note that Sphinx currentlys produce false-positive\n" \
14916db1d397SMauro Carvalho Chehab              "   warnings when the same name is used for more than one type (functions,\n" \
1492ca9087f5SMauro Carvalho Chehab              "   structs, enums,...). This is known Sphinx bug. For more details, see:\n" \
1493ca9087f5SMauro Carvalho Chehab              "\thttps://github.com/sphinx-doc/sphinx/pull/8313")
1494ca9087f5SMauro Carvalho Chehab
1495ca9087f5SMauro Carvalho Chehab    def check_needs(self):
14966d5f4f3dSMauro Carvalho Chehab        """
14976d5f4f3dSMauro Carvalho Chehab        Main method that checks needed dependencies and provides
14986d5f4f3dSMauro Carvalho Chehab        recommendations.
14996d5f4f3dSMauro Carvalho Chehab        """
1500f25bf12aSMauro Carvalho Chehab        self.python_cmd = sys.executable
1501ca9087f5SMauro Carvalho Chehab
1502ca9087f5SMauro Carvalho Chehab        # Check if Sphinx is already accessible from current environment
15039bb5f0dcSMauro Carvalho Chehab        self.check_sphinx(self.conf)
1504ca9087f5SMauro Carvalho Chehab
1505ca9087f5SMauro Carvalho Chehab        if self.system_release:
1506ca9087f5SMauro Carvalho Chehab            print(f"Detected OS: {self.system_release}.")
1507ca9087f5SMauro Carvalho Chehab        else:
1508ca9087f5SMauro Carvalho Chehab            print("Unknown OS")
1509ca9087f5SMauro Carvalho Chehab        if self.cur_version != (0, 0, 0):
1510ca9087f5SMauro Carvalho Chehab            ver = ver_str(self.cur_version)
1511ca9087f5SMauro Carvalho Chehab            print(f"Sphinx version: {ver}\n")
1512ca9087f5SMauro Carvalho Chehab
1513ca9087f5SMauro Carvalho Chehab        # Check the type of virtual env, depending on Python version
1514f25bf12aSMauro Carvalho Chehab        virtualenv_cmd = None
1515ca9087f5SMauro Carvalho Chehab
1516f25bf12aSMauro Carvalho Chehab        if sys.version_info < MIN_PYTHON_VERSION:
1517f25bf12aSMauro Carvalho Chehab            min_ver = ver_str(MIN_PYTHON_VERSION)
1518f25bf12aSMauro Carvalho Chehab            print(f"ERROR: at least python {min_ver} is required to build the kernel docs")
1519f25bf12aSMauro Carvalho Chehab            self.need_sphinx = 1
1520ca9087f5SMauro Carvalho Chehab
1521ca9087f5SMauro Carvalho Chehab        self.venv_ver = self.recommend_sphinx_upgrade()
1522ca9087f5SMauro Carvalho Chehab
1523ca9087f5SMauro Carvalho Chehab        if self.need_pip:
1524f25bf12aSMauro Carvalho Chehab            if sys.version_info < MIN_PYTHON_VERSION:
1525f25bf12aSMauro Carvalho Chehab                self.need_pip = False
1526f25bf12aSMauro Carvalho Chehab                print("Warning: python version is not supported.")
1527ca9087f5SMauro Carvalho Chehab            else:
1528ca9087f5SMauro Carvalho Chehab                virtualenv_cmd = f"{self.python_cmd} -m venv"
15299ecda2e1SMauro Carvalho Chehab                self.check_python_module("ensurepip")
1530ca9087f5SMauro Carvalho Chehab
1531ca9087f5SMauro Carvalho Chehab        # Check for needed programs/tools
15322cab00fbSMauro Carvalho Chehab        self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY)
15339ecda2e1SMauro Carvalho Chehab
15342cab00fbSMauro Carvalho Chehab        self.check_program("make", DepManager.SYSTEM_MANDATORY)
1535df4d2f96SMauro Carvalho Chehab        self.check_program("which", DepManager.SYSTEM_MANDATORY)
15369ecda2e1SMauro Carvalho Chehab
15372cab00fbSMauro Carvalho Chehab        self.check_program("dot", DepManager.SYSTEM_OPTIONAL)
15382cab00fbSMauro Carvalho Chehab        self.check_program("convert", DepManager.SYSTEM_OPTIONAL)
15399ecda2e1SMauro Carvalho Chehab
15409ecda2e1SMauro Carvalho Chehab        self.check_python_module("yaml")
1541ca9087f5SMauro Carvalho Chehab
1542ca9087f5SMauro Carvalho Chehab        if self.pdf:
15432cab00fbSMauro Carvalho Chehab            self.check_program("xelatex", DepManager.PDF_MANDATORY)
15442cab00fbSMauro Carvalho Chehab            self.check_program("rsvg-convert", DepManager.PDF_MANDATORY)
15452cab00fbSMauro Carvalho Chehab            self.check_program("latexmk", DepManager.PDF_MANDATORY)
1546ca9087f5SMauro Carvalho Chehab
1547ca9087f5SMauro Carvalho Chehab        # Do distro-specific checks and output distro-install commands
154824a34b3bSMauro Carvalho Chehab        cmd = self.get_install()
154924a34b3bSMauro Carvalho Chehab        if cmd:
155024a34b3bSMauro Carvalho Chehab            print(cmd)
155124a34b3bSMauro Carvalho Chehab
155224a34b3bSMauro Carvalho Chehab        # If distro requires some special instructions, print here.
155324a34b3bSMauro Carvalho Chehab        # Please notice that get_install() needs to be called first.
155424a34b3bSMauro Carvalho Chehab        if self.distro_msg:
155524a34b3bSMauro Carvalho Chehab            print("\n" + self.distro_msg)
1556ca9087f5SMauro Carvalho Chehab
1557ca9087f5SMauro Carvalho Chehab        if not self.python_cmd:
1558ca9087f5SMauro Carvalho Chehab            if self.need == 1:
1559ca9087f5SMauro Carvalho Chehab                sys.exit("Can't build as 1 mandatory dependency is missing")
1560ca9087f5SMauro Carvalho Chehab            elif self.need:
1561ca9087f5SMauro Carvalho Chehab                sys.exit(f"Can't build as {self.need} mandatory dependencies are missing")
1562ca9087f5SMauro Carvalho Chehab
1563ca9087f5SMauro Carvalho Chehab        # Check if sphinx-build is called sphinx-build-3
1564ca9087f5SMauro Carvalho Chehab        if self.need_symlink:
1565ca9087f5SMauro Carvalho Chehab            sphinx_path = self.which("sphinx-build-3")
1566ca9087f5SMauro Carvalho Chehab            if sphinx_path:
1567ca9087f5SMauro Carvalho Chehab                print(f"\tsudo ln -sf {sphinx_path} /usr/bin/sphinx-build\n")
1568ca9087f5SMauro Carvalho Chehab
1569ca9087f5SMauro Carvalho Chehab        self.recommend_sphinx_version(virtualenv_cmd)
1570ca9087f5SMauro Carvalho Chehab        print("")
1571ca9087f5SMauro Carvalho Chehab
15728b45effaSMauro Carvalho Chehab        if not self.deps.optional:
1573ca9087f5SMauro Carvalho Chehab            print("All optional dependencies are met.")
1574ca9087f5SMauro Carvalho Chehab
15758b45effaSMauro Carvalho Chehab        if self.deps.need == 1:
1576ca9087f5SMauro Carvalho Chehab            sys.exit("Can't build as 1 mandatory dependency is missing")
15778b45effaSMauro Carvalho Chehab        elif self.deps.need:
15788b45effaSMauro Carvalho Chehab            sys.exit(f"Can't build as {self.deps.need} mandatory dependencies are missing")
1579ca9087f5SMauro Carvalho Chehab
1580ca9087f5SMauro Carvalho Chehab        print("Needed package dependencies are met.")
1581ca9087f5SMauro Carvalho Chehab
1582ca9087f5SMauro Carvalho ChehabDESCRIPTION = """
1583ca9087f5SMauro Carvalho ChehabProcess some flags related to Sphinx installation and documentation build.
1584ca9087f5SMauro Carvalho Chehab"""
1585ca9087f5SMauro Carvalho Chehab
1586ca9087f5SMauro Carvalho Chehab
1587ca9087f5SMauro Carvalho Chehabdef main():
15886d5f4f3dSMauro Carvalho Chehab    """Main function"""
1589ca9087f5SMauro Carvalho Chehab    parser = argparse.ArgumentParser(description=DESCRIPTION)
1590ca9087f5SMauro Carvalho Chehab
1591ca9087f5SMauro Carvalho Chehab    parser.add_argument(
1592ca9087f5SMauro Carvalho Chehab        "--no-virtualenv",
1593ca9087f5SMauro Carvalho Chehab        action="store_false",
1594ca9087f5SMauro Carvalho Chehab        dest="virtualenv",
1595ca9087f5SMauro Carvalho Chehab        help="Recommend installing Sphinx instead of using a virtualenv",
1596ca9087f5SMauro Carvalho Chehab    )
1597ca9087f5SMauro Carvalho Chehab
1598ca9087f5SMauro Carvalho Chehab    parser.add_argument(
1599ca9087f5SMauro Carvalho Chehab        "--no-pdf",
1600ca9087f5SMauro Carvalho Chehab        action="store_false",
1601ca9087f5SMauro Carvalho Chehab        dest="pdf",
1602ca9087f5SMauro Carvalho Chehab        help="Don't check for dependencies required to build PDF docs",
1603ca9087f5SMauro Carvalho Chehab    )
1604ca9087f5SMauro Carvalho Chehab
1605ca9087f5SMauro Carvalho Chehab    parser.add_argument(
1606ca9087f5SMauro Carvalho Chehab        "--version-check",
1607ca9087f5SMauro Carvalho Chehab        action="store_true",
1608ca9087f5SMauro Carvalho Chehab        dest="version_check",
1609ca9087f5SMauro Carvalho Chehab        help="If version is compatible, don't check for missing dependencies",
1610ca9087f5SMauro Carvalho Chehab    )
1611ca9087f5SMauro Carvalho Chehab
1612ca9087f5SMauro Carvalho Chehab    args = parser.parse_args()
1613ca9087f5SMauro Carvalho Chehab
1614ca9087f5SMauro Carvalho Chehab    checker = SphinxDependencyChecker(args)
1615ca9087f5SMauro Carvalho Chehab
1616728648b6SMauro Carvalho Chehab    checker.check_python()
1617ca9087f5SMauro Carvalho Chehab    checker.check_needs()
1618ca9087f5SMauro Carvalho Chehab
16196d5f4f3dSMauro Carvalho Chehab# Call main if not used as module
1620ca9087f5SMauro Carvalho Chehabif __name__ == "__main__":
1621ca9087f5SMauro Carvalho Chehab    main()
1622