xref: /linux/scripts/sphinx-pre-install (revision ec2e0fb07d789976c601bec19ecced7a501c3705)
1d43cd965SMauro Carvalho Chehab#!/usr/bin/env python3
2d43cd965SMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0-or-later
3d43cd965SMauro Carvalho Chehab# Copyright (c) 2017-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
4d43cd965SMauro Carvalho Chehab#
5d43cd965SMauro Carvalho Chehab# pylint: disable=C0103,C0114,C0115,C0116,C0301,C0302
6d43cd965SMauro Carvalho Chehab# pylint: disable=R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,E1121
7d43cd965SMauro Carvalho Chehab
8d43cd965SMauro Carvalho Chehab# Note: this script requires at least Python 3.6 to run.
9d43cd965SMauro Carvalho Chehab# Don't add changes not compatible with it, it is meant to report
10d43cd965SMauro Carvalho Chehab# incompatible python versions.
11d43cd965SMauro Carvalho Chehab
12d43cd965SMauro Carvalho Chehab"""
13d43cd965SMauro Carvalho ChehabDependency checker for Sphinx documentation Kernel build.
14d43cd965SMauro Carvalho Chehab
15d43cd965SMauro Carvalho ChehabThis module provides tools to check for all required dependencies needed to
16d43cd965SMauro Carvalho Chehabbuild documentation using Sphinx, including system packages, Python modules
17d43cd965SMauro Carvalho Chehaband LaTeX packages for PDF generation.
18d43cd965SMauro Carvalho Chehab
19d43cd965SMauro Carvalho ChehabIt detect packages for a subset of Linux distributions used by Kernel
20d43cd965SMauro Carvalho Chehabmaintainers, showing hints and missing dependencies.
21d43cd965SMauro Carvalho Chehab
22d43cd965SMauro Carvalho ChehabThe main class SphinxDependencyChecker handles the dependency checking logic
23d43cd965SMauro Carvalho Chehaband provides recommendations for installing missing packages. It supports both
24d43cd965SMauro Carvalho Chehabsystem package installations and  Python virtual environments. By default,
25d43cd965SMauro Carvalho Chehabsystem pacage install is recommended.
26d43cd965SMauro Carvalho Chehab"""
27d43cd965SMauro Carvalho Chehab
28d43cd965SMauro Carvalho Chehabimport argparse
29d43cd965SMauro Carvalho Chehabimport os
30d43cd965SMauro Carvalho Chehabimport re
31d43cd965SMauro Carvalho Chehabimport subprocess
32d43cd965SMauro Carvalho Chehabimport sys
33d43cd965SMauro Carvalho Chehabfrom glob import glob
34d43cd965SMauro Carvalho Chehab
35d43cd965SMauro Carvalho Chehab
36d43cd965SMauro Carvalho Chehabdef parse_version(version):
37d43cd965SMauro Carvalho Chehab    """Convert a major.minor.patch version into a tuple"""
38d43cd965SMauro Carvalho Chehab    return tuple(int(x) for x in version.split("."))
39d43cd965SMauro Carvalho Chehab
40d43cd965SMauro Carvalho Chehab
41d43cd965SMauro Carvalho Chehabdef ver_str(version):
42d43cd965SMauro Carvalho Chehab    """Returns a version tuple as major.minor.patch"""
43d43cd965SMauro Carvalho Chehab
44d43cd965SMauro Carvalho Chehab    return ".".join([str(x) for x in version])
45d43cd965SMauro Carvalho Chehab
46d43cd965SMauro Carvalho Chehab
47d43cd965SMauro Carvalho ChehabRECOMMENDED_VERSION = parse_version("3.4.3")
48d43cd965SMauro Carvalho ChehabMIN_PYTHON_VERSION = parse_version("3.7")
49d43cd965SMauro Carvalho Chehab
50d43cd965SMauro Carvalho Chehab
51d43cd965SMauro Carvalho Chehabclass DepManager:
52d43cd965SMauro Carvalho Chehab    """
53d43cd965SMauro Carvalho Chehab    Manage package dependencies. There are three types of dependencies:
54d43cd965SMauro Carvalho Chehab
55d43cd965SMauro Carvalho Chehab    - System: dependencies required for docs build;
56d43cd965SMauro Carvalho Chehab    - Python: python dependencies for a native distro Sphinx install;
57d43cd965SMauro Carvalho Chehab    - PDF: dependencies needed by PDF builds.
58d43cd965SMauro Carvalho Chehab
59d43cd965SMauro Carvalho Chehab    Each dependency can be mandatory or optional. Not installing an optional
60d43cd965SMauro Carvalho Chehab    dependency won't break the build, but will cause degradation at the
61d43cd965SMauro Carvalho Chehab    docs output.
62d43cd965SMauro Carvalho Chehab    """
63d43cd965SMauro Carvalho Chehab
64d43cd965SMauro Carvalho Chehab    # Internal types of dependencies. Don't use them outside DepManager class.
65d43cd965SMauro Carvalho Chehab    _SYS_TYPE = 0
66d43cd965SMauro Carvalho Chehab    _PHY_TYPE = 1
67d43cd965SMauro Carvalho Chehab    _PDF_TYPE = 2
68d43cd965SMauro Carvalho Chehab
69d43cd965SMauro Carvalho Chehab    # Dependencies visible outside the class.
70d43cd965SMauro Carvalho Chehab    # The keys are tuple with: (type, is_mandatory flag).
71d43cd965SMauro Carvalho Chehab    #
72d43cd965SMauro Carvalho Chehab    # Currently we're not using all optional dep types. Yet, we'll keep all
73d43cd965SMauro Carvalho Chehab    # possible combinations here. They're not many, and that makes easier
74d43cd965SMauro Carvalho Chehab    # if later needed and for the name() method below
75d43cd965SMauro Carvalho Chehab
76d43cd965SMauro Carvalho Chehab    SYSTEM_MANDATORY = (_SYS_TYPE, True)
77d43cd965SMauro Carvalho Chehab    PYTHON_MANDATORY = (_PHY_TYPE, True)
78d43cd965SMauro Carvalho Chehab    PDF_MANDATORY = (_PDF_TYPE, True)
79d43cd965SMauro Carvalho Chehab
80d43cd965SMauro Carvalho Chehab    SYSTEM_OPTIONAL = (_SYS_TYPE, False)
81d43cd965SMauro Carvalho Chehab    PYTHON_OPTIONAL = (_PHY_TYPE, False)
82d43cd965SMauro Carvalho Chehab    PDF_OPTIONAL = (_PDF_TYPE, True)
83d43cd965SMauro Carvalho Chehab
84d43cd965SMauro Carvalho Chehab    def __init__(self, pdf):
85d43cd965SMauro Carvalho Chehab        """
86d43cd965SMauro Carvalho Chehab        Initialize internal vars:
87d43cd965SMauro Carvalho Chehab
88d43cd965SMauro Carvalho Chehab        - missing: missing dependencies list, containing a distro-independent
89d43cd965SMauro Carvalho Chehab                   name for a missing dependency and its type.
90d43cd965SMauro Carvalho Chehab        - missing_pkg: ancillary dict containing missing dependencies in
91d43cd965SMauro Carvalho Chehab                       distro namespace, organized by type.
92d43cd965SMauro Carvalho Chehab        - need: total number of needed dependencies. Never cleaned.
93d43cd965SMauro Carvalho Chehab        - optional: total number of optional dependencies. Never cleaned.
94d43cd965SMauro Carvalho Chehab        - pdf: Is PDF support enabled?
95d43cd965SMauro Carvalho Chehab        """
96d43cd965SMauro Carvalho Chehab        self.missing = {}
97d43cd965SMauro Carvalho Chehab        self.missing_pkg = {}
98d43cd965SMauro Carvalho Chehab        self.need = 0
99d43cd965SMauro Carvalho Chehab        self.optional = 0
100d43cd965SMauro Carvalho Chehab        self.pdf = pdf
101d43cd965SMauro Carvalho Chehab
102d43cd965SMauro Carvalho Chehab    @staticmethod
103d43cd965SMauro Carvalho Chehab    def name(dtype):
104d43cd965SMauro Carvalho Chehab        """
105d43cd965SMauro Carvalho Chehab        Ancillary routine to output a warn/error message reporting
106d43cd965SMauro Carvalho Chehab        missing dependencies.
107d43cd965SMauro Carvalho Chehab        """
108d43cd965SMauro Carvalho Chehab        if dtype[0] == DepManager._SYS_TYPE:
109d43cd965SMauro Carvalho Chehab            msg = "build"
110d43cd965SMauro Carvalho Chehab        elif dtype[0] == DepManager._PHY_TYPE:
111d43cd965SMauro Carvalho Chehab            msg = "Python"
112d43cd965SMauro Carvalho Chehab        else:
113d43cd965SMauro Carvalho Chehab            msg = "PDF"
114d43cd965SMauro Carvalho Chehab
115d43cd965SMauro Carvalho Chehab        if dtype[1]:
116d43cd965SMauro Carvalho Chehab            return f"ERROR: {msg} mandatory deps missing"
117d43cd965SMauro Carvalho Chehab        else:
118d43cd965SMauro Carvalho Chehab            return f"Warning: {msg} optional deps missing"
119d43cd965SMauro Carvalho Chehab
120d43cd965SMauro Carvalho Chehab    @staticmethod
121d43cd965SMauro Carvalho Chehab    def is_optional(dtype):
122d43cd965SMauro Carvalho Chehab        """Ancillary routine to report if a dependency is optional"""
123d43cd965SMauro Carvalho Chehab        return not dtype[1]
124d43cd965SMauro Carvalho Chehab
125d43cd965SMauro Carvalho Chehab    @staticmethod
126d43cd965SMauro Carvalho Chehab    def is_pdf(dtype):
127d43cd965SMauro Carvalho Chehab        """Ancillary routine to report if a dependency is for PDF generation"""
128d43cd965SMauro Carvalho Chehab        if dtype[0] == DepManager._PDF_TYPE:
129d43cd965SMauro Carvalho Chehab            return True
130d43cd965SMauro Carvalho Chehab
131d43cd965SMauro Carvalho Chehab        return False
132d43cd965SMauro Carvalho Chehab
133d43cd965SMauro Carvalho Chehab    def add_package(self, package, dtype):
134d43cd965SMauro Carvalho Chehab        """
135d43cd965SMauro Carvalho Chehab        Add a package at the self.missing() dictionary.
136d43cd965SMauro Carvalho Chehab        Doesn't update missing_pkg.
137d43cd965SMauro Carvalho Chehab        """
138d43cd965SMauro Carvalho Chehab        is_optional = DepManager.is_optional(dtype)
139d43cd965SMauro Carvalho Chehab        self.missing[package] = dtype
140d43cd965SMauro Carvalho Chehab        if is_optional:
141d43cd965SMauro Carvalho Chehab            self.optional += 1
142d43cd965SMauro Carvalho Chehab        else:
143d43cd965SMauro Carvalho Chehab            self.need += 1
144d43cd965SMauro Carvalho Chehab
145d43cd965SMauro Carvalho Chehab    def del_package(self, package):
146d43cd965SMauro Carvalho Chehab        """
147d43cd965SMauro Carvalho Chehab        Remove a package at the self.missing() dictionary.
148d43cd965SMauro Carvalho Chehab        Doesn't update missing_pkg.
149d43cd965SMauro Carvalho Chehab        """
150d43cd965SMauro Carvalho Chehab        if package in self.missing:
151d43cd965SMauro Carvalho Chehab            del self.missing[package]
152d43cd965SMauro Carvalho Chehab
153d43cd965SMauro Carvalho Chehab    def clear_deps(self):
154d43cd965SMauro Carvalho Chehab        """
155d43cd965SMauro Carvalho Chehab        Clear dependencies without changing needed/optional.
156d43cd965SMauro Carvalho Chehab
157d43cd965SMauro Carvalho Chehab        This is an ackward way to have a separate section to recommend
158d43cd965SMauro Carvalho Chehab        a package after system main dependencies.
159d43cd965SMauro Carvalho Chehab
160d43cd965SMauro Carvalho Chehab        TODO: rework the logic to prevent needing it.
161d43cd965SMauro Carvalho Chehab        """
162d43cd965SMauro Carvalho Chehab
163d43cd965SMauro Carvalho Chehab        self.missing = {}
164d43cd965SMauro Carvalho Chehab        self.missing_pkg = {}
165d43cd965SMauro Carvalho Chehab
166d43cd965SMauro Carvalho Chehab    def check_missing(self, progs):
167d43cd965SMauro Carvalho Chehab        """
168d43cd965SMauro Carvalho Chehab        Update self.missing_pkg, using progs dict to convert from the
169d43cd965SMauro Carvalho Chehab        agnostic package name to distro-specific one.
170d43cd965SMauro Carvalho Chehab
171d43cd965SMauro Carvalho Chehab        Returns an string with the packages to be installed, sorted and
172d43cd965SMauro Carvalho Chehab        with eventual duplicates removed.
173d43cd965SMauro Carvalho Chehab        """
174d43cd965SMauro Carvalho Chehab
175d43cd965SMauro Carvalho Chehab        self.missing_pkg = {}
176d43cd965SMauro Carvalho Chehab
177d43cd965SMauro Carvalho Chehab        for prog, dtype in sorted(self.missing.items()):
178d43cd965SMauro Carvalho Chehab            # At least on some LTS distros like CentOS 7, texlive doesn't
179d43cd965SMauro Carvalho Chehab            # provide all packages we need. When such distros are
180d43cd965SMauro Carvalho Chehab            # detected, we have to disable PDF output.
181d43cd965SMauro Carvalho Chehab            #
182d43cd965SMauro Carvalho Chehab            # So, we need to ignore the packages that distros would
183d43cd965SMauro Carvalho Chehab            # need for LaTeX to work
184d43cd965SMauro Carvalho Chehab            if DepManager.is_pdf(dtype) and not self.pdf:
185d43cd965SMauro Carvalho Chehab                self.optional -= 1
186d43cd965SMauro Carvalho Chehab                continue
187d43cd965SMauro Carvalho Chehab
188d43cd965SMauro Carvalho Chehab            if not dtype in self.missing_pkg:
189d43cd965SMauro Carvalho Chehab                self.missing_pkg[dtype] = []
190d43cd965SMauro Carvalho Chehab
191d43cd965SMauro Carvalho Chehab            self.missing_pkg[dtype].append(progs.get(prog, prog))
192d43cd965SMauro Carvalho Chehab
193d43cd965SMauro Carvalho Chehab        install = []
194d43cd965SMauro Carvalho Chehab        for dtype, pkgs in self.missing_pkg.items():
195d43cd965SMauro Carvalho Chehab            install += pkgs
196d43cd965SMauro Carvalho Chehab
197d43cd965SMauro Carvalho Chehab        return " ".join(sorted(set(install)))
198d43cd965SMauro Carvalho Chehab
199d43cd965SMauro Carvalho Chehab    def warn_install(self):
200d43cd965SMauro Carvalho Chehab        """
201d43cd965SMauro Carvalho Chehab        Emit warnings/errors related to missing packages.
202d43cd965SMauro Carvalho Chehab        """
203d43cd965SMauro Carvalho Chehab
204d43cd965SMauro Carvalho Chehab        output_msg = ""
205d43cd965SMauro Carvalho Chehab
206d43cd965SMauro Carvalho Chehab        for dtype in sorted(self.missing_pkg.keys()):
207d43cd965SMauro Carvalho Chehab            progs = " ".join(sorted(set(self.missing_pkg[dtype])))
208d43cd965SMauro Carvalho Chehab
209d43cd965SMauro Carvalho Chehab            try:
210d43cd965SMauro Carvalho Chehab                name = DepManager.name(dtype)
211d43cd965SMauro Carvalho Chehab                output_msg += f'{name}:\t{progs}\n'
212d43cd965SMauro Carvalho Chehab            except KeyError:
213d43cd965SMauro Carvalho Chehab                raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}")
214d43cd965SMauro Carvalho Chehab
215d43cd965SMauro Carvalho Chehab        if output_msg:
216d43cd965SMauro Carvalho Chehab            print(f"\n{output_msg}")
217d43cd965SMauro Carvalho Chehab
218d43cd965SMauro Carvalho Chehabclass AncillaryMethods:
219d43cd965SMauro Carvalho Chehab    """
220d43cd965SMauro Carvalho Chehab    Ancillary methods that checks for missing dependencies for different
221d43cd965SMauro Carvalho Chehab    types of types, like binaries, python modules, rpm deps, etc.
222d43cd965SMauro Carvalho Chehab    """
223d43cd965SMauro Carvalho Chehab
224d43cd965SMauro Carvalho Chehab    @staticmethod
225d43cd965SMauro Carvalho Chehab    def which(prog):
226d43cd965SMauro Carvalho Chehab        """
227d43cd965SMauro Carvalho Chehab        Our own implementation of which(). We could instead use
228d43cd965SMauro Carvalho Chehab        shutil.which(), but this function is simple enough.
229d43cd965SMauro Carvalho Chehab        Probably faster to use this implementation than to import shutil.
230d43cd965SMauro Carvalho Chehab        """
231d43cd965SMauro Carvalho Chehab        for path in os.environ.get("PATH", "").split(":"):
232d43cd965SMauro Carvalho Chehab            full_path = os.path.join(path, prog)
233d43cd965SMauro Carvalho Chehab            if os.access(full_path, os.X_OK):
234d43cd965SMauro Carvalho Chehab                return full_path
235d43cd965SMauro Carvalho Chehab
236d43cd965SMauro Carvalho Chehab        return None
237d43cd965SMauro Carvalho Chehab
238d43cd965SMauro Carvalho Chehab    @staticmethod
239d43cd965SMauro Carvalho Chehab    def get_python_version(cmd):
240d43cd965SMauro Carvalho Chehab        """
241d43cd965SMauro Carvalho Chehab        Get python version from a Python binary. As we need to detect if
242d43cd965SMauro Carvalho Chehab        are out there newer python binaries, we can't rely on sys.release here.
243d43cd965SMauro Carvalho Chehab        """
244d43cd965SMauro Carvalho Chehab
245d43cd965SMauro Carvalho Chehab        result = SphinxDependencyChecker.run([cmd, "--version"],
246d43cd965SMauro Carvalho Chehab                                            capture_output=True, text=True)
247d43cd965SMauro Carvalho Chehab        version = result.stdout.strip()
248d43cd965SMauro Carvalho Chehab
249d43cd965SMauro Carvalho Chehab        match = re.search(r"(\d+\.\d+\.\d+)", version)
250d43cd965SMauro Carvalho Chehab        if match:
251d43cd965SMauro Carvalho Chehab            return parse_version(match.group(1))
252d43cd965SMauro Carvalho Chehab
253d43cd965SMauro Carvalho Chehab        print(f"Can't parse version {version}")
254d43cd965SMauro Carvalho Chehab        return (0, 0, 0)
255d43cd965SMauro Carvalho Chehab
256d43cd965SMauro Carvalho Chehab    @staticmethod
257d43cd965SMauro Carvalho Chehab    def find_python():
258d43cd965SMauro Carvalho Chehab        """
259d43cd965SMauro Carvalho Chehab        Detect if are out there any python 3.xy version newer than the
260d43cd965SMauro Carvalho Chehab        current one.
261d43cd965SMauro Carvalho Chehab
262d43cd965SMauro Carvalho Chehab        Note: this routine is limited to up to 2 digits for python3. We
263d43cd965SMauro Carvalho Chehab        may need to update it one day, hopefully on a distant future.
264d43cd965SMauro Carvalho Chehab        """
265d43cd965SMauro Carvalho Chehab        patterns = [
266d43cd965SMauro Carvalho Chehab            "python3.[0-9]",
267d43cd965SMauro Carvalho Chehab            "python3.[0-9][0-9]",
268d43cd965SMauro Carvalho Chehab        ]
269d43cd965SMauro Carvalho Chehab
270d43cd965SMauro Carvalho Chehab        # Seek for a python binary newer than MIN_PYTHON_VERSION
271d43cd965SMauro Carvalho Chehab        for path in os.getenv("PATH", "").split(":"):
272d43cd965SMauro Carvalho Chehab            for pattern in patterns:
273d43cd965SMauro Carvalho Chehab                for cmd in glob(os.path.join(path, pattern)):
274d43cd965SMauro Carvalho Chehab                    if os.path.isfile(cmd) and os.access(cmd, os.X_OK):
275d43cd965SMauro Carvalho Chehab                        version = SphinxDependencyChecker.get_python_version(cmd)
276d43cd965SMauro Carvalho Chehab                        if version >= MIN_PYTHON_VERSION:
277d43cd965SMauro Carvalho Chehab                            return cmd
278d43cd965SMauro Carvalho Chehab
279d43cd965SMauro Carvalho Chehab    @staticmethod
280d43cd965SMauro Carvalho Chehab    def check_python():
281d43cd965SMauro Carvalho Chehab        """
282d43cd965SMauro Carvalho Chehab        Check if the current python binary satisfies our minimal requirement
283d43cd965SMauro Carvalho Chehab        for Sphinx build. If not, re-run with a newer version if found.
284d43cd965SMauro Carvalho Chehab        """
285d43cd965SMauro Carvalho Chehab        cur_ver = sys.version_info[:3]
286d43cd965SMauro Carvalho Chehab        if cur_ver >= MIN_PYTHON_VERSION:
287d43cd965SMauro Carvalho Chehab            ver = ver_str(cur_ver)
288d43cd965SMauro Carvalho Chehab            print(f"Python version: {ver}")
289d43cd965SMauro Carvalho Chehab
290d43cd965SMauro Carvalho Chehab            # This could be useful for debugging purposes
291d43cd965SMauro Carvalho Chehab            if SphinxDependencyChecker.which("docutils"):
292d43cd965SMauro Carvalho Chehab                result = SphinxDependencyChecker.run(["docutils", "--version"],
293d43cd965SMauro Carvalho Chehab                                                    capture_output=True, text=True)
294d43cd965SMauro Carvalho Chehab                ver = result.stdout.strip()
295d43cd965SMauro Carvalho Chehab                match = re.search(r"(\d+\.\d+\.\d+)", ver)
296d43cd965SMauro Carvalho Chehab                if match:
297d43cd965SMauro Carvalho Chehab                    ver = match.group(1)
298d43cd965SMauro Carvalho Chehab
299d43cd965SMauro Carvalho Chehab                print(f"Docutils version: {ver}")
300d43cd965SMauro Carvalho Chehab
301d43cd965SMauro Carvalho Chehab            return
302d43cd965SMauro Carvalho Chehab
303d43cd965SMauro Carvalho Chehab        python_ver = ver_str(cur_ver)
304d43cd965SMauro Carvalho Chehab
305d43cd965SMauro Carvalho Chehab        new_python_cmd = SphinxDependencyChecker.find_python()
306d43cd965SMauro Carvalho Chehab        if not new_python_cmd:
307d43cd965SMauro Carvalho Chehab            print(f"ERROR: Python version {python_ver} is not spported anymore\n")
308d43cd965SMauro Carvalho Chehab            print("       Can't find a new version. This script may fail")
309d43cd965SMauro Carvalho Chehab            return
310d43cd965SMauro Carvalho Chehab
311d43cd965SMauro Carvalho Chehab        # Restart script using the newer version
312d43cd965SMauro Carvalho Chehab        script_path = os.path.abspath(sys.argv[0])
313d43cd965SMauro Carvalho Chehab        args = [new_python_cmd, script_path] + sys.argv[1:]
314d43cd965SMauro Carvalho Chehab
315d43cd965SMauro Carvalho Chehab        print(f"Python {python_ver} not supported. Changing to {new_python_cmd}")
316d43cd965SMauro Carvalho Chehab
317d43cd965SMauro Carvalho Chehab        try:
318d43cd965SMauro Carvalho Chehab            os.execv(new_python_cmd, args)
319d43cd965SMauro Carvalho Chehab        except OSError as e:
320d43cd965SMauro Carvalho Chehab            sys.exit(f"Failed to restart with {new_python_cmd}: {e}")
321d43cd965SMauro Carvalho Chehab
322d43cd965SMauro Carvalho Chehab    @staticmethod
323d43cd965SMauro Carvalho Chehab    def run(*args, **kwargs):
324d43cd965SMauro Carvalho Chehab        """
325d43cd965SMauro Carvalho Chehab        Excecute a command, hiding its output by default.
326d43cd965SMauro Carvalho Chehab        Preserve comatibility with older Python versions.
327d43cd965SMauro Carvalho Chehab        """
328d43cd965SMauro Carvalho Chehab
329d43cd965SMauro Carvalho Chehab        capture_output = kwargs.pop('capture_output', False)
330d43cd965SMauro Carvalho Chehab
331d43cd965SMauro Carvalho Chehab        if capture_output:
332d43cd965SMauro Carvalho Chehab            if 'stdout' not in kwargs:
333d43cd965SMauro Carvalho Chehab                kwargs['stdout'] = subprocess.PIPE
334d43cd965SMauro Carvalho Chehab            if 'stderr' not in kwargs:
335d43cd965SMauro Carvalho Chehab                kwargs['stderr'] = subprocess.PIPE
336d43cd965SMauro Carvalho Chehab        else:
337d43cd965SMauro Carvalho Chehab            if 'stdout' not in kwargs:
338d43cd965SMauro Carvalho Chehab                kwargs['stdout'] = subprocess.DEVNULL
339d43cd965SMauro Carvalho Chehab            if 'stderr' not in kwargs:
340d43cd965SMauro Carvalho Chehab                kwargs['stderr'] = subprocess.DEVNULL
341d43cd965SMauro Carvalho Chehab
342d43cd965SMauro Carvalho Chehab        # Don't break with older Python versions
343d43cd965SMauro Carvalho Chehab        if 'text' in kwargs and sys.version_info < (3, 7):
344d43cd965SMauro Carvalho Chehab            kwargs['universal_newlines'] = kwargs.pop('text')
345d43cd965SMauro Carvalho Chehab
346d43cd965SMauro Carvalho Chehab        return subprocess.run(*args, **kwargs)
347d43cd965SMauro Carvalho Chehab
348d43cd965SMauro Carvalho Chehabclass MissingCheckers(AncillaryMethods):
349d43cd965SMauro Carvalho Chehab    """
350d43cd965SMauro Carvalho Chehab    Contains some ancillary checkers for different types of binaries and
351d43cd965SMauro Carvalho Chehab    package managers.
352d43cd965SMauro Carvalho Chehab    """
353d43cd965SMauro Carvalho Chehab
354d43cd965SMauro Carvalho Chehab    def __init__(self, args, texlive):
355d43cd965SMauro Carvalho Chehab        """
356d43cd965SMauro Carvalho Chehab        Initialize its internal variables
357d43cd965SMauro Carvalho Chehab        """
358d43cd965SMauro Carvalho Chehab        self.pdf = args.pdf
359d43cd965SMauro Carvalho Chehab        self.virtualenv = args.virtualenv
360d43cd965SMauro Carvalho Chehab        self.version_check = args.version_check
361d43cd965SMauro Carvalho Chehab        self.texlive = texlive
362d43cd965SMauro Carvalho Chehab
363d43cd965SMauro Carvalho Chehab        self.min_version = (0, 0, 0)
364d43cd965SMauro Carvalho Chehab        self.cur_version = (0, 0, 0)
365d43cd965SMauro Carvalho Chehab
366d43cd965SMauro Carvalho Chehab        self.deps = DepManager(self.pdf)
367d43cd965SMauro Carvalho Chehab
368d43cd965SMauro Carvalho Chehab        self.need_symlink = 0
369d43cd965SMauro Carvalho Chehab        self.need_sphinx = 0
370d43cd965SMauro Carvalho Chehab
371d43cd965SMauro Carvalho Chehab        self.verbose_warn_install = 1
372d43cd965SMauro Carvalho Chehab
373d43cd965SMauro Carvalho Chehab        self.virtenv_dir = ""
374d43cd965SMauro Carvalho Chehab        self.install = ""
375d43cd965SMauro Carvalho Chehab        self.python_cmd = ""
376d43cd965SMauro Carvalho Chehab
377d43cd965SMauro Carvalho Chehab        self.virtenv_prefix = ["sphinx_", "Sphinx_" ]
378d43cd965SMauro Carvalho Chehab
379d43cd965SMauro Carvalho Chehab    def check_missing_file(self, files, package, dtype):
380d43cd965SMauro Carvalho Chehab        """
381d43cd965SMauro Carvalho Chehab        Does the file exists? If not, add it to missing dependencies.
382d43cd965SMauro Carvalho Chehab        """
383d43cd965SMauro Carvalho Chehab        for f in files:
384d43cd965SMauro Carvalho Chehab            if os.path.exists(f):
385d43cd965SMauro Carvalho Chehab                return
386d43cd965SMauro Carvalho Chehab        self.deps.add_package(package, dtype)
387d43cd965SMauro Carvalho Chehab
388d43cd965SMauro Carvalho Chehab    def check_program(self, prog, dtype):
389d43cd965SMauro Carvalho Chehab        """
390d43cd965SMauro Carvalho Chehab        Does the program exists and it is at the PATH?
391d43cd965SMauro Carvalho Chehab        If not, add it to missing dependencies.
392d43cd965SMauro Carvalho Chehab        """
393d43cd965SMauro Carvalho Chehab        found = self.which(prog)
394d43cd965SMauro Carvalho Chehab        if found:
395d43cd965SMauro Carvalho Chehab            return found
396d43cd965SMauro Carvalho Chehab
397d43cd965SMauro Carvalho Chehab        self.deps.add_package(prog, dtype)
398d43cd965SMauro Carvalho Chehab
399d43cd965SMauro Carvalho Chehab        return None
400d43cd965SMauro Carvalho Chehab
401d43cd965SMauro Carvalho Chehab    def check_perl_module(self, prog, dtype):
402d43cd965SMauro Carvalho Chehab        """
403d43cd965SMauro Carvalho Chehab        Does perl have a dependency? Is it available?
404d43cd965SMauro Carvalho Chehab        If not, add it to missing dependencies.
405d43cd965SMauro Carvalho Chehab
406d43cd965SMauro Carvalho Chehab        Right now, we still need Perl for doc build, as it is required
407d43cd965SMauro Carvalho Chehab        by some tools called at docs or kernel build time, like:
408d43cd965SMauro Carvalho Chehab
409d43cd965SMauro Carvalho Chehab            scripts/documentation-file-ref-check
410d43cd965SMauro Carvalho Chehab
411d43cd965SMauro Carvalho Chehab        Also, checkpatch is on Perl.
412d43cd965SMauro Carvalho Chehab        """
413d43cd965SMauro Carvalho Chehab
414d43cd965SMauro Carvalho Chehab        # While testing with lxc download template, one of the
415d43cd965SMauro Carvalho Chehab        # distros (Oracle) didn't have perl - nor even an option to install
416d43cd965SMauro Carvalho Chehab        # before installing oraclelinux-release-el9 package.
417d43cd965SMauro Carvalho Chehab        #
418d43cd965SMauro Carvalho Chehab        # Check it before running an error. If perl is not there,
419d43cd965SMauro Carvalho Chehab        # add it as a mandatory package, as some parts of the doc builder
420d43cd965SMauro Carvalho Chehab        # needs it.
421d43cd965SMauro Carvalho Chehab        if not self.which("perl"):
422d43cd965SMauro Carvalho Chehab            self.deps.add_package("perl", DepManager.SYSTEM_MANDATORY)
423d43cd965SMauro Carvalho Chehab            self.deps.add_package(prog, dtype)
424d43cd965SMauro Carvalho Chehab            return
425d43cd965SMauro Carvalho Chehab
426d43cd965SMauro Carvalho Chehab        try:
427d43cd965SMauro Carvalho Chehab            self.run(["perl", f"-M{prog}", "-e", "1"], check=True)
428d43cd965SMauro Carvalho Chehab        except subprocess.CalledProcessError:
429d43cd965SMauro Carvalho Chehab            self.deps.add_package(prog, dtype)
430d43cd965SMauro Carvalho Chehab
431d43cd965SMauro Carvalho Chehab    def check_python_module(self, module, is_optional=False):
432d43cd965SMauro Carvalho Chehab        """
433d43cd965SMauro Carvalho Chehab        Does a python module exists outside venv? If not, add it to missing
434d43cd965SMauro Carvalho Chehab        dependencies.
435d43cd965SMauro Carvalho Chehab        """
436d43cd965SMauro Carvalho Chehab        if is_optional:
437d43cd965SMauro Carvalho Chehab            dtype = DepManager.PYTHON_OPTIONAL
438d43cd965SMauro Carvalho Chehab        else:
439d43cd965SMauro Carvalho Chehab            dtype = DepManager.PYTHON_MANDATORY
440d43cd965SMauro Carvalho Chehab
441d43cd965SMauro Carvalho Chehab        try:
442d43cd965SMauro Carvalho Chehab            self.run([self.python_cmd, "-c", f"import {module}"], check=True)
443d43cd965SMauro Carvalho Chehab        except subprocess.CalledProcessError:
444d43cd965SMauro Carvalho Chehab            self.deps.add_package(module, dtype)
445d43cd965SMauro Carvalho Chehab
446d43cd965SMauro Carvalho Chehab    def check_rpm_missing(self, pkgs, dtype):
447d43cd965SMauro Carvalho Chehab        """
448d43cd965SMauro Carvalho Chehab        Does a rpm package exists? If not, add it to missing dependencies.
449d43cd965SMauro Carvalho Chehab        """
450d43cd965SMauro Carvalho Chehab        for prog in pkgs:
451d43cd965SMauro Carvalho Chehab            try:
452d43cd965SMauro Carvalho Chehab                self.run(["rpm", "-q", prog], check=True)
453d43cd965SMauro Carvalho Chehab            except subprocess.CalledProcessError:
454d43cd965SMauro Carvalho Chehab                self.deps.add_package(prog, dtype)
455d43cd965SMauro Carvalho Chehab
456d43cd965SMauro Carvalho Chehab    def check_pacman_missing(self, pkgs, dtype):
457d43cd965SMauro Carvalho Chehab        """
458d43cd965SMauro Carvalho Chehab        Does a pacman package exists? If not, add it to missing dependencies.
459d43cd965SMauro Carvalho Chehab        """
460d43cd965SMauro Carvalho Chehab        for prog in pkgs:
461d43cd965SMauro Carvalho Chehab            try:
462d43cd965SMauro Carvalho Chehab                self.run(["pacman", "-Q", prog], check=True)
463d43cd965SMauro Carvalho Chehab            except subprocess.CalledProcessError:
464d43cd965SMauro Carvalho Chehab                self.deps.add_package(prog, dtype)
465d43cd965SMauro Carvalho Chehab
466d43cd965SMauro Carvalho Chehab    def check_missing_tex(self, is_optional=False):
467d43cd965SMauro Carvalho Chehab        """
468d43cd965SMauro Carvalho Chehab        Does a LaTeX package exists? If not, add it to missing dependencies.
469d43cd965SMauro Carvalho Chehab        """
470d43cd965SMauro Carvalho Chehab        if is_optional:
471d43cd965SMauro Carvalho Chehab            dtype = DepManager.PDF_OPTIONAL
472d43cd965SMauro Carvalho Chehab        else:
473d43cd965SMauro Carvalho Chehab            dtype = DepManager.PDF_MANDATORY
474d43cd965SMauro Carvalho Chehab
475d43cd965SMauro Carvalho Chehab        kpsewhich = self.which("kpsewhich")
476d43cd965SMauro Carvalho Chehab        for prog, package in self.texlive.items():
477d43cd965SMauro Carvalho Chehab
478d43cd965SMauro Carvalho Chehab            # If kpsewhich is not there, just add it to deps
479d43cd965SMauro Carvalho Chehab            if not kpsewhich:
480d43cd965SMauro Carvalho Chehab                self.deps.add_package(package, dtype)
481d43cd965SMauro Carvalho Chehab                continue
482d43cd965SMauro Carvalho Chehab
483d43cd965SMauro Carvalho Chehab            # Check if the package is needed
484d43cd965SMauro Carvalho Chehab            try:
485d43cd965SMauro Carvalho Chehab                result = self.run(
486d43cd965SMauro Carvalho Chehab                    [kpsewhich, prog], stdout=subprocess.PIPE, text=True, check=True
487d43cd965SMauro Carvalho Chehab                )
488d43cd965SMauro Carvalho Chehab
489d43cd965SMauro Carvalho Chehab                # Didn't find. Add it
490d43cd965SMauro Carvalho Chehab                if not result.stdout.strip():
491d43cd965SMauro Carvalho Chehab                    self.deps.add_package(package, dtype)
492d43cd965SMauro Carvalho Chehab
493d43cd965SMauro Carvalho Chehab            except subprocess.CalledProcessError:
494d43cd965SMauro Carvalho Chehab                # kpsewhich returned an error. Add it, just in case
495d43cd965SMauro Carvalho Chehab                self.deps.add_package(package, dtype)
496d43cd965SMauro Carvalho Chehab
497d43cd965SMauro Carvalho Chehab    def get_sphinx_fname(self):
498d43cd965SMauro Carvalho Chehab        """
499d43cd965SMauro Carvalho Chehab        Gets the binary filename for sphinx-build.
500d43cd965SMauro Carvalho Chehab        """
501d43cd965SMauro Carvalho Chehab        if "SPHINXBUILD" in os.environ:
502d43cd965SMauro Carvalho Chehab            return os.environ["SPHINXBUILD"]
503d43cd965SMauro Carvalho Chehab
504d43cd965SMauro Carvalho Chehab        fname = "sphinx-build"
505d43cd965SMauro Carvalho Chehab        if self.which(fname):
506d43cd965SMauro Carvalho Chehab            return fname
507d43cd965SMauro Carvalho Chehab
508d43cd965SMauro Carvalho Chehab        fname = "sphinx-build-3"
509d43cd965SMauro Carvalho Chehab        if self.which(fname):
510d43cd965SMauro Carvalho Chehab            self.need_symlink = 1
511d43cd965SMauro Carvalho Chehab            return fname
512d43cd965SMauro Carvalho Chehab
513d43cd965SMauro Carvalho Chehab        return ""
514d43cd965SMauro Carvalho Chehab
515d43cd965SMauro Carvalho Chehab    def get_sphinx_version(self, cmd):
516d43cd965SMauro Carvalho Chehab        """
517d43cd965SMauro Carvalho Chehab        Gets sphinx-build version.
518d43cd965SMauro Carvalho Chehab        """
519d43cd965SMauro Carvalho Chehab        try:
520d43cd965SMauro Carvalho Chehab            result = self.run([cmd, "--version"],
521d43cd965SMauro Carvalho Chehab                              stdout=subprocess.PIPE,
522d43cd965SMauro Carvalho Chehab                              stderr=subprocess.STDOUT,
523d43cd965SMauro Carvalho Chehab                              text=True, check=True)
524d43cd965SMauro Carvalho Chehab        except (subprocess.CalledProcessError, FileNotFoundError):
525d43cd965SMauro Carvalho Chehab            return None
526d43cd965SMauro Carvalho Chehab
527d43cd965SMauro Carvalho Chehab        for line in result.stdout.split("\n"):
528d43cd965SMauro Carvalho Chehab            match = re.match(r"^sphinx-build\s+([\d\.]+)(?:\+(?:/[\da-f]+)|b\d+)?\s*$", line)
529d43cd965SMauro Carvalho Chehab            if match:
530d43cd965SMauro Carvalho Chehab                return parse_version(match.group(1))
531d43cd965SMauro Carvalho Chehab
532d43cd965SMauro Carvalho Chehab            match = re.match(r"^Sphinx.*\s+([\d\.]+)\s*$", line)
533d43cd965SMauro Carvalho Chehab            if match:
534d43cd965SMauro Carvalho Chehab                return parse_version(match.group(1))
535d43cd965SMauro Carvalho Chehab
536d43cd965SMauro Carvalho Chehab    def check_sphinx(self, conf):
537d43cd965SMauro Carvalho Chehab        """
538d43cd965SMauro Carvalho Chehab        Checks Sphinx minimal requirements
539d43cd965SMauro Carvalho Chehab        """
540d43cd965SMauro Carvalho Chehab        try:
541d43cd965SMauro Carvalho Chehab            with open(conf, "r", encoding="utf-8") as f:
542d43cd965SMauro Carvalho Chehab                for line in f:
543d43cd965SMauro Carvalho Chehab                    match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line)
544d43cd965SMauro Carvalho Chehab                    if match:
545d43cd965SMauro Carvalho Chehab                        self.min_version = parse_version(match.group(1))
546d43cd965SMauro Carvalho Chehab                        break
547d43cd965SMauro Carvalho Chehab        except IOError:
548d43cd965SMauro Carvalho Chehab            sys.exit(f"Can't open {conf}")
549d43cd965SMauro Carvalho Chehab
550d43cd965SMauro Carvalho Chehab        if not self.min_version:
551d43cd965SMauro Carvalho Chehab            sys.exit(f"Can't get needs_sphinx version from {conf}")
552d43cd965SMauro Carvalho Chehab
553d43cd965SMauro Carvalho Chehab        self.virtenv_dir = self.virtenv_prefix[0] + "latest"
554d43cd965SMauro Carvalho Chehab
555d43cd965SMauro Carvalho Chehab        sphinx = self.get_sphinx_fname()
556d43cd965SMauro Carvalho Chehab        if not sphinx:
557d43cd965SMauro Carvalho Chehab            self.need_sphinx = 1
558d43cd965SMauro Carvalho Chehab            return
559d43cd965SMauro Carvalho Chehab
560d43cd965SMauro Carvalho Chehab        self.cur_version = self.get_sphinx_version(sphinx)
561d43cd965SMauro Carvalho Chehab        if not self.cur_version:
562d43cd965SMauro Carvalho Chehab            sys.exit(f"{sphinx} didn't return its version")
563d43cd965SMauro Carvalho Chehab
564d43cd965SMauro Carvalho Chehab        if self.cur_version < self.min_version:
565d43cd965SMauro Carvalho Chehab            curver = ver_str(self.cur_version)
566d43cd965SMauro Carvalho Chehab            minver = ver_str(self.min_version)
567d43cd965SMauro Carvalho Chehab
568d43cd965SMauro Carvalho Chehab            print(f"ERROR: Sphinx version is {curver}. It should be >= {minver}")
569d43cd965SMauro Carvalho Chehab            self.need_sphinx = 1
570d43cd965SMauro Carvalho Chehab            return
571d43cd965SMauro Carvalho Chehab
572d43cd965SMauro Carvalho Chehab        # On version check mode, just assume Sphinx has all mandatory deps
573d43cd965SMauro Carvalho Chehab        if self.version_check and self.cur_version >= RECOMMENDED_VERSION:
574d43cd965SMauro Carvalho Chehab            sys.exit(0)
575d43cd965SMauro Carvalho Chehab
576d43cd965SMauro Carvalho Chehab    def catcheck(self, filename):
577d43cd965SMauro Carvalho Chehab        """
578d43cd965SMauro Carvalho Chehab        Reads a file if it exists, returning as string.
579d43cd965SMauro Carvalho Chehab        If not found, returns an empty string.
580d43cd965SMauro Carvalho Chehab        """
581d43cd965SMauro Carvalho Chehab        if os.path.exists(filename):
582d43cd965SMauro Carvalho Chehab            with open(filename, "r", encoding="utf-8") as f:
583d43cd965SMauro Carvalho Chehab                return f.read().strip()
584d43cd965SMauro Carvalho Chehab        return ""
585d43cd965SMauro Carvalho Chehab
586d43cd965SMauro Carvalho Chehab    def get_system_release(self):
587d43cd965SMauro Carvalho Chehab        """
588d43cd965SMauro Carvalho Chehab        Determine the system type. There's no unique way that would work
589d43cd965SMauro Carvalho Chehab        with all distros with a minimal package install. So, several
590d43cd965SMauro Carvalho Chehab        methods are used here.
591d43cd965SMauro Carvalho Chehab
592d43cd965SMauro Carvalho Chehab        By default, it will use lsb_release function. If not available, it will
593d43cd965SMauro Carvalho Chehab        fail back to reading the known different places where the distro name
594d43cd965SMauro Carvalho Chehab        is stored.
595d43cd965SMauro Carvalho Chehab
596d43cd965SMauro Carvalho Chehab        Several modern distros now have /etc/os-release, which usually have
597d43cd965SMauro Carvalho Chehab        a decent coverage.
598d43cd965SMauro Carvalho Chehab        """
599d43cd965SMauro Carvalho Chehab
600d43cd965SMauro Carvalho Chehab        system_release = ""
601d43cd965SMauro Carvalho Chehab
602d43cd965SMauro Carvalho Chehab        if self.which("lsb_release"):
603d43cd965SMauro Carvalho Chehab            result = self.run(["lsb_release", "-d"], capture_output=True, text=True)
604d43cd965SMauro Carvalho Chehab            system_release = result.stdout.replace("Description:", "").strip()
605d43cd965SMauro Carvalho Chehab
606d43cd965SMauro Carvalho Chehab        release_files = [
607d43cd965SMauro Carvalho Chehab            "/etc/system-release",
608d43cd965SMauro Carvalho Chehab            "/etc/redhat-release",
609d43cd965SMauro Carvalho Chehab            "/etc/lsb-release",
610d43cd965SMauro Carvalho Chehab            "/etc/gentoo-release",
611d43cd965SMauro Carvalho Chehab        ]
612d43cd965SMauro Carvalho Chehab
613d43cd965SMauro Carvalho Chehab        if not system_release:
614d43cd965SMauro Carvalho Chehab            for f in release_files:
615d43cd965SMauro Carvalho Chehab                system_release = self.catcheck(f)
616d43cd965SMauro Carvalho Chehab                if system_release:
617d43cd965SMauro Carvalho Chehab                    break
618d43cd965SMauro Carvalho Chehab
619d43cd965SMauro Carvalho Chehab        # This seems more common than LSB these days
620d43cd965SMauro Carvalho Chehab        if not system_release:
621d43cd965SMauro Carvalho Chehab            os_var = {}
622d43cd965SMauro Carvalho Chehab            try:
623d43cd965SMauro Carvalho Chehab                with open("/etc/os-release", "r", encoding="utf-8") as f:
624d43cd965SMauro Carvalho Chehab                    for line in f:
625d43cd965SMauro Carvalho Chehab                        match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line)
626d43cd965SMauro Carvalho Chehab                        if match:
627d43cd965SMauro Carvalho Chehab                            os_var[match.group(1)] = match.group(2)
628d43cd965SMauro Carvalho Chehab
629d43cd965SMauro Carvalho Chehab                system_release = os_var.get("NAME", "")
630d43cd965SMauro Carvalho Chehab                if "VERSION_ID" in os_var:
631d43cd965SMauro Carvalho Chehab                    system_release += " " + os_var["VERSION_ID"]
632d43cd965SMauro Carvalho Chehab                elif "VERSION" in os_var:
633d43cd965SMauro Carvalho Chehab                    system_release += " " + os_var["VERSION"]
634d43cd965SMauro Carvalho Chehab            except IOError:
635d43cd965SMauro Carvalho Chehab                pass
636d43cd965SMauro Carvalho Chehab
637d43cd965SMauro Carvalho Chehab        if not system_release:
638d43cd965SMauro Carvalho Chehab            system_release = self.catcheck("/etc/issue")
639d43cd965SMauro Carvalho Chehab
640d43cd965SMauro Carvalho Chehab        system_release = system_release.strip()
641d43cd965SMauro Carvalho Chehab
642d43cd965SMauro Carvalho Chehab        return system_release
643d43cd965SMauro Carvalho Chehab
644d43cd965SMauro Carvalho Chehabclass SphinxDependencyChecker(MissingCheckers):
645d43cd965SMauro Carvalho Chehab    """
646d43cd965SMauro Carvalho Chehab    Main class for checking Sphinx documentation build dependencies.
647d43cd965SMauro Carvalho Chehab
648d43cd965SMauro Carvalho Chehab    - Check for missing system packages;
649d43cd965SMauro Carvalho Chehab    - Check for missing Python modules;
650d43cd965SMauro Carvalho Chehab    - Check for missing LaTeX packages needed by PDF generation;
651d43cd965SMauro Carvalho Chehab    - Propose Sphinx install via Python Virtual environment;
652d43cd965SMauro Carvalho Chehab    - Propose Sphinx install via distro-specific package install.
653d43cd965SMauro Carvalho Chehab    """
654d43cd965SMauro Carvalho Chehab    def __init__(self, args):
655d43cd965SMauro Carvalho Chehab        """Initialize checker variables"""
656d43cd965SMauro Carvalho Chehab
657d43cd965SMauro Carvalho Chehab        # List of required texlive packages on Fedora and OpenSuse
658d43cd965SMauro Carvalho Chehab        texlive = {
659d43cd965SMauro Carvalho Chehab            "amsfonts.sty":       "texlive-amsfonts",
660d43cd965SMauro Carvalho Chehab            "amsmath.sty":        "texlive-amsmath",
661d43cd965SMauro Carvalho Chehab            "amssymb.sty":        "texlive-amsfonts",
662d43cd965SMauro Carvalho Chehab            "amsthm.sty":         "texlive-amscls",
663d43cd965SMauro Carvalho Chehab            "anyfontsize.sty":    "texlive-anyfontsize",
664d43cd965SMauro Carvalho Chehab            "atbegshi.sty":       "texlive-oberdiek",
665d43cd965SMauro Carvalho Chehab            "bm.sty":             "texlive-tools",
666d43cd965SMauro Carvalho Chehab            "capt-of.sty":        "texlive-capt-of",
667d43cd965SMauro Carvalho Chehab            "cmap.sty":           "texlive-cmap",
668d43cd965SMauro Carvalho Chehab            "ctexhook.sty":       "texlive-ctex",
669d43cd965SMauro Carvalho Chehab            "ecrm1000.tfm":       "texlive-ec",
670d43cd965SMauro Carvalho Chehab            "eqparbox.sty":       "texlive-eqparbox",
671d43cd965SMauro Carvalho Chehab            "eu1enc.def":         "texlive-euenc",
672d43cd965SMauro Carvalho Chehab            "fancybox.sty":       "texlive-fancybox",
673d43cd965SMauro Carvalho Chehab            "fancyvrb.sty":       "texlive-fancyvrb",
674d43cd965SMauro Carvalho Chehab            "float.sty":          "texlive-float",
675d43cd965SMauro Carvalho Chehab            "fncychap.sty":       "texlive-fncychap",
676d43cd965SMauro Carvalho Chehab            "footnote.sty":       "texlive-mdwtools",
677d43cd965SMauro Carvalho Chehab            "framed.sty":         "texlive-framed",
678d43cd965SMauro Carvalho Chehab            "luatex85.sty":       "texlive-luatex85",
679d43cd965SMauro Carvalho Chehab            "multirow.sty":       "texlive-multirow",
680d43cd965SMauro Carvalho Chehab            "needspace.sty":      "texlive-needspace",
681d43cd965SMauro Carvalho Chehab            "palatino.sty":       "texlive-psnfss",
682d43cd965SMauro Carvalho Chehab            "parskip.sty":        "texlive-parskip",
683d43cd965SMauro Carvalho Chehab            "polyglossia.sty":    "texlive-polyglossia",
684d43cd965SMauro Carvalho Chehab            "tabulary.sty":       "texlive-tabulary",
685d43cd965SMauro Carvalho Chehab            "threeparttable.sty": "texlive-threeparttable",
686d43cd965SMauro Carvalho Chehab            "titlesec.sty":       "texlive-titlesec",
687d43cd965SMauro Carvalho Chehab            "ucs.sty":            "texlive-ucs",
688d43cd965SMauro Carvalho Chehab            "upquote.sty":        "texlive-upquote",
689d43cd965SMauro Carvalho Chehab            "wrapfig.sty":        "texlive-wrapfig",
690d43cd965SMauro Carvalho Chehab        }
691d43cd965SMauro Carvalho Chehab
692d43cd965SMauro Carvalho Chehab        super().__init__(args, texlive)
693d43cd965SMauro Carvalho Chehab
6949f51a1d6SMauro Carvalho Chehab        self.need_pip = False
695d43cd965SMauro Carvalho Chehab        self.rec_sphinx_upgrade = 0
696d43cd965SMauro Carvalho Chehab
697d43cd965SMauro Carvalho Chehab        self.system_release = self.get_system_release()
698d43cd965SMauro Carvalho Chehab        self.activate_cmd = ""
699d43cd965SMauro Carvalho Chehab
700d43cd965SMauro Carvalho Chehab        # Some distros may not have a Sphinx shipped package compatible with
701d43cd965SMauro Carvalho Chehab        # our minimal requirements
702d43cd965SMauro Carvalho Chehab        self.package_supported = True
703d43cd965SMauro Carvalho Chehab
704d43cd965SMauro Carvalho Chehab        # Recommend a new python version
705d43cd965SMauro Carvalho Chehab        self.recommend_python = None
706d43cd965SMauro Carvalho Chehab
707d43cd965SMauro Carvalho Chehab        # Certain hints are meant to be shown only once
708d43cd965SMauro Carvalho Chehab        self.distro_msg = None
709d43cd965SMauro Carvalho Chehab
710d43cd965SMauro Carvalho Chehab        self.latest_avail_ver = (0, 0, 0)
711d43cd965SMauro Carvalho Chehab        self.venv_ver = (0, 0, 0)
712d43cd965SMauro Carvalho Chehab
713d43cd965SMauro Carvalho Chehab        prefix = os.environ.get("srctree", ".") + "/"
714d43cd965SMauro Carvalho Chehab
715d43cd965SMauro Carvalho Chehab        self.conf = prefix + "Documentation/conf.py"
716d43cd965SMauro Carvalho Chehab        self.requirement_file = prefix + "Documentation/sphinx/requirements.txt"
717d43cd965SMauro Carvalho Chehab
718d43cd965SMauro Carvalho Chehab    def get_install_progs(self, progs, cmd, extra=None):
719d43cd965SMauro Carvalho Chehab        """
720d43cd965SMauro Carvalho Chehab        Check for missing dependencies using the provided program mapping.
721d43cd965SMauro Carvalho Chehab
722d43cd965SMauro Carvalho Chehab        The actual distro-specific programs are mapped via progs argument.
723d43cd965SMauro Carvalho Chehab        """
724d43cd965SMauro Carvalho Chehab        install = self.deps.check_missing(progs)
725d43cd965SMauro Carvalho Chehab
726d43cd965SMauro Carvalho Chehab        if self.verbose_warn_install:
727d43cd965SMauro Carvalho Chehab            self.deps.warn_install()
728d43cd965SMauro Carvalho Chehab
729d43cd965SMauro Carvalho Chehab        if not install:
730d43cd965SMauro Carvalho Chehab            return
731d43cd965SMauro Carvalho Chehab
732d43cd965SMauro Carvalho Chehab        if cmd:
733d43cd965SMauro Carvalho Chehab            if self.verbose_warn_install:
734d43cd965SMauro Carvalho Chehab                msg = "You should run:"
735d43cd965SMauro Carvalho Chehab            else:
736d43cd965SMauro Carvalho Chehab                msg = ""
737d43cd965SMauro Carvalho Chehab
738d43cd965SMauro Carvalho Chehab            if extra:
739d43cd965SMauro Carvalho Chehab                msg += "\n\t" + extra.replace("\n", "\n\t")
740d43cd965SMauro Carvalho Chehab
741d43cd965SMauro Carvalho Chehab            return(msg + "\n\tsudo " + cmd + " " + install)
742d43cd965SMauro Carvalho Chehab
743d43cd965SMauro Carvalho Chehab        return None
744d43cd965SMauro Carvalho Chehab
745d43cd965SMauro Carvalho Chehab    #
746d43cd965SMauro Carvalho Chehab    # Distro-specific hints methods
747d43cd965SMauro Carvalho Chehab    #
748d43cd965SMauro Carvalho Chehab
749d43cd965SMauro Carvalho Chehab    def give_debian_hints(self):
750d43cd965SMauro Carvalho Chehab        """
751d43cd965SMauro Carvalho Chehab        Provide package installation hints for Debian-based distros.
752d43cd965SMauro Carvalho Chehab        """
753d43cd965SMauro Carvalho Chehab        progs = {
754d43cd965SMauro Carvalho Chehab            "Pod::Usage":    "perl-modules",
755d43cd965SMauro Carvalho Chehab            "convert":       "imagemagick",
756d43cd965SMauro Carvalho Chehab            "dot":           "graphviz",
757d43cd965SMauro Carvalho Chehab            "ensurepip":     "python3-venv",
758d43cd965SMauro Carvalho Chehab            "python-sphinx": "python3-sphinx",
759d43cd965SMauro Carvalho Chehab            "rsvg-convert":  "librsvg2-bin",
760d43cd965SMauro Carvalho Chehab            "virtualenv":    "virtualenv",
761d43cd965SMauro Carvalho Chehab            "xelatex":       "texlive-xetex",
762d43cd965SMauro Carvalho Chehab            "yaml":          "python3-yaml",
763d43cd965SMauro Carvalho Chehab        }
764d43cd965SMauro Carvalho Chehab
765d43cd965SMauro Carvalho Chehab        if self.pdf:
766d43cd965SMauro Carvalho Chehab            pdf_pkgs = {
767d43cd965SMauro Carvalho Chehab                "fonts-dejavu": [
768d43cd965SMauro Carvalho Chehab                    "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
769d43cd965SMauro Carvalho Chehab                ],
770d43cd965SMauro Carvalho Chehab                "fonts-noto-cjk": [
771d43cd965SMauro Carvalho Chehab                    "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
772d43cd965SMauro Carvalho Chehab                    "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
773d43cd965SMauro Carvalho Chehab                    "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc",
774d43cd965SMauro 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                ],
784d43cd965SMauro Carvalho Chehab            }
785d43cd965SMauro Carvalho Chehab
786d43cd965SMauro Carvalho Chehab            for package, files in pdf_pkgs.items():
787d43cd965SMauro Carvalho Chehab                self.check_missing_file(files, package, DepManager.PDF_MANDATORY)
788d43cd965SMauro Carvalho Chehab
789d43cd965SMauro Carvalho Chehab            self.check_program("dvipng", DepManager.PDF_MANDATORY)
790d43cd965SMauro 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
796d43cd965SMauro Carvalho Chehab        return self.get_install_progs(progs, "apt-get install")
797d43cd965SMauro Carvalho Chehab
798d43cd965SMauro Carvalho Chehab    def give_redhat_hints(self):
799d43cd965SMauro Carvalho Chehab        """
800d43cd965SMauro Carvalho Chehab        Provide package installation hints for RedHat-based distros
801d43cd965SMauro Carvalho Chehab        (Fedora, RHEL and RHEL-based variants).
802d43cd965SMauro Carvalho Chehab        """
803d43cd965SMauro Carvalho Chehab        progs = {
804d43cd965SMauro Carvalho Chehab            "Pod::Usage":       "perl-Pod-Usage",
805d43cd965SMauro Carvalho Chehab            "convert":          "ImageMagick",
806d43cd965SMauro Carvalho Chehab            "dot":              "graphviz",
807d43cd965SMauro Carvalho Chehab            "python-sphinx":    "python3-sphinx",
808d43cd965SMauro Carvalho Chehab            "rsvg-convert":     "librsvg2-tools",
809d43cd965SMauro Carvalho Chehab            "virtualenv":       "python3-virtualenv",
810d43cd965SMauro Carvalho Chehab            "xelatex":          "texlive-xetex-bin",
811d43cd965SMauro Carvalho Chehab            "yaml":             "python3-pyyaml",
812d43cd965SMauro Carvalho Chehab        }
813d43cd965SMauro Carvalho Chehab
814d43cd965SMauro Carvalho Chehab        fedora_tex_pkgs = [
815d43cd965SMauro Carvalho Chehab            "dejavu-sans-fonts",
816d43cd965SMauro Carvalho Chehab            "dejavu-sans-mono-fonts",
817d43cd965SMauro Carvalho Chehab            "dejavu-serif-fonts",
818d43cd965SMauro Carvalho Chehab            "texlive-collection-fontsrecommended",
819d43cd965SMauro Carvalho Chehab            "texlive-collection-latex",
820d43cd965SMauro Carvalho Chehab            "texlive-xecjk",
821d43cd965SMauro Carvalho Chehab        ]
822d43cd965SMauro Carvalho Chehab
823d43cd965SMauro Carvalho Chehab        fedora = False
824d43cd965SMauro Carvalho Chehab        rel = None
825d43cd965SMauro Carvalho Chehab
826d43cd965SMauro Carvalho Chehab        match = re.search(r"(release|Linux)\s+(\d+)", self.system_release)
827d43cd965SMauro Carvalho Chehab        if match:
828d43cd965SMauro Carvalho Chehab            rel = int(match.group(2))
829d43cd965SMauro Carvalho Chehab
830d43cd965SMauro Carvalho Chehab        if not rel:
831d43cd965SMauro Carvalho Chehab            print("Couldn't identify release number")
832d43cd965SMauro Carvalho Chehab            noto_sans_redhat = None
833d43cd965SMauro Carvalho Chehab            self.pdf = False
834d43cd965SMauro Carvalho Chehab        elif re.search("Fedora", self.system_release):
835d43cd965SMauro Carvalho Chehab            # Fedora 38 and upper use this CJK font
836d43cd965SMauro Carvalho Chehab
837d43cd965SMauro Carvalho Chehab            noto_sans_redhat = "google-noto-sans-cjk-fonts"
838d43cd965SMauro Carvalho Chehab            fedora = True
839d43cd965SMauro Carvalho Chehab        else:
840d43cd965SMauro Carvalho Chehab            # Almalinux, CentOS, RHEL, ...
841d43cd965SMauro Carvalho Chehab
842d43cd965SMauro Carvalho Chehab            # at least up to version 9 (and Fedora < 38), that's the CJK font
843d43cd965SMauro Carvalho Chehab            noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"
844d43cd965SMauro Carvalho Chehab
845d43cd965SMauro Carvalho Chehab            progs["virtualenv"] = "python-virtualenv"
846d43cd965SMauro Carvalho Chehab
847d43cd965SMauro Carvalho Chehab            if not rel or rel < 8:
848d43cd965SMauro Carvalho Chehab                print("ERROR: Distro not supported. Too old?")
849d43cd965SMauro Carvalho Chehab                return
850d43cd965SMauro Carvalho Chehab
851d43cd965SMauro Carvalho Chehab            # RHEL 8 uses Python 3.6, which is not compatible with
852d43cd965SMauro Carvalho Chehab            # the build system anymore. Suggest Python 3.11
853d43cd965SMauro 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
858d43cd965SMauro Carvalho Chehab                self.recommend_python = True
859d43cd965SMauro 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
863d43cd965SMauro Carvalho Chehab            if not self.distro_msg:
864d43cd965SMauro Carvalho Chehab                self.distro_msg = \
865d43cd965SMauro Carvalho Chehab                    "Note: RHEL-based distros typically require extra repositories.\n" \
866d43cd965SMauro Carvalho Chehab                    "For most, enabling epel and crb are enough:\n" \
867d43cd965SMauro Carvalho Chehab                    "\tsudo dnf install -y epel-release\n" \
868d43cd965SMauro Carvalho Chehab                    "\tsudo dnf config-manager --set-enabled crb\n" \
869d43cd965SMauro Carvalho Chehab                    "Yet, some may have other required repositories. Those commands could be useful:\n" \
870d43cd965SMauro Carvalho Chehab                    "\tsudo dnf repolist all\n" \
871d43cd965SMauro Carvalho Chehab                    "\tsudo dnf repoquery --available --info <pkgs>\n" \
872d43cd965SMauro Carvalho Chehab                    "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want"
873d43cd965SMauro Carvalho Chehab
874d43cd965SMauro Carvalho Chehab        if self.pdf:
875d43cd965SMauro Carvalho Chehab            pdf_pkgs = [
876d43cd965SMauro Carvalho Chehab                "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
877d43cd965SMauro Carvalho Chehab                "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc",
878d43cd965SMauro Carvalho Chehab            ]
879d43cd965SMauro Carvalho Chehab
880d43cd965SMauro Carvalho Chehab            self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepManager.PDF_MANDATORY)
881d43cd965SMauro Carvalho Chehab
882d43cd965SMauro Carvalho Chehab            self.check_rpm_missing(fedora_tex_pkgs, DepManager.PDF_MANDATORY)
883d43cd965SMauro Carvalho Chehab
884d43cd965SMauro Carvalho Chehab            self.check_missing_tex(DepManager.PDF_MANDATORY)
885d43cd965SMauro Carvalho Chehab
886d43cd965SMauro Carvalho Chehab            # There's no texlive-ctex on RHEL 8 repositories. This will
887d43cd965SMauro Carvalho Chehab            # likely affect CJK pdf build only.
888d43cd965SMauro Carvalho Chehab            if not fedora and rel == 8:
889d43cd965SMauro Carvalho Chehab                self.deps.del_package("texlive-ctex")
890d43cd965SMauro Carvalho Chehab
891d43cd965SMauro Carvalho Chehab        return self.get_install_progs(progs, "dnf install")
892d43cd965SMauro Carvalho Chehab
893d43cd965SMauro Carvalho Chehab    def give_opensuse_hints(self):
894d43cd965SMauro Carvalho Chehab        """
895d43cd965SMauro Carvalho Chehab        Provide package installation hints for openSUSE-based distros
896d43cd965SMauro Carvalho Chehab        (Leap and Tumbleweed).
897d43cd965SMauro Carvalho Chehab        """
898d43cd965SMauro Carvalho Chehab        progs = {
899d43cd965SMauro Carvalho Chehab            "Pod::Usage":    "perl-Pod-Usage",
900d43cd965SMauro Carvalho Chehab            "convert":       "ImageMagick",
901d43cd965SMauro Carvalho Chehab            "dot":           "graphviz",
902d43cd965SMauro Carvalho Chehab            "python-sphinx": "python3-sphinx",
903d43cd965SMauro Carvalho Chehab            "virtualenv":    "python3-virtualenv",
904b2d5d61cSMauro Carvalho Chehab            "xelatex":       "texlive-xetex-bin texlive-dejavu",
905d43cd965SMauro Carvalho Chehab            "yaml":          "python3-pyyaml",
906d43cd965SMauro Carvalho Chehab        }
907d43cd965SMauro Carvalho Chehab
908d43cd965SMauro Carvalho Chehab        suse_tex_pkgs = [
909d43cd965SMauro Carvalho Chehab            "texlive-babel-english",
910d43cd965SMauro Carvalho Chehab            "texlive-caption",
911d43cd965SMauro Carvalho Chehab            "texlive-colortbl",
912d43cd965SMauro Carvalho Chehab            "texlive-courier",
913d43cd965SMauro Carvalho Chehab            "texlive-dvips",
914d43cd965SMauro Carvalho Chehab            "texlive-helvetic",
915d43cd965SMauro Carvalho Chehab            "texlive-makeindex",
916d43cd965SMauro Carvalho Chehab            "texlive-metafont",
917d43cd965SMauro Carvalho Chehab            "texlive-metapost",
918d43cd965SMauro Carvalho Chehab            "texlive-palatino",
919d43cd965SMauro Carvalho Chehab            "texlive-preview",
920d43cd965SMauro Carvalho Chehab            "texlive-times",
921d43cd965SMauro Carvalho Chehab            "texlive-zapfchan",
922d43cd965SMauro Carvalho Chehab            "texlive-zapfding",
923d43cd965SMauro Carvalho Chehab        ]
924d43cd965SMauro Carvalho Chehab
925d43cd965SMauro Carvalho Chehab        progs["latexmk"] = "texlive-latexmk-bin"
926d43cd965SMauro Carvalho Chehab
927d43cd965SMauro Carvalho Chehab        match = re.search(r"(Leap)\s+(\d+).(\d)", self.system_release)
928d43cd965SMauro Carvalho Chehab        if match:
929d43cd965SMauro Carvalho Chehab            rel = int(match.group(2))
930d43cd965SMauro Carvalho Chehab
931d43cd965SMauro Carvalho Chehab            # Leap 15.x uses Python 3.6, which is not compatible with
932d43cd965SMauro Carvalho Chehab            # the build system anymore. Suggest Python 3.11
933d43cd965SMauro Carvalho Chehab            if rel == 15:
934d43cd965SMauro 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"
937d43cd965SMauro Carvalho Chehab                    self.recommend_python = True
938d43cd965SMauro Carvalho Chehab
939d43cd965SMauro Carvalho Chehab                progs.update({
940b2d5d61cSMauro Carvalho Chehab                    "python-sphinx": "python311-Sphinx python311-Sphinx-latex",
941d43cd965SMauro Carvalho Chehab                    "virtualenv":    "python311-virtualenv",
942d43cd965SMauro Carvalho Chehab                    "yaml":          "python311-PyYAML",
943d43cd965SMauro Carvalho Chehab                })
944d43cd965SMauro Carvalho Chehab        else:
945d43cd965SMauro Carvalho Chehab            # Tumbleweed defaults to Python 3.11
946d43cd965SMauro Carvalho Chehab
947d43cd965SMauro Carvalho Chehab            progs.update({
948b2d5d61cSMauro Carvalho Chehab                "python-sphinx": "python313-Sphinx python313-Sphinx-latex",
949d43cd965SMauro Carvalho Chehab                "virtualenv":    "python313-virtualenv",
950d43cd965SMauro Carvalho Chehab                "yaml":          "python313-PyYAML",
951d43cd965SMauro Carvalho Chehab            })
952d43cd965SMauro Carvalho Chehab
953d43cd965SMauro Carvalho Chehab        # FIXME: add support for installing CJK fonts
954d43cd965SMauro Carvalho Chehab        #
955d43cd965SMauro Carvalho Chehab        # I tried hard, but was unable to find a way to install
956d43cd965SMauro Carvalho Chehab        # "Noto Sans CJK SC" on openSUSE
957d43cd965SMauro Carvalho Chehab
958d43cd965SMauro Carvalho Chehab        if self.pdf:
959d43cd965SMauro Carvalho Chehab            self.check_rpm_missing(suse_tex_pkgs, DepManager.PDF_MANDATORY)
960d43cd965SMauro Carvalho Chehab        if self.pdf:
961d43cd965SMauro Carvalho Chehab            self.check_missing_tex()
962d43cd965SMauro Carvalho Chehab
963d43cd965SMauro Carvalho Chehab        return self.get_install_progs(progs, "zypper install --no-recommends")
964d43cd965SMauro Carvalho Chehab
965d43cd965SMauro Carvalho Chehab    def give_mageia_hints(self):
966d43cd965SMauro Carvalho Chehab        """
967d43cd965SMauro Carvalho Chehab        Provide package installation hints for Mageia and OpenMandriva.
968d43cd965SMauro Carvalho Chehab        """
969d43cd965SMauro Carvalho Chehab        progs = {
970d43cd965SMauro Carvalho Chehab            "Pod::Usage":    "perl-Pod-Usage",
971d43cd965SMauro Carvalho Chehab            "convert":       "ImageMagick",
972d43cd965SMauro Carvalho Chehab            "dot":           "graphviz",
973d43cd965SMauro Carvalho Chehab            "python-sphinx": "python3-sphinx",
974d43cd965SMauro Carvalho Chehab            "rsvg-convert":  "librsvg2",
975d43cd965SMauro Carvalho Chehab            "virtualenv":    "python3-virtualenv",
976d43cd965SMauro Carvalho Chehab            "xelatex":       "texlive",
977d43cd965SMauro Carvalho Chehab            "yaml":          "python3-yaml",
978d43cd965SMauro Carvalho Chehab        }
979d43cd965SMauro Carvalho Chehab
980d43cd965SMauro Carvalho Chehab        tex_pkgs = [
981d43cd965SMauro Carvalho Chehab            "texlive-fontsextra",
982c71c5d6dSMauro Carvalho Chehab            "texlive-fonts-asian",
983c71c5d6dSMauro Carvalho Chehab            "fonts-ttf-dejavu",
984d43cd965SMauro Carvalho Chehab        ]
985d43cd965SMauro Carvalho Chehab
986d43cd965SMauro Carvalho Chehab        if re.search(r"OpenMandriva", self.system_release):
987d43cd965SMauro Carvalho Chehab            packager_cmd = "dnf install"
988d43cd965SMauro 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            ]
995d43cd965SMauro Carvalho Chehab
996d43cd965SMauro Carvalho Chehab            # Tested on OpenMandriva Lx 4.3
997d43cd965SMauro Carvalho Chehab            progs["convert"] = "imagemagick"
998d43cd965SMauro 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"
1018d43cd965SMauro Carvalho Chehab
1019d43cd965SMauro Carvalho Chehab        else:
1020d43cd965SMauro Carvalho Chehab            packager_cmd = "urpmi"
1021d43cd965SMauro Carvalho Chehab            noto_sans = "google-noto-sans-cjk-ttc-fonts"
1022d43cd965SMauro Carvalho Chehab
1023d43cd965SMauro Carvalho Chehab        progs["latexmk"] = "texlive-collection-basic"
1024d43cd965SMauro Carvalho Chehab
1025d43cd965SMauro Carvalho Chehab        if self.pdf:
1026d43cd965SMauro Carvalho Chehab            pdf_pkgs = [
1027d43cd965SMauro Carvalho Chehab                "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
1028d43cd965SMauro Carvalho Chehab                "/usr/share/fonts/TTF/NotoSans-Regular.ttf",
1029d43cd965SMauro Carvalho Chehab            ]
1030d43cd965SMauro Carvalho Chehab
1031d43cd965SMauro Carvalho Chehab            self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY)
1032d43cd965SMauro Carvalho Chehab            self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY)
1033d43cd965SMauro Carvalho Chehab
1034d43cd965SMauro Carvalho Chehab        return self.get_install_progs(progs, packager_cmd)
1035d43cd965SMauro Carvalho Chehab
1036d43cd965SMauro Carvalho Chehab    def give_arch_linux_hints(self):
1037d43cd965SMauro Carvalho Chehab        """
1038d43cd965SMauro Carvalho Chehab        Provide package installation hints for ArchLinux.
1039d43cd965SMauro Carvalho Chehab        """
1040d43cd965SMauro Carvalho Chehab        progs = {
1041d43cd965SMauro Carvalho Chehab            "convert":      "imagemagick",
1042d43cd965SMauro Carvalho Chehab            "dot":          "graphviz",
1043d43cd965SMauro Carvalho Chehab            "latexmk":      "texlive-core",
1044d43cd965SMauro Carvalho Chehab            "rsvg-convert": "extra/librsvg",
1045d43cd965SMauro Carvalho Chehab            "virtualenv":   "python-virtualenv",
1046d43cd965SMauro Carvalho Chehab            "xelatex":      "texlive-xetex",
1047d43cd965SMauro Carvalho Chehab            "yaml":         "python-yaml",
1048d43cd965SMauro Carvalho Chehab        }
1049d43cd965SMauro Carvalho Chehab
1050d43cd965SMauro Carvalho Chehab        archlinux_tex_pkgs = [
1051*c6e23912SMauro Carvalho Chehab            "texlive-basic",
1052*c6e23912SMauro Carvalho Chehab            "texlive-binextra",
1053d43cd965SMauro Carvalho Chehab            "texlive-core",
1054*c6e23912SMauro Carvalho Chehab            "texlive-fontsrecommended",
1055*c6e23912SMauro Carvalho Chehab            "texlive-langchinese",
1056*c6e23912SMauro Carvalho Chehab            "texlive-langcjk",
1057d43cd965SMauro Carvalho Chehab            "texlive-latexextra",
1058d43cd965SMauro Carvalho Chehab            "ttf-dejavu",
1059d43cd965SMauro Carvalho Chehab        ]
1060d43cd965SMauro Carvalho Chehab
1061d43cd965SMauro Carvalho Chehab        if self.pdf:
1062d43cd965SMauro Carvalho Chehab            self.check_pacman_missing(archlinux_tex_pkgs,
1063d43cd965SMauro Carvalho Chehab                                      DepManager.PDF_MANDATORY)
1064d43cd965SMauro Carvalho Chehab
1065d43cd965SMauro Carvalho Chehab            self.check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"],
1066d43cd965SMauro Carvalho Chehab                                    "noto-fonts-cjk",
1067d43cd965SMauro Carvalho Chehab                                    DepManager.PDF_MANDATORY)
1068d43cd965SMauro Carvalho Chehab
1069d43cd965SMauro Carvalho Chehab
1070d43cd965SMauro Carvalho Chehab        return self.get_install_progs(progs, "pacman -S")
1071d43cd965SMauro Carvalho Chehab
1072d43cd965SMauro Carvalho Chehab    def give_gentoo_hints(self):
1073d43cd965SMauro Carvalho Chehab        """
1074d43cd965SMauro Carvalho Chehab        Provide package installation hints for Gentoo.
1075d43cd965SMauro 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
1083d43cd965SMauro Carvalho Chehab        progs = {
1084d43cd965SMauro Carvalho Chehab            "convert":       "media-gfx/imagemagick",
1085d43cd965SMauro Carvalho Chehab            "dot":           "media-gfx/graphviz",
1086d43cd965SMauro Carvalho Chehab            "rsvg-convert":  "gnome-base/librsvg",
1087d43cd965SMauro Carvalho Chehab            "virtualenv":    "dev-python/virtualenv",
10889ff5c2f5SMauro Carvalho Chehab            "xelatex":       " ".join(texlive_deps),
1089d43cd965SMauro Carvalho Chehab            "yaml":          "dev-python/pyyaml",
1090d43cd965SMauro Carvalho Chehab            "python-sphinx": "dev-python/sphinx",
1091d43cd965SMauro Carvalho Chehab        }
1092d43cd965SMauro Carvalho Chehab
1093d43cd965SMauro Carvalho Chehab        if self.pdf:
1094d43cd965SMauro Carvalho Chehab            pdf_pkgs = {
1095d43cd965SMauro Carvalho Chehab                "media-fonts/dejavu": [
1096d43cd965SMauro Carvalho Chehab                    "/usr/share/fonts/dejavu/DejaVuSans.ttf",
1097d43cd965SMauro Carvalho Chehab                ],
1098d43cd965SMauro Carvalho Chehab                "media-fonts/noto-cjk": [
1099d43cd965SMauro Carvalho Chehab                    "/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf",
1100d43cd965SMauro Carvalho Chehab                    "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc",
1101d43cd965SMauro Carvalho Chehab                ],
1102d43cd965SMauro Carvalho Chehab            }
1103d43cd965SMauro Carvalho Chehab            for package, files in pdf_pkgs.items():
1104d43cd965SMauro Carvalho Chehab                self.check_missing_file(files, package, DepManager.PDF_MANDATORY)
1105d43cd965SMauro Carvalho Chehab
1106d43cd965SMauro Carvalho Chehab        # Handling dependencies is a nightmare, as Gentoo refuses to emerge
1107d43cd965SMauro Carvalho Chehab        # some packages if there's no package.use file describing them.
1108d43cd965SMauro Carvalho Chehab        # To make it worse, compilation flags shall also be present there
1109d43cd965SMauro Carvalho Chehab        # for some packages. If USE is not perfect, error/warning messages
1110d43cd965SMauro Carvalho Chehab        #   like those are shown:
1111d43cd965SMauro Carvalho Chehab        #
1112d43cd965SMauro Carvalho Chehab        #   !!! The following binary packages have been ignored due to non matching USE:
1113d43cd965SMauro Carvalho Chehab        #
1114d43cd965SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg
1115d43cd965SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg
1116d43cd965SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg
1117d43cd965SMauro Carvalho Chehab        #    =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg
1118d43cd965SMauro 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
1119d43cd965SMauro Carvalho Chehab        #    =media-fonts/noto-cjk-20190416 X
1120d43cd965SMauro Carvalho Chehab        #    =app-text/texlive-core-2024-r1 X cjk -xetex
1121d43cd965SMauro Carvalho Chehab        #    =app-text/texlive-core-2024-r1 X -xetex
1122d43cd965SMauro Carvalho Chehab        #    =app-text/texlive-core-2024-r1 -xetex
1123d43cd965SMauro Carvalho Chehab        #    =dev-libs/zziplib-0.13.79-r1 sdl
1124d43cd965SMauro Carvalho Chehab        #
1125d43cd965SMauro Carvalho Chehab        # And will ignore such packages, installing the remaining ones. That
1126d43cd965SMauro Carvalho Chehab        # affects mostly the image extension and PDF generation.
1127d43cd965SMauro Carvalho Chehab
1128d43cd965SMauro Carvalho Chehab        # Package dependencies and the minimal needed args:
1129d43cd965SMauro Carvalho Chehab        portages = {
1130d43cd965SMauro Carvalho Chehab            "graphviz": "media-gfx/graphviz",
1131d43cd965SMauro Carvalho Chehab            "imagemagick": "media-gfx/imagemagick",
1132d43cd965SMauro Carvalho Chehab            "media-libs": "media-libs/harfbuzz icu",
1133d43cd965SMauro Carvalho Chehab            "media-fonts": "media-fonts/noto-cjk",
1134d43cd965SMauro Carvalho Chehab            "texlive": "app-text/texlive-core xetex",
1135d43cd965SMauro Carvalho Chehab            "zziblib": "dev-libs/zziplib sdl",
1136d43cd965SMauro Carvalho Chehab        }
1137d43cd965SMauro Carvalho Chehab
1138d43cd965SMauro Carvalho Chehab        extra_cmds = ""
1139d43cd965SMauro Carvalho Chehab        if not self.distro_msg:
1140d43cd965SMauro Carvalho Chehab            self.distro_msg = "Note: Gentoo requires package.use to be adjusted before emerging packages"
1141d43cd965SMauro Carvalho Chehab
1142d43cd965SMauro Carvalho Chehab            use_base = "/etc/portage/package.use"
1143d43cd965SMauro Carvalho Chehab            files = glob(f"{use_base}/*")
1144d43cd965SMauro Carvalho Chehab
1145d43cd965SMauro Carvalho Chehab            for fname, portage in portages.items():
1146d43cd965SMauro Carvalho Chehab                install = False
1147d43cd965SMauro Carvalho Chehab
1148d43cd965SMauro Carvalho Chehab                while install is False:
1149d43cd965SMauro Carvalho Chehab                    if not files:
1150d43cd965SMauro Carvalho Chehab                        # No files under package.usage. Install all
1151d43cd965SMauro Carvalho Chehab                        install = True
1152d43cd965SMauro Carvalho Chehab                        break
1153d43cd965SMauro Carvalho Chehab
1154d43cd965SMauro Carvalho Chehab                    args = portage.split(" ")
1155d43cd965SMauro Carvalho Chehab
1156d43cd965SMauro Carvalho Chehab                    name = args.pop(0)
1157d43cd965SMauro Carvalho Chehab
1158d43cd965SMauro Carvalho Chehab                    cmd = ["grep", "-l", "-E", rf"^{name}\b" ] + files
1159d43cd965SMauro Carvalho Chehab                    result = self.run(cmd, stdout=subprocess.PIPE, text=True)
1160d43cd965SMauro Carvalho Chehab                    if result.returncode or not result.stdout.strip():
1161d43cd965SMauro Carvalho Chehab                        # File containing portage name not found
1162d43cd965SMauro Carvalho Chehab                        install = True
1163d43cd965SMauro Carvalho Chehab                        break
1164d43cd965SMauro Carvalho Chehab
1165d43cd965SMauro Carvalho Chehab                    # Ensure that needed USE flags are present
1166d43cd965SMauro Carvalho Chehab                    if args:
1167d43cd965SMauro Carvalho Chehab                        match_fname = result.stdout.strip()
1168d43cd965SMauro Carvalho Chehab                        with open(match_fname, 'r', encoding='utf8',
1169d43cd965SMauro Carvalho Chehab                                errors='backslashreplace') as fp:
1170d43cd965SMauro Carvalho Chehab                            for line in fp:
1171d43cd965SMauro Carvalho Chehab                                for arg in args:
1172d43cd965SMauro Carvalho Chehab                                    if arg.startswith("-"):
1173d43cd965SMauro Carvalho Chehab                                        continue
1174d43cd965SMauro Carvalho Chehab
1175d43cd965SMauro Carvalho Chehab                                if not re.search(rf"\s*{arg}\b", line):
1176d43cd965SMauro Carvalho Chehab                                    # Needed file argument not found
1177d43cd965SMauro Carvalho Chehab                                    install = True
1178d43cd965SMauro Carvalho Chehab                                    break
1179d43cd965SMauro Carvalho Chehab
1180d43cd965SMauro Carvalho Chehab                    # Everything looks ok, don't install
1181d43cd965SMauro Carvalho Chehab                    break
1182d43cd965SMauro Carvalho Chehab
1183d43cd965SMauro Carvalho Chehab                # emit a code to setup missing USE
1184d43cd965SMauro Carvalho Chehab                if install:
1185d43cd965SMauro Carvalho Chehab                    extra_cmds += (f"sudo su -c 'echo \"{portage}\" > {use_base}/{fname}'\n")
1186d43cd965SMauro Carvalho Chehab
1187d43cd965SMauro Carvalho Chehab        # Now, we can use emerge and let it respect USE
1188d43cd965SMauro Carvalho Chehab        return self.get_install_progs(progs,
1189d43cd965SMauro Carvalho Chehab                                      "emerge --ask --changed-use --binpkg-respect-use=y",
1190d43cd965SMauro Carvalho Chehab                                      extra_cmds)
1191d43cd965SMauro Carvalho Chehab
1192d43cd965SMauro Carvalho Chehab    def get_install(self):
1193d43cd965SMauro Carvalho Chehab        """
1194d43cd965SMauro Carvalho Chehab        OS-specific hints logic. Seeks for a hinter. If found, use it to
1195d43cd965SMauro Carvalho Chehab        provide package-manager specific install commands.
1196d43cd965SMauro Carvalho Chehab
1197d43cd965SMauro Carvalho Chehab        Otherwise, outputs install instructions for the meta-packages.
1198d43cd965SMauro Carvalho Chehab
1199d43cd965SMauro Carvalho Chehab        Returns a string with the command to be executed to install the
1200d43cd965SMauro Carvalho Chehab        the needed packages, if distro found. Otherwise, return just a
1201d43cd965SMauro Carvalho Chehab        list of packages that require installation.
1202d43cd965SMauro Carvalho Chehab        """
1203d43cd965SMauro Carvalho Chehab        os_hints = {
1204d43cd965SMauro Carvalho Chehab            re.compile("Red Hat Enterprise Linux"):   self.give_redhat_hints,
1205d43cd965SMauro Carvalho Chehab            re.compile("Fedora"):                     self.give_redhat_hints,
1206d43cd965SMauro Carvalho Chehab            re.compile("AlmaLinux"):                  self.give_redhat_hints,
1207d43cd965SMauro Carvalho Chehab            re.compile("Amazon Linux"):               self.give_redhat_hints,
1208d43cd965SMauro Carvalho Chehab            re.compile("CentOS"):                     self.give_redhat_hints,
1209d43cd965SMauro Carvalho Chehab            re.compile("openEuler"):                  self.give_redhat_hints,
1210d43cd965SMauro Carvalho Chehab            re.compile("Oracle Linux Server"):        self.give_redhat_hints,
1211d43cd965SMauro Carvalho Chehab            re.compile("Rocky Linux"):                self.give_redhat_hints,
1212d43cd965SMauro Carvalho Chehab            re.compile("Springdale Open Enterprise"): self.give_redhat_hints,
1213d43cd965SMauro Carvalho Chehab
1214d43cd965SMauro Carvalho Chehab            re.compile("Ubuntu"):                     self.give_debian_hints,
1215d43cd965SMauro Carvalho Chehab            re.compile("Debian"):                     self.give_debian_hints,
1216d43cd965SMauro Carvalho Chehab            re.compile("Devuan"):                     self.give_debian_hints,
1217d43cd965SMauro Carvalho Chehab            re.compile("Kali"):                       self.give_debian_hints,
1218d43cd965SMauro Carvalho Chehab            re.compile("Mint"):                       self.give_debian_hints,
1219d43cd965SMauro Carvalho Chehab
1220d43cd965SMauro Carvalho Chehab            re.compile("openSUSE"):                   self.give_opensuse_hints,
1221d43cd965SMauro Carvalho Chehab
1222d43cd965SMauro Carvalho Chehab            re.compile("Mageia"):                     self.give_mageia_hints,
1223d43cd965SMauro Carvalho Chehab            re.compile("OpenMandriva"):               self.give_mageia_hints,
1224d43cd965SMauro Carvalho Chehab
1225d43cd965SMauro Carvalho Chehab            re.compile("Arch Linux"):                 self.give_arch_linux_hints,
1226d43cd965SMauro Carvalho Chehab            re.compile("Gentoo"):                     self.give_gentoo_hints,
1227d43cd965SMauro Carvalho Chehab        }
1228d43cd965SMauro Carvalho Chehab
1229d43cd965SMauro Carvalho Chehab        # If the OS is detected, use per-OS hint logic
1230d43cd965SMauro Carvalho Chehab        for regex, os_hint in os_hints.items():
1231d43cd965SMauro Carvalho Chehab            if regex.search(self.system_release):
1232d43cd965SMauro Carvalho Chehab                return os_hint()
1233d43cd965SMauro Carvalho Chehab
1234d43cd965SMauro Carvalho Chehab        #
1235d43cd965SMauro Carvalho Chehab        # Fall-back to generic hint code for other distros
1236d43cd965SMauro Carvalho Chehab        # That's far from ideal, specially for LaTeX dependencies.
1237d43cd965SMauro Carvalho Chehab        #
1238d43cd965SMauro Carvalho Chehab        progs = {"sphinx-build": "sphinx"}
1239d43cd965SMauro Carvalho Chehab        if self.pdf:
1240d43cd965SMauro Carvalho Chehab            self.check_missing_tex()
1241d43cd965SMauro Carvalho Chehab
1242d43cd965SMauro Carvalho Chehab        self.distro_msg = \
1243d43cd965SMauro Carvalho Chehab            f"I don't know distro {self.system_release}.\n" \
1244d43cd965SMauro Carvalho Chehab            "So, I can't provide you a hint with the install procedure.\n" \
1245491a9951SMauro Carvalho Chehab            "There are likely missing dependencies."
1246d43cd965SMauro Carvalho Chehab
1247d43cd965SMauro Carvalho Chehab        return self.get_install_progs(progs, None)
1248d43cd965SMauro Carvalho Chehab
1249d43cd965SMauro Carvalho Chehab    #
1250d43cd965SMauro Carvalho Chehab    # Common dependencies
1251d43cd965SMauro Carvalho Chehab    #
1252d43cd965SMauro Carvalho Chehab    def deactivate_help(self):
1253d43cd965SMauro Carvalho Chehab        """
1254d43cd965SMauro Carvalho Chehab        Print a helper message to disable a virtual environment.
1255d43cd965SMauro Carvalho Chehab        """
1256d43cd965SMauro Carvalho Chehab
1257d43cd965SMauro Carvalho Chehab        print("\n    If you want to exit the virtualenv, you can use:")
1258d43cd965SMauro Carvalho Chehab        print("\tdeactivate")
1259d43cd965SMauro Carvalho Chehab
1260d43cd965SMauro Carvalho Chehab    def get_virtenv(self):
1261d43cd965SMauro Carvalho Chehab        """
1262d43cd965SMauro Carvalho Chehab        Give a hint about how to activate an already-existing virtual
1263d43cd965SMauro Carvalho Chehab        environment containing sphinx-build.
1264d43cd965SMauro Carvalho Chehab
1265d43cd965SMauro Carvalho Chehab        Returns a tuble with (activate_cmd_path, sphinx_version) with
1266d43cd965SMauro Carvalho Chehab        the newest available virtual env.
1267d43cd965SMauro Carvalho Chehab        """
1268d43cd965SMauro Carvalho Chehab
1269d43cd965SMauro Carvalho Chehab        cwd = os.getcwd()
1270d43cd965SMauro Carvalho Chehab
1271d43cd965SMauro Carvalho Chehab        activates = []
1272d43cd965SMauro Carvalho Chehab
1273d43cd965SMauro Carvalho Chehab        # Add all sphinx prefixes with possible version numbers
1274d43cd965SMauro Carvalho Chehab        for p in self.virtenv_prefix:
1275d43cd965SMauro Carvalho Chehab            activates += glob(f"{cwd}/{p}[0-9]*/bin/activate")
1276d43cd965SMauro Carvalho Chehab
1277d43cd965SMauro Carvalho Chehab        activates.sort(reverse=True, key=str.lower)
1278d43cd965SMauro Carvalho Chehab
1279d43cd965SMauro Carvalho Chehab        # Place sphinx_latest first, if it exists
1280d43cd965SMauro Carvalho Chehab        for p in self.virtenv_prefix:
1281d43cd965SMauro Carvalho Chehab            activates = glob(f"{cwd}/{p}*latest/bin/activate") + activates
1282d43cd965SMauro Carvalho Chehab
1283d43cd965SMauro Carvalho Chehab        ver = (0, 0, 0)
1284d43cd965SMauro Carvalho Chehab        for f in activates:
1285d43cd965SMauro Carvalho Chehab            # Discard too old Sphinx virtual environments
1286d43cd965SMauro Carvalho Chehab            match = re.search(r"(\d+)\.(\d+)\.(\d+)", f)
1287d43cd965SMauro Carvalho Chehab            if match:
1288d43cd965SMauro Carvalho Chehab                ver = (int(match.group(1)), int(match.group(2)), int(match.group(3)))
1289d43cd965SMauro Carvalho Chehab
1290d43cd965SMauro Carvalho Chehab                if ver < self.min_version:
1291d43cd965SMauro Carvalho Chehab                    continue
1292d43cd965SMauro Carvalho Chehab
1293d43cd965SMauro Carvalho Chehab            sphinx_cmd = f.replace("activate", "sphinx-build")
1294d43cd965SMauro Carvalho Chehab            if not os.path.isfile(sphinx_cmd):
1295d43cd965SMauro Carvalho Chehab                continue
1296d43cd965SMauro Carvalho Chehab
1297d43cd965SMauro Carvalho Chehab            ver = self.get_sphinx_version(sphinx_cmd)
1298d43cd965SMauro Carvalho Chehab
1299d43cd965SMauro Carvalho Chehab            if not ver:
1300d43cd965SMauro Carvalho Chehab                venv_dir = f.replace("/bin/activate", "")
1301d43cd965SMauro Carvalho Chehab                print(f"Warning: virtual environment {venv_dir} is not working.\n" \
1302d43cd965SMauro Carvalho Chehab                      "Python version upgrade? Remove it with:\n\n" \
1303d43cd965SMauro Carvalho Chehab                      "\trm -rf {venv_dir}\n\n")
1304d43cd965SMauro Carvalho Chehab            else:
1305d43cd965SMauro Carvalho Chehab                if self.need_sphinx and ver >= self.min_version:
1306d43cd965SMauro Carvalho Chehab                    return (f, ver)
1307d43cd965SMauro Carvalho Chehab                elif parse_version(ver) > self.cur_version:
1308d43cd965SMauro Carvalho Chehab                    return (f, ver)
1309d43cd965SMauro Carvalho Chehab
1310d43cd965SMauro Carvalho Chehab        return ("", ver)
1311d43cd965SMauro Carvalho Chehab
1312d43cd965SMauro Carvalho Chehab    def recommend_sphinx_upgrade(self):
1313d43cd965SMauro Carvalho Chehab        """
1314d43cd965SMauro Carvalho Chehab        Check if Sphinx needs to be upgraded.
1315d43cd965SMauro Carvalho Chehab
1316d43cd965SMauro Carvalho Chehab        Returns a tuple with the higest available Sphinx version if found.
1317d43cd965SMauro Carvalho Chehab        Otherwise, returns None to indicate either that no upgrade is needed
1318d43cd965SMauro Carvalho Chehab        or no venv was found.
1319d43cd965SMauro Carvalho Chehab        """
1320d43cd965SMauro Carvalho Chehab
1321d43cd965SMauro Carvalho Chehab        # Avoid running sphinx-builds from venv if cur_version is good
1322d43cd965SMauro Carvalho Chehab        if self.cur_version and self.cur_version >= RECOMMENDED_VERSION:
1323d43cd965SMauro Carvalho Chehab            self.latest_avail_ver = self.cur_version
1324d43cd965SMauro Carvalho Chehab            return None
1325d43cd965SMauro Carvalho Chehab
1326d43cd965SMauro Carvalho Chehab        # Get the highest version from sphinx_*/bin/sphinx-build and the
1327d43cd965SMauro Carvalho Chehab        # corresponding command to activate the venv/virtenv
1328d43cd965SMauro Carvalho Chehab        self.activate_cmd, self.venv_ver = self.get_virtenv()
1329d43cd965SMauro Carvalho Chehab
1330d43cd965SMauro Carvalho Chehab        # Store the highest version from Sphinx existing virtualenvs
1331d43cd965SMauro Carvalho Chehab        if self.activate_cmd and self.venv_ver > self.cur_version:
1332d43cd965SMauro Carvalho Chehab            self.latest_avail_ver = self.venv_ver
1333d43cd965SMauro Carvalho Chehab        else:
1334d43cd965SMauro Carvalho Chehab            if self.cur_version:
1335d43cd965SMauro Carvalho Chehab                self.latest_avail_ver = self.cur_version
1336d43cd965SMauro Carvalho Chehab            else:
1337d43cd965SMauro Carvalho Chehab                self.latest_avail_ver = (0, 0, 0)
1338d43cd965SMauro Carvalho Chehab
1339d43cd965SMauro Carvalho Chehab        # As we don't know package version of Sphinx, and there's no
1340d43cd965SMauro Carvalho Chehab        # virtual environments, don't check if upgrades are needed
1341d43cd965SMauro Carvalho Chehab        if not self.virtualenv:
1342d43cd965SMauro Carvalho Chehab            if not self.latest_avail_ver:
1343d43cd965SMauro Carvalho Chehab                return None
1344d43cd965SMauro Carvalho Chehab
1345d43cd965SMauro Carvalho Chehab            return self.latest_avail_ver
1346d43cd965SMauro Carvalho Chehab
1347d43cd965SMauro Carvalho Chehab        # Either there are already a virtual env or a new one should be created
13489f51a1d6SMauro Carvalho Chehab        self.need_pip = True
1349d43cd965SMauro Carvalho Chehab
1350d43cd965SMauro Carvalho Chehab        if not self.latest_avail_ver:
1351d43cd965SMauro Carvalho Chehab            return None
1352d43cd965SMauro Carvalho Chehab
1353d43cd965SMauro Carvalho Chehab        # Return if the reason is due to an upgrade or not
1354d43cd965SMauro Carvalho Chehab        if self.latest_avail_ver != (0, 0, 0):
1355d43cd965SMauro Carvalho Chehab            if self.latest_avail_ver < RECOMMENDED_VERSION:
1356d43cd965SMauro Carvalho Chehab                self.rec_sphinx_upgrade = 1
1357d43cd965SMauro Carvalho Chehab
1358d43cd965SMauro Carvalho Chehab        return self.latest_avail_ver
1359d43cd965SMauro Carvalho Chehab
1360d43cd965SMauro Carvalho Chehab    def recommend_package(self):
1361d43cd965SMauro Carvalho Chehab        """
1362d43cd965SMauro Carvalho Chehab        Recommend installing Sphinx as a distro-specific package.
1363d43cd965SMauro Carvalho Chehab        """
1364d43cd965SMauro Carvalho Chehab
1365d43cd965SMauro Carvalho Chehab        print("\n2) As a package with:")
1366d43cd965SMauro Carvalho Chehab
1367d43cd965SMauro Carvalho Chehab        old_need = self.deps.need
1368d43cd965SMauro Carvalho Chehab        old_optional = self.deps.optional
1369d43cd965SMauro Carvalho Chehab
1370d43cd965SMauro Carvalho Chehab        self.pdf = False
1371d43cd965SMauro Carvalho Chehab        self.deps.optional = 0
1372d43cd965SMauro Carvalho Chehab        old_verbose = self.verbose_warn_install
1373d43cd965SMauro Carvalho Chehab        self.verbose_warn_install = 0
1374d43cd965SMauro Carvalho Chehab
1375d43cd965SMauro Carvalho Chehab        self.deps.clear_deps()
1376d43cd965SMauro Carvalho Chehab
1377d43cd965SMauro Carvalho Chehab        self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY)
1378d43cd965SMauro Carvalho Chehab
1379d43cd965SMauro Carvalho Chehab        cmd = self.get_install()
1380d43cd965SMauro Carvalho Chehab        if cmd:
1381d43cd965SMauro Carvalho Chehab            print(cmd)
1382d43cd965SMauro Carvalho Chehab
1383d43cd965SMauro Carvalho Chehab        self.deps.need = old_need
1384d43cd965SMauro Carvalho Chehab        self.deps.optional = old_optional
1385d43cd965SMauro Carvalho Chehab        self.verbose_warn_install = old_verbose
1386d43cd965SMauro Carvalho Chehab
1387d43cd965SMauro Carvalho Chehab    def recommend_sphinx_version(self, virtualenv_cmd):
1388d43cd965SMauro Carvalho Chehab        """
1389d43cd965SMauro Carvalho Chehab        Provide recommendations for installing or upgrading Sphinx based
1390d43cd965SMauro Carvalho Chehab        on current version.
1391d43cd965SMauro Carvalho Chehab
1392d43cd965SMauro Carvalho Chehab        The logic here is complex, as it have to deal with different versions:
1393d43cd965SMauro Carvalho Chehab
1394d43cd965SMauro Carvalho Chehab        - minimal supported version;
1395d43cd965SMauro Carvalho Chehab        - minimal PDF version;
1396d43cd965SMauro Carvalho Chehab        - recommended version.
1397d43cd965SMauro Carvalho Chehab
1398d43cd965SMauro Carvalho Chehab        It also needs to work fine with both distro's package and
1399d43cd965SMauro Carvalho Chehab        venv/virtualenv
1400d43cd965SMauro Carvalho Chehab        """
1401d43cd965SMauro Carvalho Chehab
1402d43cd965SMauro 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" \
1406d43cd965SMauro Carvalho Chehab                    "Please upgrade it and re-run.\n")
1407d43cd965SMauro Carvalho Chehab                return
1408d43cd965SMauro Carvalho Chehab
1409d43cd965SMauro Carvalho Chehab        # Version is OK. Nothing to do.
1410d43cd965SMauro Carvalho Chehab        if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION:
1411d43cd965SMauro Carvalho Chehab            return
1412d43cd965SMauro Carvalho Chehab
1413d43cd965SMauro Carvalho Chehab        if self.latest_avail_ver:
1414d43cd965SMauro Carvalho Chehab            latest_avail_ver = ver_str(self.latest_avail_ver)
1415d43cd965SMauro Carvalho Chehab
1416d43cd965SMauro Carvalho Chehab        if not self.need_sphinx:
1417d43cd965SMauro Carvalho Chehab            # sphinx-build is present and its version is >= $min_version
1418d43cd965SMauro Carvalho Chehab
1419d43cd965SMauro Carvalho Chehab            # only recommend enabling a newer virtenv version if makes sense.
1420d43cd965SMauro Carvalho Chehab            if self.latest_avail_ver and self.latest_avail_ver > self.cur_version:
1421d43cd965SMauro Carvalho Chehab                print(f"\nYou may also use the newer Sphinx version {latest_avail_ver} with:")
1422d43cd965SMauro Carvalho Chehab                if f"{self.virtenv_prefix}" in os.getcwd():
1423d43cd965SMauro Carvalho Chehab                    print("\tdeactivate")
1424d43cd965SMauro Carvalho Chehab                print(f"\t. {self.activate_cmd}")
1425d43cd965SMauro Carvalho Chehab                self.deactivate_help()
1426d43cd965SMauro Carvalho Chehab                return
1427d43cd965SMauro Carvalho Chehab
1428d43cd965SMauro Carvalho Chehab            if self.latest_avail_ver and self.latest_avail_ver >= RECOMMENDED_VERSION:
1429d43cd965SMauro Carvalho Chehab                return
1430d43cd965SMauro Carvalho Chehab
1431d43cd965SMauro Carvalho Chehab        if not self.virtualenv:
1432d43cd965SMauro Carvalho Chehab            # No sphinx either via package or via virtenv. As we can't
1433d43cd965SMauro Carvalho Chehab            # Compare the versions here, just return, recommending the
1434d43cd965SMauro Carvalho Chehab            # user to install it from the package distro.
1435d43cd965SMauro Carvalho Chehab            if not self.latest_avail_ver or self.latest_avail_ver == (0, 0, 0):
1436d43cd965SMauro Carvalho Chehab                return
1437d43cd965SMauro Carvalho Chehab
1438d43cd965SMauro Carvalho Chehab            # User doesn't want a virtenv recommendation, but he already
1439d43cd965SMauro Carvalho Chehab            # installed one via virtenv with a newer version.
1440d43cd965SMauro Carvalho Chehab            # So, print commands to enable it
1441d43cd965SMauro Carvalho Chehab            if self.latest_avail_ver > self.cur_version:
1442d43cd965SMauro Carvalho Chehab                print(f"\nYou may also use the Sphinx virtualenv version {latest_avail_ver} with:")
1443d43cd965SMauro Carvalho Chehab                if f"{self.virtenv_prefix}" in os.getcwd():
1444d43cd965SMauro Carvalho Chehab                    print("\tdeactivate")
1445d43cd965SMauro Carvalho Chehab                print(f"\t. {self.activate_cmd}")
1446d43cd965SMauro Carvalho Chehab                self.deactivate_help()
1447d43cd965SMauro Carvalho Chehab                return
1448d43cd965SMauro Carvalho Chehab            print("\n")
1449d43cd965SMauro Carvalho Chehab        else:
1450d43cd965SMauro Carvalho Chehab            if self.need_sphinx:
1451d43cd965SMauro Carvalho Chehab                self.deps.need += 1
1452d43cd965SMauro Carvalho Chehab
1453d43cd965SMauro Carvalho Chehab        # Suggest newer versions if current ones are too old
1454d43cd965SMauro Carvalho Chehab        if self.latest_avail_ver and self.latest_avail_ver >= self.min_version:
1455d43cd965SMauro Carvalho Chehab            if self.latest_avail_ver >= RECOMMENDED_VERSION:
1456d43cd965SMauro Carvalho Chehab                print(f"\nNeed to activate Sphinx (version {latest_avail_ver}) on virtualenv with:")
1457d43cd965SMauro Carvalho Chehab                print(f"\t. {self.activate_cmd}")
1458d43cd965SMauro Carvalho Chehab                self.deactivate_help()
1459d43cd965SMauro Carvalho Chehab                return
1460d43cd965SMauro Carvalho Chehab
1461d43cd965SMauro Carvalho Chehab            # Version is above the minimal required one, but may be
1462d43cd965SMauro Carvalho Chehab            # below the recommended one. So, print warnings/notes
1463d43cd965SMauro Carvalho Chehab            if self.latest_avail_ver < RECOMMENDED_VERSION:
1464d43cd965SMauro Carvalho Chehab                print(f"Warning: It is recommended at least Sphinx version {RECOMMENDED_VERSION}.")
1465d43cd965SMauro Carvalho Chehab
1466d43cd965SMauro Carvalho Chehab        # At this point, either it needs Sphinx or upgrade is recommended,
1467d43cd965SMauro Carvalho Chehab        # both via pip
1468d43cd965SMauro Carvalho Chehab
1469d43cd965SMauro Carvalho Chehab        if self.rec_sphinx_upgrade:
1470d43cd965SMauro Carvalho Chehab            if not self.virtualenv:
1471d43cd965SMauro Carvalho Chehab                print("Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n")
1472d43cd965SMauro Carvalho Chehab            else:
1473d43cd965SMauro Carvalho Chehab                print("To upgrade Sphinx, use:\n\n")
1474d43cd965SMauro Carvalho Chehab        else:
1475d43cd965SMauro Carvalho Chehab            print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n")
1476d43cd965SMauro Carvalho Chehab
1477d43cd965SMauro Carvalho Chehab        if not virtualenv_cmd:
1478d43cd965SMauro Carvalho Chehab            print("   Currently not possible.\n")
1479d43cd965SMauro Carvalho Chehab            print("   Please upgrade Python to a newer version and run this script again")
1480d43cd965SMauro Carvalho Chehab        else:
1481d43cd965SMauro Carvalho Chehab            print(f"\t{virtualenv_cmd} {self.virtenv_dir}")
1482d43cd965SMauro Carvalho Chehab            print(f"\t. {self.virtenv_dir}/bin/activate")
1483d43cd965SMauro Carvalho Chehab            print(f"\tpip install -r {self.requirement_file}")
1484d43cd965SMauro Carvalho Chehab            self.deactivate_help()
1485d43cd965SMauro Carvalho Chehab
1486d43cd965SMauro Carvalho Chehab        if self.package_supported:
1487d43cd965SMauro Carvalho Chehab            self.recommend_package()
1488d43cd965SMauro Carvalho Chehab
1489d43cd965SMauro Carvalho Chehab        print("\n" \
1490d43cd965SMauro Carvalho Chehab              "   Please note that Sphinx currentlys produce false-positive\n" \
1491d43cd965SMauro Carvalho Chehab              "   warnings when the same name is used for more than one type (functions,\n" \
1492d43cd965SMauro Carvalho Chehab              "   structs, enums,...). This is known Sphinx bug. For more details, see:\n" \
1493d43cd965SMauro Carvalho Chehab              "\thttps://github.com/sphinx-doc/sphinx/pull/8313")
1494d43cd965SMauro Carvalho Chehab
1495d43cd965SMauro Carvalho Chehab    def check_needs(self):
1496d43cd965SMauro Carvalho Chehab        """
1497d43cd965SMauro Carvalho Chehab        Main method that checks needed dependencies and provides
1498d43cd965SMauro Carvalho Chehab        recommendations.
1499d43cd965SMauro Carvalho Chehab        """
1500d43cd965SMauro Carvalho Chehab        self.python_cmd = sys.executable
1501d43cd965SMauro Carvalho Chehab
1502d43cd965SMauro Carvalho Chehab        # Check if Sphinx is already accessible from current environment
1503d43cd965SMauro Carvalho Chehab        self.check_sphinx(self.conf)
1504d43cd965SMauro Carvalho Chehab
1505d43cd965SMauro Carvalho Chehab        if self.system_release:
1506d43cd965SMauro Carvalho Chehab            print(f"Detected OS: {self.system_release}.")
1507d43cd965SMauro Carvalho Chehab        else:
1508d43cd965SMauro Carvalho Chehab            print("Unknown OS")
1509d43cd965SMauro Carvalho Chehab        if self.cur_version != (0, 0, 0):
1510d43cd965SMauro Carvalho Chehab            ver = ver_str(self.cur_version)
1511d43cd965SMauro Carvalho Chehab            print(f"Sphinx version: {ver}\n")
1512d43cd965SMauro Carvalho Chehab
1513d43cd965SMauro Carvalho Chehab        # Check the type of virtual env, depending on Python version
1514d43cd965SMauro Carvalho Chehab        virtualenv_cmd = None
1515d43cd965SMauro Carvalho Chehab
1516d43cd965SMauro Carvalho Chehab        if sys.version_info < MIN_PYTHON_VERSION:
1517d43cd965SMauro Carvalho Chehab            min_ver = ver_str(MIN_PYTHON_VERSION)
1518d43cd965SMauro Carvalho Chehab            print(f"ERROR: at least python {min_ver} is required to build the kernel docs")
1519d43cd965SMauro Carvalho Chehab            self.need_sphinx = 1
1520d43cd965SMauro Carvalho Chehab
1521d43cd965SMauro Carvalho Chehab        self.venv_ver = self.recommend_sphinx_upgrade()
1522d43cd965SMauro Carvalho Chehab
1523d43cd965SMauro Carvalho Chehab        if self.need_pip:
1524d43cd965SMauro Carvalho Chehab            if sys.version_info < MIN_PYTHON_VERSION:
1525d43cd965SMauro Carvalho Chehab                self.need_pip = False
1526d43cd965SMauro Carvalho Chehab                print("Warning: python version is not supported.")
1527d43cd965SMauro Carvalho Chehab            else:
1528d43cd965SMauro Carvalho Chehab                virtualenv_cmd = f"{self.python_cmd} -m venv"
1529d43cd965SMauro Carvalho Chehab                self.check_python_module("ensurepip")
1530d43cd965SMauro Carvalho Chehab
1531d43cd965SMauro Carvalho Chehab        # Check for needed programs/tools
1532d43cd965SMauro Carvalho Chehab        self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY)
1533d43cd965SMauro Carvalho Chehab
1534d43cd965SMauro Carvalho Chehab        self.check_program("make", DepManager.SYSTEM_MANDATORY)
1535df4d2f96SMauro Carvalho Chehab        self.check_program("which", DepManager.SYSTEM_MANDATORY)
1536d43cd965SMauro Carvalho Chehab
1537d43cd965SMauro Carvalho Chehab        self.check_program("dot", DepManager.SYSTEM_OPTIONAL)
1538d43cd965SMauro Carvalho Chehab        self.check_program("convert", DepManager.SYSTEM_OPTIONAL)
1539d43cd965SMauro Carvalho Chehab
1540d43cd965SMauro Carvalho Chehab        self.check_python_module("yaml")
1541d43cd965SMauro Carvalho Chehab
1542d43cd965SMauro Carvalho Chehab        if self.pdf:
1543d43cd965SMauro Carvalho Chehab            self.check_program("xelatex", DepManager.PDF_MANDATORY)
1544d43cd965SMauro Carvalho Chehab            self.check_program("rsvg-convert", DepManager.PDF_MANDATORY)
1545d43cd965SMauro Carvalho Chehab            self.check_program("latexmk", DepManager.PDF_MANDATORY)
1546d43cd965SMauro Carvalho Chehab
1547d43cd965SMauro Carvalho Chehab        # Do distro-specific checks and output distro-install commands
1548d43cd965SMauro Carvalho Chehab        cmd = self.get_install()
1549d43cd965SMauro Carvalho Chehab        if cmd:
1550d43cd965SMauro Carvalho Chehab            print(cmd)
1551d43cd965SMauro Carvalho Chehab
1552d43cd965SMauro Carvalho Chehab        # If distro requires some special instructions, print here.
1553d43cd965SMauro Carvalho Chehab        # Please notice that get_install() needs to be called first.
1554d43cd965SMauro Carvalho Chehab        if self.distro_msg:
1555d43cd965SMauro Carvalho Chehab            print("\n" + self.distro_msg)
1556d43cd965SMauro Carvalho Chehab
1557d43cd965SMauro Carvalho Chehab        if not self.python_cmd:
1558d43cd965SMauro Carvalho Chehab            if self.need == 1:
1559d43cd965SMauro Carvalho Chehab                sys.exit("Can't build as 1 mandatory dependency is missing")
1560d43cd965SMauro Carvalho Chehab            elif self.need:
1561d43cd965SMauro Carvalho Chehab                sys.exit(f"Can't build as {self.need} mandatory dependencies are missing")
1562d43cd965SMauro Carvalho Chehab
1563d43cd965SMauro Carvalho Chehab        # Check if sphinx-build is called sphinx-build-3
1564d43cd965SMauro Carvalho Chehab        if self.need_symlink:
1565d43cd965SMauro Carvalho Chehab            sphinx_path = self.which("sphinx-build-3")
1566d43cd965SMauro Carvalho Chehab            if sphinx_path:
1567d43cd965SMauro Carvalho Chehab                print(f"\tsudo ln -sf {sphinx_path} /usr/bin/sphinx-build\n")
1568d43cd965SMauro Carvalho Chehab
1569d43cd965SMauro Carvalho Chehab        self.recommend_sphinx_version(virtualenv_cmd)
1570d43cd965SMauro Carvalho Chehab        print("")
1571d43cd965SMauro Carvalho Chehab
1572d43cd965SMauro Carvalho Chehab        if not self.deps.optional:
1573d43cd965SMauro Carvalho Chehab            print("All optional dependencies are met.")
1574d43cd965SMauro Carvalho Chehab
1575d43cd965SMauro Carvalho Chehab        if self.deps.need == 1:
1576d43cd965SMauro Carvalho Chehab            sys.exit("Can't build as 1 mandatory dependency is missing")
1577d43cd965SMauro Carvalho Chehab        elif self.deps.need:
1578d43cd965SMauro Carvalho Chehab            sys.exit(f"Can't build as {self.deps.need} mandatory dependencies are missing")
1579d43cd965SMauro Carvalho Chehab
1580d43cd965SMauro Carvalho Chehab        print("Needed package dependencies are met.")
1581d43cd965SMauro Carvalho Chehab
1582d43cd965SMauro Carvalho ChehabDESCRIPTION = """
1583d43cd965SMauro Carvalho ChehabProcess some flags related to Sphinx installation and documentation build.
1584d43cd965SMauro Carvalho Chehab"""
1585d43cd965SMauro Carvalho Chehab
1586d43cd965SMauro Carvalho Chehab
1587d43cd965SMauro Carvalho Chehabdef main():
1588d43cd965SMauro Carvalho Chehab    """Main function"""
1589d43cd965SMauro Carvalho Chehab    parser = argparse.ArgumentParser(description=DESCRIPTION)
1590d43cd965SMauro Carvalho Chehab
1591d43cd965SMauro Carvalho Chehab    parser.add_argument(
1592d43cd965SMauro Carvalho Chehab        "--no-virtualenv",
1593d43cd965SMauro Carvalho Chehab        action="store_false",
1594d43cd965SMauro Carvalho Chehab        dest="virtualenv",
1595d43cd965SMauro Carvalho Chehab        help="Recommend installing Sphinx instead of using a virtualenv",
1596d43cd965SMauro Carvalho Chehab    )
1597d43cd965SMauro Carvalho Chehab
1598d43cd965SMauro Carvalho Chehab    parser.add_argument(
1599d43cd965SMauro Carvalho Chehab        "--no-pdf",
1600d43cd965SMauro Carvalho Chehab        action="store_false",
1601d43cd965SMauro Carvalho Chehab        dest="pdf",
1602d43cd965SMauro Carvalho Chehab        help="Don't check for dependencies required to build PDF docs",
1603d43cd965SMauro Carvalho Chehab    )
1604d43cd965SMauro Carvalho Chehab
1605d43cd965SMauro Carvalho Chehab    parser.add_argument(
1606d43cd965SMauro Carvalho Chehab        "--version-check",
1607d43cd965SMauro Carvalho Chehab        action="store_true",
1608d43cd965SMauro Carvalho Chehab        dest="version_check",
1609d43cd965SMauro Carvalho Chehab        help="If version is compatible, don't check for missing dependencies",
1610d43cd965SMauro Carvalho Chehab    )
1611d43cd965SMauro Carvalho Chehab
1612d43cd965SMauro Carvalho Chehab    args = parser.parse_args()
1613d43cd965SMauro Carvalho Chehab
1614d43cd965SMauro Carvalho Chehab    checker = SphinxDependencyChecker(args)
1615d43cd965SMauro Carvalho Chehab
1616d43cd965SMauro Carvalho Chehab    checker.check_python()
1617d43cd965SMauro Carvalho Chehab    checker.check_needs()
1618d43cd965SMauro Carvalho Chehab
1619d43cd965SMauro Carvalho Chehab# Call main if not used as module
1620d43cd965SMauro Carvalho Chehabif __name__ == "__main__":
1621d43cd965SMauro Carvalho Chehab    main()
1622