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