History

scripts: sphinx-pre-install: convert is_optional to a class

When is_optional was added in Perl, it was a boolean. With time, it ended becoming a sort of enum, which makes the module harder to maintain. Convert it to a enum-like class and add more options to it.

Mauro Carvalho Chehab committed 1 year ago

Commit 9ecda2e1 parent 8cd25651 file · +130 −62Browse files

Unified · Split

scripts/sphinx-pre-install.py+130−62
@@ -32,6 +32,53 @@ RECOMMENDED_VERSION = parse_version("3.4.3") · 31 unmodified lines
32MIN_PYTHON_VERSION = parse_version("3.7")32MIN_PYTHON_VERSION = parse_version("3.7")
33 33
34 34
35class DepType:
36
37 # Internal types of dependencies.
38 _SYS_TYPE = 0
39 _PHY_TYPE = 1
40 _PDF_TYPE = 2
41
42 # Let's define keys as a tuple with the type and mandatory/optional.
43 # This way, checking for optional or type is easy.
44
45 SYSTEM_MANDATORY = (_SYS_TYPE, True)
46 PYTHON_MANDATORY = (_PHY_TYPE, True)
47 PDF_MANDATORY = (_PDF_TYPE, True)
48
49 # Currently we're not using all optional types, but let's keep all
50 # combinations here, as we may end needing them in the future. Also,
51 # it allows a name() function that handles all possibilities.
52 SYSTEM_OPTIONAL = (_SYS_TYPE, False)
53 PYTHON_OPTIONAL = (_PHY_TYPE, False)
54 PDF_OPTIONAL = (_PDF_TYPE, True)
55
56 @staticmethod
57 def name(dtype):
58 if dtype[0] == DepType._SYS_TYPE:
59 msg = "build"
60 elif dtype[0] == DepType._PHY_TYPE:
61 msg = "Python"
62 else:
63 msg = "PDF"
64
65 if dtype[1]:
66 return f"ERROR: {msg} mandatory deps missing"
67 else:
68 out = f"Warning: {msg} optional deps missing"
69
70 @staticmethod
71 def is_optional(dtype):
72 return not dtype[1]
73
74 @staticmethod
75 def is_pdf(dtype):
76 if (dtype[0] == DepType._PDF_TYPE):
77 return True
78
79 return False
80
81
35class SphinxDependencyChecker:82class SphinxDependencyChecker:
36 # List of required texlive packages on Fedora and OpenSuse83 # List of required texlive packages on Fedora and OpenSuse
37 texlive = {84 texlive = {
@@ -223,56 +270,68 @@ class SphinxDependencyChecker: · 185 unmodified lines
223 # Methods to check if a feature exists270 # Methods to check if a feature exists
224 #271 #
225 272
226 # Note: is_optional has 3 states:
227 # - 0: mandatory
228 # - 1: optional, but nice to have
229 # - 2: LaTeX optional - pdf builds without it, but may have visual impact
230
231 def check_missing(self, progs):273 def check_missing(self, progs):
232 for prog, is_optional in sorted(self.missing.items()):274 run = {}
275
276 for prog, dtype in sorted(self.missing.items()):
233 # At least on some LTS distros like CentOS 7, texlive doesn't277 # At least on some LTS distros like CentOS 7, texlive doesn't
234 # provide all packages we need. When such distros are278 # provide all packages we need. When such distros are
235 # detected, we have to disable PDF output.279 # detected, we have to disable PDF output.
236 #280 #
237 # So, we need to ignore the packages that distros would281 # So, we need to ignore the packages that distros would
238 # need for LaTeX to work282 # need for LaTeX to work
239 if is_optional == 2 and not self.pdf:283 if DepType.is_pdf(dtype) and not self.pdf:
240 self.optional -= 1284 self.optional -= 1
241 continue285 continue
242 286
287 if not dtype in run:
288 run[dtype] = []
289
290 run[dtype].append(prog)
291
292 output_msg = ""
293
294 for dtype in sorted(run.keys()):
295 progs = " ".join(run[dtype])
296
243 if self.verbose_warn_install:297 if self.verbose_warn_install:
244 if is_optional:298 try:
245 print(f'Warning: better to also install "{prog}".')299 name = DepType.name(dtype)
246 else:300 output_msg += f'{name}:\t{progs}\n'
247 print(f'ERROR: please install "{prog}", otherwise, build won\'t work.')301 except KeyError:
302 raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}")
248 303
249 self.install += " " + progs.get(prog, prog)304 self.install += " " + progs
305
306 if output_msg:
307 print(f"\n{output_msg}\n")
250 308
251 self.install = self.install.lstrip()309 self.install = self.install.lstrip()
252 310
253 def add_package(self, package, is_optional):311 def add_package(self, package, dtype):
254 self.missing[package] = is_optional312 is_optional = DepType.is_optional(dtype)
313 self.missing[package] = dtype
255 if is_optional:314 if is_optional:
256 self.optional += 1315 self.optional += 1
257 else:316 else:
258 self.need += 1317 self.need += 1
259 318
260 def check_missing_file(self, files, package, is_optional):319 def check_missing_file(self, files, package, dtype):
261 for f in files:320 for f in files:
262 if os.path.exists(f):321 if os.path.exists(f):
263 return322 return
264 self.add_package(package, is_optional)323 self.add_package(package, dtype)
265 324
266 def check_program(self, prog, is_optional):325 def check_program(self, prog, dtype):
267 found = self.which(prog)326 found = self.which(prog)
268 if found:327 if found:
269 return found328 return found
270 329
271 self.add_package(prog, is_optional)330 self.add_package(prog, dtype)
272 331
273 return None332 return None
274 333
275 def check_perl_module(self, prog, is_optional):334 def check_perl_module(self, prog, dtype):
276 # While testing with lxc download template, one of the335 # While testing with lxc download template, one of the
277 # distros (Oracle) didn't have perl - nor even an option to install336 # distros (Oracle) didn't have perl - nor even an option to install
278 # before installing oraclelinux-release-el9 package.337 # before installing oraclelinux-release-el9 package.
@@ -281,46 +340,52 @@ class SphinxDependencyChecker: · 2 unmodified lines
281 # add it as a mandatory package, as some parts of the doc builder340 # add it as a mandatory package, as some parts of the doc builder
282 # needs it.341 # needs it.
283 if not self.which("perl"):342 if not self.which("perl"):
284 self.add_package("perl", 0)343 self.add_package("perl", DepType.SYSTEM_MANDATORY)
285 self.add_package(prog, is_optional)344 self.add_package(prog, dtype)
286 return345 return
287 346
288 try:347 try:
289 self.run(["perl", f"-M{prog}", "-e", "1"], check=True)348 self.run(["perl", f"-M{prog}", "-e", "1"], check=True)
290 except subprocess.CalledProcessError:349 except subprocess.CalledProcessError:
291 self.add_package(prog, is_optional)350 self.add_package(prog, dtype)
292 351
293 def check_python_module(self, module, is_optional):352 def check_python_module(self, module, is_optional=False):
294 # FIXME: is it needed at the Python version? Maybe due to venv?353 if is_optional:
295 if not self.python_cmd:354 dtype = DepType.PYTHON_OPTIONAL
296 return355 else:
356 dtype = DepType.PYTHON_MANDATORY
297 357
298 try:358 try:
299 self.run([self.python_cmd, "-c", f"import {module}"], check=True)359 self.run([self.python_cmd, "-c", f"import {module}"], check=True)
300 except subprocess.CalledProcessError:360 except subprocess.CalledProcessError:
301 self.add_package(module, is_optional)361 self.add_package(module, dtype)
302 362
303 def check_rpm_missing(self, pkgs, is_optional):363 def check_rpm_missing(self, pkgs, dtype):
304 for prog in pkgs:364 for prog in pkgs:
305 try:365 try:
306 self.run(["rpm", "-q", prog], check=True)366 self.run(["rpm", "-q", prog], check=True)
307 except subprocess.CalledProcessError:367 except subprocess.CalledProcessError:
308 self.add_package(prog, is_optional)368 self.add_package(prog, dtype)
309 369
310 def check_pacman_missing(self, pkgs, is_optional):370 def check_pacman_missing(self, pkgs, dtype):
311 for prog in pkgs:371 for prog in pkgs:
312 try:372 try:
313 self.run(["pacman", "-Q", prog], check=True)373 self.run(["pacman", "-Q", prog], check=True)
314 except subprocess.CalledProcessError:374 except subprocess.CalledProcessError:
315 self.add_package(prog, is_optional)375 self.add_package(prog, dtype)
376
377 def check_missing_tex(self, is_optional=False):
378 if is_optional:
379 dtype = DepType.PDF_OPTIONAL
380 else:
381 dtype = DepType.PDF_MANDATORY
316 382
317 def check_missing_tex(self, is_optional):
318 kpsewhich = self.which("kpsewhich")383 kpsewhich = self.which("kpsewhich")
319 for prog, package in self.texlive.items():384 for prog, package in self.texlive.items():
320 385
321 # If kpsewhich is not there, just add it to deps386 # If kpsewhich is not there, just add it to deps
322 if not kpsewhich:387 if not kpsewhich:
323 self.add_package(package, is_optional)388 self.add_package(package, dtype)
324 continue389 continue
325 390
326 # Check if the package is needed391 # Check if the package is needed
@@ -331,11 +396,11 @@ class SphinxDependencyChecker: · 4 unmodified lines
331 396
332 # Didn't find. Add it397 # Didn't find. Add it
333 if not result.stdout.strip():398 if not result.stdout.strip():
334 self.add_package(package, is_optional)399 self.add_package(package, dtype)
335 400
336 except subprocess.CalledProcessError:401 except subprocess.CalledProcessError:
337 # kpsewhich returned an error. Add it, just in case402 # kpsewhich returned an error. Add it, just in case
338 self.add_package(package, is_optional)403 self.add_package(package, dtype)
339 404
340 def get_sphinx_fname(self):405 def get_sphinx_fname(self):
341 if "SPHINXBUILD" in os.environ:406 if "SPHINXBUILD" in os.environ:
@@ -446,9 +511,9 @@ class SphinxDependencyChecker: · 104 unmodified lines
446 }511 }
447 512
448 for package, files in pdf_pkgs.items():513 for package, files in pdf_pkgs.items():
449 self.check_missing_file(files, package, 2)514 self.check_missing_file(files, package, DepType.PDF_MANDATORY)
450 515
451 self.check_program("dvipng", 2)516 self.check_program("dvipng", DepType.PDF_MANDATORY)
452 517
453 self.check_missing(progs)518 self.check_missing(progs)
454 519
@@ -518,7 +583,7 @@ class SphinxDependencyChecker: · 63 unmodified lines
518 # RHEL 8 uses Python 3.6, which is not compatible with583 # RHEL 8 uses Python 3.6, which is not compatible with
519 # the build system anymore. Suggest Python 3.11584 # the build system anymore. Suggest Python 3.11
520 if rel == 8:585 if rel == 8:
521 self.add_package("python39", 0)586 self.add_package("python39", DepType.SYSTEM_MANDATORY)
522 self.recommend_python = True587 self.recommend_python = True
523 588
524 if self.first_hint:589 if self.first_hint:
@@ -540,13 +605,13 @@ class SphinxDependencyChecker: · 15 unmodified lines
540 "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc",605 "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc",
541 ]606 ]
542 607
543 self.check_missing_file(pdf_pkgs, noto_sans_redhat, 2)608 self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepType.PDF_MANDATORY)
544 609
545 if not old:610 if not old:
546 self.check_rpm_missing(fedora26_opt_pkgs, 2)611 self.check_rpm_missing(fedora26_opt_pkgs, DepType.PDF_MANDATORY)
547 self.check_rpm_missing(fedora_tex_pkgs, 2)612 self.check_rpm_missing(fedora_tex_pkgs, DepType.PDF_MANDATORY)
548 613
549 self.check_missing_tex(2)614 self.check_missing_tex()
550 615
551 self.check_missing(progs)616 self.check_missing(progs)
552 617
@@ -601,7 +666,7 @@ class SphinxDependencyChecker: · 48 unmodified lines
601 if rel == 15:666 if rel == 15:
602 if not self.which(self.python_cmd):667 if not self.which(self.python_cmd):
603 self.recommend_python = True668 self.recommend_python = True
604 self.add_package(self.python_cmd, 0)669 self.add_package(self.python_cmd, DepType.SYSTEM_MANDATORY)
605 670
606 progs.update({671 progs.update({
607 "python-sphinx": "python311-Sphinx",672 "python-sphinx": "python311-Sphinx",
@@ -623,9 +688,9 @@ class SphinxDependencyChecker: · 15 unmodified lines
623 # "Noto Sans CJK SC" on openSUSE688 # "Noto Sans CJK SC" on openSUSE
624 689
625 if self.pdf:690 if self.pdf:
626 self.check_rpm_missing(suse_tex_pkgs, 2)691 self.check_rpm_missing(suse_tex_pkgs, DepType.PDF_MANDATORY)
627 if self.pdf:692 if self.pdf:
628 self.check_missing_tex(2)693 self.check_missing_tex()
629 self.check_missing(progs)694 self.check_missing(progs)
630 695
631 if not self.need and not self.optional:696 if not self.need and not self.optional:
@@ -672,8 +737,8 @@ class SphinxDependencyChecker: · 40 unmodified lines
672 "/usr/share/fonts/TTF/NotoSans-Regular.ttf",737 "/usr/share/fonts/TTF/NotoSans-Regular.ttf",
673 ]738 ]
674 739
675 self.check_missing_file(pdf_pkgs, noto_sans, 2)740 self.check_missing_file(pdf_pkgs, noto_sans, DepType.PDF_MANDATORY)
676 self.check_rpm_missing(tex_pkgs, 2)741 self.check_rpm_missing(tex_pkgs, DepType.PDF_MANDATORY)
677 742
678 self.check_missing(progs)743 self.check_missing(progs)
679 744
@@ -701,7 +766,7 @@ class SphinxDependencyChecker: · 21 unmodified lines
701 ]766 ]
702 767
703 if self.pdf:768 if self.pdf:
704 self.check_pacman_missing(archlinux_tex_pkgs, 2)769 self.check_pacman_missing(archlinux_tex_pkgs, DepType.PDF_MANDATORY)
705 770
706 self.check_missing_file(771 self.check_missing_file(
707 ["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"],772 ["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"],
@@ -739,7 +804,7 @@ class SphinxDependencyChecker: · 31 unmodified lines
739 ],804 ],
740 }805 }
741 for package, files in pdf_pkgs.items():806 for package, files in pdf_pkgs.items():
742 self.check_missing_file(files, package, 2)807 self.check_missing_file(files, package, DepType.PDF_MANDATORY)
743 808
744 self.check_missing(progs)809 self.check_missing(progs)
745 810
@@ -878,7 +943,7 @@ class SphinxDependencyChecker: · 132 unmodified lines
878 #943 #
879 progs = {"sphinx-build": "sphinx"}944 progs = {"sphinx-build": "sphinx"}
880 if self.pdf:945 if self.pdf:
881 self.check_missing_tex(2)946 self.check_missing_tex()
882 947
883 self.check_missing(progs)948 self.check_missing(progs)
884 949
@@ -990,7 +1055,7 @@ class SphinxDependencyChecker: · 105 unmodified lines
990 old_verbose = self.verbose_warn_install1055 old_verbose = self.verbose_warn_install
991 self.verbose_warn_install = 01056 self.verbose_warn_install = 0
992 1057
993 self.add_package("python-sphinx", 0)1058 self.add_package("python-sphinx", DepType.PYTHON_MANDATORY)
994 1059
995 self.check_distros()1060 self.check_distros()
996 1061
@@ -1010,6 +1075,7 @@ class SphinxDependencyChecker: · 13 unmodified lines
1010 "Please upgrade it and re-run.\n")1075 "Please upgrade it and re-run.\n")
1011 return1076 return
1012 1077
1078
1013 # Version is OK. Nothing to do.1079 # Version is OK. Nothing to do.
1014 if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION:1080 if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION:
1015 return1081 return
@@ -1128,21 +1194,23 @@ class SphinxDependencyChecker: · 112 unmodified lines
1128 1194
1129 else:1195 else:
1130 virtualenv_cmd = f"{self.python_cmd} -m venv"1196 virtualenv_cmd = f"{self.python_cmd} -m venv"
1131 self.check_python_module("ensurepip", 0)1197 self.check_python_module("ensurepip")
1132 1198
1133 # Check for needed programs/tools1199 # Check for needed programs/tools
1134 self.check_perl_module("Pod::Usage", 0)1200 self.check_perl_module("Pod::Usage", DepType.SYSTEM_MANDATORY)
1135 self.check_python_module("yaml", 0)1201
1136 self.check_program("make", 0)1202 self.check_program("make", DepType.SYSTEM_MANDATORY)
1137 self.check_program("gcc", 0)1203 self.check_program("gcc", DepType.SYSTEM_MANDATORY)
1138 self.check_program("dot", 1)1204
1139 self.check_program("convert", 1)1205 self.check_program("dot", DepType.SYSTEM_OPTIONAL)
1206 self.check_program("convert", DepType.SYSTEM_OPTIONAL)
1207
1208 self.check_python_module("yaml")
1140 1209
1141 if self.pdf:1210 if self.pdf:
1142 # Extra PDF files - should use 2 for LaTeX is_optional1211 self.check_program("xelatex", DepType.PDF_MANDATORY)
1143 self.check_program("xelatex", 2)1212 self.check_program("rsvg-convert", DepType.PDF_MANDATORY)
1144 self.check_program("rsvg-convert", 2)1213 self.check_program("latexmk", DepType.PDF_MANDATORY)
1145 self.check_program("latexmk", 2)
1146 1214
1147 # Do distro-specific checks and output distro-install commands1215 # Do distro-specific checks and output distro-install commands
1148 self.check_distros()1216 self.check_distros()