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