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