scripts: sphinx-pre-install: change the warning for version < 2.4.4
[linux-2.6-microblaze.git] / scripts / sphinx-pre-install
1 #!/usr/bin/perl
2 # SPDX-License-Identifier: GPL-2.0-or-later
3 use strict;
4
5 # Copyright (c) 2017-2020 Mauro Carvalho Chehab <mchehab@kernel.org>
6 #
7
8 my $prefix = "./";
9 $prefix = "$ENV{'srctree'}/" if ($ENV{'srctree'});
10
11 my $conf = $prefix . "Documentation/conf.py";
12 my $requirement_file = $prefix . "Documentation/sphinx/requirements.txt";
13 my $virtenv_prefix = "sphinx_";
14
15 #
16 # Static vars
17 #
18
19 my %missing;
20 my $system_release;
21 my $need = 0;
22 my $optional = 0;
23 my $need_symlink = 0;
24 my $need_sphinx = 0;
25 my $need_venv = 0;
26 my $need_virtualenv = 0;
27 my $rec_sphinx_upgrade = 0;
28 my $install = "";
29 my $virtenv_dir = "";
30 my $python_cmd = "";
31 my $min_version;
32 my $rec_version = "1.7.9";      # PDF won't build here
33 my $min_pdf_version = "2.4.4";  # Min version where pdf builds
34
35 #
36 # Command line arguments
37 #
38
39 my $pdf = 1;
40 my $virtualenv = 1;
41 my $version_check = 0;
42
43 #
44 # List of required texlive packages on Fedora and OpenSuse
45 #
46
47 my %texlive = (
48         'amsfonts.sty'       => 'texlive-amsfonts',
49         'amsmath.sty'        => 'texlive-amsmath',
50         'amssymb.sty'        => 'texlive-amsfonts',
51         'amsthm.sty'         => 'texlive-amscls',
52         'anyfontsize.sty'    => 'texlive-anyfontsize',
53         'atbegshi.sty'       => 'texlive-oberdiek',
54         'bm.sty'             => 'texlive-tools',
55         'capt-of.sty'        => 'texlive-capt-of',
56         'cmap.sty'           => 'texlive-cmap',
57         'ecrm1000.tfm'       => 'texlive-ec',
58         'eqparbox.sty'       => 'texlive-eqparbox',
59         'eu1enc.def'         => 'texlive-euenc',
60         'fancybox.sty'       => 'texlive-fancybox',
61         'fancyvrb.sty'       => 'texlive-fancyvrb',
62         'float.sty'          => 'texlive-float',
63         'fncychap.sty'       => 'texlive-fncychap',
64         'footnote.sty'       => 'texlive-mdwtools',
65         'framed.sty'         => 'texlive-framed',
66         'luatex85.sty'       => 'texlive-luatex85',
67         'multirow.sty'       => 'texlive-multirow',
68         'needspace.sty'      => 'texlive-needspace',
69         'palatino.sty'       => 'texlive-psnfss',
70         'parskip.sty'        => 'texlive-parskip',
71         'polyglossia.sty'    => 'texlive-polyglossia',
72         'tabulary.sty'       => 'texlive-tabulary',
73         'threeparttable.sty' => 'texlive-threeparttable',
74         'titlesec.sty'       => 'texlive-titlesec',
75         'ucs.sty'            => 'texlive-ucs',
76         'upquote.sty'        => 'texlive-upquote',
77         'wrapfig.sty'        => 'texlive-wrapfig',
78 );
79
80 #
81 # Subroutines that checks if a feature exists
82 #
83
84 sub check_missing(%)
85 {
86         my %map = %{$_[0]};
87
88         foreach my $prog (sort keys %missing) {
89                 my $is_optional = $missing{$prog};
90
91                 # At least on some LTS distros like CentOS 7, texlive doesn't
92                 # provide all packages we need. When such distros are
93                 # detected, we have to disable PDF output.
94                 #
95                 # So, we need to ignore the packages that distros would
96                 # need for LaTeX to work
97                 if ($is_optional == 2 && !$pdf) {
98                         $optional--;
99                         next;
100                 }
101
102                 if ($is_optional) {
103                         print "Warning: better to also install \"$prog\".\n";
104                 } else {
105                         print "ERROR: please install \"$prog\", otherwise, build won't work.\n";
106                 }
107                 if (defined($map{$prog})) {
108                         $install .= " " . $map{$prog};
109                 } else {
110                         $install .= " " . $prog;
111                 }
112         }
113
114         $install =~ s/^\s//;
115 }
116
117 sub add_package($$)
118 {
119         my $package = shift;
120         my $is_optional = shift;
121
122         $missing{$package} = $is_optional;
123         if ($is_optional) {
124                 $optional++;
125         } else {
126                 $need++;
127         }
128 }
129
130 sub check_missing_file($$$)
131 {
132         my $files = shift;
133         my $package = shift;
134         my $is_optional = shift;
135
136         for (@$files) {
137                 return if(-e $_);
138         }
139
140         add_package($package, $is_optional);
141 }
142
143 sub findprog($)
144 {
145         foreach(split(/:/, $ENV{PATH})) {
146                 return "$_/$_[0]" if(-x "$_/$_[0]");
147         }
148 }
149
150 sub check_program($$)
151 {
152         my $prog = shift;
153         my $is_optional = shift;
154
155         return $prog if findprog($prog);
156
157         add_package($prog, $is_optional);
158 }
159
160 sub check_perl_module($$)
161 {
162         my $prog = shift;
163         my $is_optional = shift;
164
165         my $err = system("perl -M$prog -e 1 2>/dev/null /dev/null");
166         return if ($err == 0);
167
168         add_package($prog, $is_optional);
169 }
170
171 sub check_python_module($$)
172 {
173         my $prog = shift;
174         my $is_optional = shift;
175
176         return if (!$python_cmd);
177
178         my $err = system("$python_cmd -c 'import $prog' 2>/dev/null /dev/null");
179         return if ($err == 0);
180
181         add_package($prog, $is_optional);
182 }
183
184 sub check_rpm_missing($$)
185 {
186         my @pkgs = @{$_[0]};
187         my $is_optional = $_[1];
188
189         foreach my $prog(@pkgs) {
190                 my $err = system("rpm -q '$prog' 2>/dev/null >/dev/null");
191                 add_package($prog, $is_optional) if ($err);
192         }
193 }
194
195 sub check_pacman_missing($$)
196 {
197         my @pkgs = @{$_[0]};
198         my $is_optional = $_[1];
199
200         foreach my $prog(@pkgs) {
201                 my $err = system("pacman -Q '$prog' 2>/dev/null >/dev/null");
202                 add_package($prog, $is_optional) if ($err);
203         }
204 }
205
206 sub check_missing_tex($)
207 {
208         my $is_optional = shift;
209         my $kpsewhich = findprog("kpsewhich");
210
211         foreach my $prog(keys %texlive) {
212                 my $package = $texlive{$prog};
213                 if (!$kpsewhich) {
214                         add_package($package, $is_optional);
215                         next;
216                 }
217                 my $file = qx($kpsewhich $prog);
218                 add_package($package, $is_optional) if ($file =~ /^\s*$/);
219         }
220 }
221
222 sub get_sphinx_fname()
223 {
224         my $fname = "sphinx-build";
225         return $fname if findprog($fname);
226
227         $fname = "sphinx-build-3";
228         if (findprog($fname)) {
229                 $need_symlink = 1;
230                 return $fname;
231         }
232
233         return "";
234 }
235
236 sub get_sphinx_version($)
237 {
238         my $cmd = shift;
239         my $ver;
240
241         open IN, "$cmd --version 2>&1 |";
242         while (<IN>) {
243                 if (m/^\s*sphinx-build\s+([\d\.]+)(\+\/[\da-f]+)?$/) {
244                         $ver=$1;
245                         last;
246                 }
247                 # Sphinx 1.2.x uses a different format
248                 if (m/^\s*Sphinx.*\s+([\d\.]+)$/) {
249                         $ver=$1;
250                         last;
251                 }
252         }
253         close IN;
254         return $ver;
255 }
256
257 sub check_sphinx()
258 {
259         my $default_version;
260         my $cur_version;
261
262         open IN, $conf or die "Can't open $conf";
263         while (<IN>) {
264                 if (m/^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]/) {
265                         $min_version=$1;
266                         last;
267                 }
268         }
269         close IN;
270
271         die "Can't get needs_sphinx version from $conf" if (!$min_version);
272
273         open IN, $requirement_file or die "Can't open $requirement_file";
274         while (<IN>) {
275                 if (m/^\s*Sphinx\s*==\s*([\d\.]+)$/) {
276                         $default_version=$1;
277                         last;
278                 }
279         }
280         close IN;
281
282         die "Can't get default sphinx version from $requirement_file" if (!$default_version);
283
284         $virtenv_dir = $virtenv_prefix . $default_version;
285
286         my $sphinx = get_sphinx_fname();
287         if ($sphinx eq "") {
288                 $need_sphinx = 1;
289                 return;
290         }
291
292         $cur_version = get_sphinx_version($sphinx);
293         die ("$sphinx returned an error") if (!$cur_version);
294
295         die "$sphinx didn't return its version" if (!$cur_version);
296
297         if ($cur_version lt $min_version) {
298                 printf "ERROR: Sphinx version is %s. It should be >= %s (recommended >= %s)\n",
299                        $cur_version, $min_version, $default_version;
300                 $need_sphinx = 1;
301                 return;
302         }
303
304         if ($cur_version lt $rec_version) {
305                 printf "Sphinx version %s\n", $cur_version;
306                 print "Warning: It is recommended at least Sphinx version $rec_version.\n";
307                 print "         If you want pdf, you need at least $min_pdf_version.\n";
308                 $rec_sphinx_upgrade = 1;
309                 return;
310         }
311         if ($cur_version lt $min_pdf_version) {
312                 printf "Sphinx version %s\n", $cur_version;
313                 print "Note: It is recommended at least Sphinx version $min_pdf_version if you need PDF support.\n";
314                 $rec_sphinx_upgrade = 1;
315                 return;
316         }
317
318         # On version check mode, just assume Sphinx has all mandatory deps
319         exit (0) if ($version_check);
320 }
321
322 #
323 # Ancillary subroutines
324 #
325
326 sub catcheck($)
327 {
328   my $res = "";
329   $res = qx(cat $_[0]) if (-r $_[0]);
330   return $res;
331 }
332
333 sub which($)
334 {
335         my $file = shift;
336         my @path = split ":", $ENV{PATH};
337
338         foreach my $dir(@path) {
339                 my $name = $dir.'/'.$file;
340                 return $name if (-x $name );
341         }
342         return undef;
343 }
344
345 #
346 # Subroutines that check distro-specific hints
347 #
348
349 sub give_debian_hints()
350 {
351         my %map = (
352                 "python-sphinx"         => "python3-sphinx",
353                 "sphinx_rtd_theme"      => "python3-sphinx-rtd-theme",
354                 "ensurepip"             => "python3-venv",
355                 "virtualenv"            => "virtualenv",
356                 "dot"                   => "graphviz",
357                 "convert"               => "imagemagick",
358                 "Pod::Usage"            => "perl-modules",
359                 "xelatex"               => "texlive-xetex",
360                 "rsvg-convert"          => "librsvg2-bin",
361         );
362
363         if ($pdf) {
364                 check_missing_file(["/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"],
365                                    "fonts-dejavu", 2);
366
367                 check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
368                                     "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
369                                     "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"],
370                                    "fonts-noto-cjk", 2);
371         }
372
373         check_program("dvipng", 2) if ($pdf);
374         check_missing(\%map);
375
376         return if (!$need && !$optional);
377         printf("You should run:\n\n\tsudo apt-get install $install\n");
378 }
379
380 sub give_redhat_hints()
381 {
382         my %map = (
383                 "python-sphinx"         => "python3-sphinx",
384                 "sphinx_rtd_theme"      => "python3-sphinx_rtd_theme",
385                 "virtualenv"            => "python3-virtualenv",
386                 "dot"                   => "graphviz",
387                 "convert"               => "ImageMagick",
388                 "Pod::Usage"            => "perl-Pod-Usage",
389                 "xelatex"               => "texlive-xetex-bin",
390                 "rsvg-convert"          => "librsvg2-tools",
391         );
392
393         my @fedora26_opt_pkgs = (
394                 "graphviz-gd",          # Fedora 26: needed for PDF support
395         );
396
397         my @fedora_tex_pkgs = (
398                 "texlive-collection-fontsrecommended",
399                 "texlive-collection-latex",
400                 "texlive-xecjk",
401                 "dejavu-sans-fonts",
402                 "dejavu-serif-fonts",
403                 "dejavu-sans-mono-fonts",
404         );
405
406         #
407         # Checks valid for RHEL/CentOS version 7.x.
408         #
409         my $old = 0;
410         my $rel;
411         $rel = $1 if ($system_release =~ /release\s+(\d+)/);
412
413         if (!($system_release =~ /Fedora/)) {
414                 $map{"virtualenv"} = "python-virtualenv";
415
416                 if ($rel && $rel < 8) {
417                         $old = 1;
418                         $pdf = 0;
419
420                         printf("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output\n");
421                         printf("If you want to build PDF, please read:\n");
422                         printf("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/\n");
423                 }
424         } else {
425                 if ($rel && $rel < 26) {
426                         $old = 1;
427                 }
428         }
429         if (!$rel) {
430                 printf("Couldn't identify release number\n");
431                 $old = 1;
432                 $pdf = 0;
433         }
434
435         if ($pdf) {
436                 check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc"],
437                                    "google-noto-sans-cjk-ttc-fonts", 2);
438         }
439
440         check_rpm_missing(\@fedora26_opt_pkgs, 2) if ($pdf && !$old);
441         check_rpm_missing(\@fedora_tex_pkgs, 2) if ($pdf);
442         check_missing_tex(2) if ($pdf);
443         check_missing(\%map);
444
445         return if (!$need && !$optional);
446
447         if (!$old) {
448                 # dnf, for Fedora 18+
449                 printf("You should run:\n\n\tsudo dnf install -y $install\n");
450         } else {
451                 # yum, for RHEL (and clones) or Fedora version < 18
452                 printf("You should run:\n\n\tsudo yum install -y $install\n");
453         }
454 }
455
456 sub give_opensuse_hints()
457 {
458         my %map = (
459                 "python-sphinx"         => "python3-sphinx",
460                 "sphinx_rtd_theme"      => "python3-sphinx_rtd_theme",
461                 "virtualenv"            => "python3-virtualenv",
462                 "dot"                   => "graphviz",
463                 "convert"               => "ImageMagick",
464                 "Pod::Usage"            => "perl-Pod-Usage",
465                 "xelatex"               => "texlive-xetex-bin",
466         );
467
468         # On Tumbleweed, this package is also named rsvg-convert
469         $map{"rsvg-convert"} = "rsvg-view" if (!($system_release =~ /Tumbleweed/));
470
471         my @suse_tex_pkgs = (
472                 "texlive-babel-english",
473                 "texlive-caption",
474                 "texlive-colortbl",
475                 "texlive-courier",
476                 "texlive-dvips",
477                 "texlive-helvetic",
478                 "texlive-makeindex",
479                 "texlive-metafont",
480                 "texlive-metapost",
481                 "texlive-palatino",
482                 "texlive-preview",
483                 "texlive-times",
484                 "texlive-zapfchan",
485                 "texlive-zapfding",
486         );
487
488         $map{"latexmk"} = "texlive-latexmk-bin";
489
490         # FIXME: add support for installing CJK fonts
491         #
492         # I tried hard, but was unable to find a way to install
493         # "Noto Sans CJK SC" on openSUSE
494
495         check_rpm_missing(\@suse_tex_pkgs, 2) if ($pdf);
496         check_missing_tex(2) if ($pdf);
497         check_missing(\%map);
498
499         return if (!$need && !$optional);
500         printf("You should run:\n\n\tsudo zypper install --no-recommends $install\n");
501 }
502
503 sub give_mageia_hints()
504 {
505         my %map = (
506                 "python-sphinx"         => "python3-sphinx",
507                 "sphinx_rtd_theme"      => "python3-sphinx_rtd_theme",
508                 "virtualenv"            => "python3-virtualenv",
509                 "dot"                   => "graphviz",
510                 "convert"               => "ImageMagick",
511                 "Pod::Usage"            => "perl-Pod-Usage",
512                 "xelatex"               => "texlive",
513                 "rsvg-convert"          => "librsvg2",
514         );
515
516         my @tex_pkgs = (
517                 "texlive-fontsextra",
518         );
519
520         $map{"latexmk"} = "texlive-collection-basic";
521
522         my $packager_cmd;
523         my $noto_sans;
524         if ($system_release =~ /OpenMandriva/) {
525                 $packager_cmd = "dnf install";
526                 $noto_sans = "noto-sans-cjk-fonts";
527                 @tex_pkgs = ( "texlive-collection-fontsextra" );
528         } else {
529                 $packager_cmd = "urpmi";
530                 $noto_sans = "google-noto-sans-cjk-ttc-fonts";
531         }
532
533
534         if ($pdf) {
535                 check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
536                                     "/usr/share/fonts/TTF/NotoSans-Regular.ttf"],
537                                    $noto_sans, 2);
538         }
539
540         check_rpm_missing(\@tex_pkgs, 2) if ($pdf);
541         check_missing(\%map);
542
543         return if (!$need && !$optional);
544         printf("You should run:\n\n\tsudo $packager_cmd $install\n");
545 }
546
547 sub give_arch_linux_hints()
548 {
549         my %map = (
550                 "sphinx_rtd_theme"      => "python-sphinx_rtd_theme",
551                 "virtualenv"            => "python-virtualenv",
552                 "dot"                   => "graphviz",
553                 "convert"               => "imagemagick",
554                 "xelatex"               => "texlive-bin",
555                 "latexmk"               => "texlive-core",
556                 "rsvg-convert"          => "extra/librsvg",
557         );
558
559         my @archlinux_tex_pkgs = (
560                 "texlive-core",
561                 "texlive-latexextra",
562                 "ttf-dejavu",
563         );
564         check_pacman_missing(\@archlinux_tex_pkgs, 2) if ($pdf);
565
566         if ($pdf) {
567                 check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"],
568                                    "noto-fonts-cjk", 2);
569         }
570
571         check_missing(\%map);
572
573         return if (!$need && !$optional);
574         printf("You should run:\n\n\tsudo pacman -S $install\n");
575 }
576
577 sub give_gentoo_hints()
578 {
579         my %map = (
580                 "sphinx_rtd_theme"      => "dev-python/sphinx_rtd_theme",
581                 "virtualenv"            => "dev-python/virtualenv",
582                 "dot"                   => "media-gfx/graphviz",
583                 "convert"               => "media-gfx/imagemagick",
584                 "xelatex"               => "dev-texlive/texlive-xetex media-fonts/dejavu",
585                 "rsvg-convert"          => "gnome-base/librsvg",
586         );
587
588         check_missing_file(["/usr/share/fonts/dejavu/DejaVuSans.ttf"],
589                            "media-fonts/dejavu", 2) if ($pdf);
590
591         if ($pdf) {
592                 check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf",
593                                     "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc"],
594                                    "media-fonts/noto-cjk", 2);
595         }
596
597         check_missing(\%map);
598
599         return if (!$need && !$optional);
600
601         printf("You should run:\n\n");
602
603         my $imagemagick = "media-gfx/imagemagick svg png";
604         my $cairo = "media-gfx/graphviz cairo pdf";
605         my $portage_imagemagick = "/etc/portage/package.use/imagemagick";
606         my $portage_cairo = "/etc/portage/package.use/graphviz";
607
608         if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") {
609                 printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n")
610         }
611         if (qx(grep graphviz $portage_cairo 2>/dev/null) eq  "") {
612                 printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n");
613         }
614
615         printf("\tsudo emerge --ask $install\n");
616
617 }
618
619 sub check_distros()
620 {
621         # Distro-specific hints
622         if ($system_release =~ /Red Hat Enterprise Linux/) {
623                 give_redhat_hints;
624                 return;
625         }
626         if ($system_release =~ /CentOS/) {
627                 give_redhat_hints;
628                 return;
629         }
630         if ($system_release =~ /Scientific Linux/) {
631                 give_redhat_hints;
632                 return;
633         }
634         if ($system_release =~ /Oracle Linux Server/) {
635                 give_redhat_hints;
636                 return;
637         }
638         if ($system_release =~ /Fedora/) {
639                 give_redhat_hints;
640                 return;
641         }
642         if ($system_release =~ /Ubuntu/) {
643                 give_debian_hints;
644                 return;
645         }
646         if ($system_release =~ /Debian/) {
647                 give_debian_hints;
648                 return;
649         }
650         if ($system_release =~ /openSUSE/) {
651                 give_opensuse_hints;
652                 return;
653         }
654         if ($system_release =~ /Mageia/) {
655                 give_mageia_hints;
656                 return;
657         }
658         if ($system_release =~ /OpenMandriva/) {
659                 give_mageia_hints;
660                 return;
661         }
662         if ($system_release =~ /Arch Linux/) {
663                 give_arch_linux_hints;
664                 return;
665         }
666         if ($system_release =~ /Gentoo/) {
667                 give_gentoo_hints;
668                 return;
669         }
670
671         #
672         # Fall-back to generic hint code for other distros
673         # That's far from ideal, specially for LaTeX dependencies.
674         #
675         my %map = (
676                 "sphinx-build" => "sphinx"
677         );
678         check_missing_tex(2) if ($pdf);
679         check_missing(\%map);
680         print "I don't know distro $system_release.\n";
681         print "So, I can't provide you a hint with the install procedure.\n";
682         print "There are likely missing dependencies.\n";
683 }
684
685 #
686 # Common dependencies
687 #
688
689 sub deactivate_help()
690 {
691         printf "\nIf you want to exit the virtualenv, you can use:\n";
692         printf "\tdeactivate\n";
693 }
694
695 sub check_needs()
696 {
697         # Check if Sphinx is already accessible from current environment
698         check_sphinx();
699
700         if ($system_release) {
701                 print "Detected OS: $system_release.\n\n";
702         } else {
703                 print "Unknown OS\n\n";
704         }
705
706         print "To upgrade Sphinx, use:\n\n" if ($rec_sphinx_upgrade);
707
708         # Check python command line, trying first python3
709         $python_cmd = findprog("python3");
710         $python_cmd = check_program("python", 0) if (!$python_cmd);
711
712         # Check the type of virtual env, depending on Python version
713         if ($python_cmd) {
714                 if ($virtualenv) {
715                         my $tmp = qx($python_cmd --version 2>&1);
716                         if ($tmp =~ m/(\d+\.)(\d+\.)/) {
717                                 if ($1 >= 3 && $2 >= 3) {
718                                         $need_venv = 1;         # python 3.3 or upper
719                                 } else {
720                                         $need_virtualenv = 1;
721                                 }
722                                 if ($1 < 3) {
723                                         # Complain if it finds python2 (or worse)
724                                         printf "Warning: python$1 support is deprecated. Use it with caution!\n";
725                                 }
726                         } else {
727                                 die "Warning: couldn't identify $python_cmd version!";
728                         }
729                 } else {
730                         add_package("python-sphinx", 0);
731                 }
732         }
733
734         # Set virtualenv command line, if python < 3.3
735         my $virtualenv_cmd;
736         if ($need_virtualenv) {
737                 $virtualenv_cmd = findprog("virtualenv-3");
738                 $virtualenv_cmd = findprog("virtualenv-3.5") if (!$virtualenv_cmd);
739                 if (!$virtualenv_cmd) {
740                         check_program("virtualenv", 0);
741                         $virtualenv_cmd = "virtualenv";
742                 }
743         }
744
745         # Check for needed programs/tools
746         check_perl_module("Pod::Usage", 0);
747         check_program("make", 0);
748         check_program("gcc", 0);
749         check_python_module("sphinx_rtd_theme", 1) if (!$virtualenv);
750         check_program("dot", 1);
751         check_program("convert", 1);
752
753         # Extra PDF files - should use 2 for is_optional
754         check_program("xelatex", 2) if ($pdf);
755         check_program("rsvg-convert", 2) if ($pdf);
756         check_program("latexmk", 2) if ($pdf);
757
758         if ($need_sphinx || $rec_sphinx_upgrade) {
759                 check_python_module("ensurepip", 0) if ($need_venv);
760         }
761
762         # Do distro-specific checks and output distro-install commands
763         check_distros();
764
765         if (!$python_cmd) {
766                 if ($need == 1) {
767                         die "Can't build as $need mandatory dependency is missing";
768                 } elsif ($need) {
769                         die "Can't build as $need mandatory dependencies are missing";
770                 }
771         }
772
773         # Check if sphinx-build is called sphinx-build-3
774         if ($need_symlink) {
775                 printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n",
776                        which("sphinx-build-3");
777         }
778
779         # NOTE: if the system has a too old Sphinx version installed,
780         # it will recommend installing a newer version using virtualenv
781
782         if ($need_sphinx || $rec_sphinx_upgrade) {
783                 my $min_activate = "$ENV{'PWD'}/${virtenv_prefix}${min_version}/bin/activate";
784                 my @activates = glob "$ENV{'PWD'}/${virtenv_prefix}*/bin/activate";
785
786                 @activates = sort {$b cmp $a} @activates;
787                 my ($activate, $ver);
788                 foreach my $f (@activates) {
789                         $activate = $f;
790                         next if ($activate lt $min_activate);
791
792                         my $sphinx_cmd = $activate;
793                         $sphinx_cmd =~ s/activate/sphinx-build/;
794                         next if (! -f $sphinx_cmd);
795
796                         $ver = get_sphinx_version($sphinx_cmd);
797                         last if ($ver ge $min_version);
798                 }
799                 if ($need_sphinx && ($activate ne "")) {
800                         printf "\nNeed to activate Sphinx (version $ver) on virtualenv with:\n";
801                         printf "\t. $activate\n";
802                         deactivate_help();
803                         exit (1);
804                 } else {
805                         my $rec_activate = "$virtenv_dir/bin/activate";
806
807                         if ($need_venv) {
808                                 printf "\t$python_cmd -m venv $virtenv_dir\n";
809                         } else {
810                                 printf "\t$virtualenv_cmd $virtenv_dir\n";
811                         }
812                         printf "\t. $rec_activate\n";
813                         printf "\tpip install -r $requirement_file\n";
814                         deactivate_help();
815
816                         $need++ if (!$rec_sphinx_upgrade);
817                 }
818         }
819         printf "\n";
820
821         print "All optional dependencies are met.\n" if (!$optional);
822
823         if ($need == 1) {
824                 die "Can't build as $need mandatory dependency is missing";
825         } elsif ($need) {
826                 die "Can't build as $need mandatory dependencies are missing";
827         }
828
829         print "Needed package dependencies are met.\n";
830 }
831
832 #
833 # Main
834 #
835
836 while (@ARGV) {
837         my $arg = shift(@ARGV);
838
839         if ($arg eq "--no-virtualenv") {
840                 $virtualenv = 0;
841         } elsif ($arg eq "--no-pdf"){
842                 $pdf = 0;
843         } elsif ($arg eq "--version-check"){
844                 $version_check = 1;
845         } else {
846                 print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf> <--version-check>\n\n";
847                 print "Where:\n";
848                 print "\t--no-virtualenv\t- Recommend installing Sphinx instead of using a virtualenv\n";
849                 print "\t--version-check\t- if version is compatible, don't check for missing dependencies\n";
850                 print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n\n";
851                 exit -1;
852         }
853 }
854
855 #
856 # Determine the system type. There's no standard unique way that would
857 # work with all distros with a minimal package install. So, several
858 # methods are used here.
859 #
860 # By default, it will use lsb_release function. If not available, it will
861 # fail back to reading the known different places where the distro name
862 # is stored
863 #
864
865 $system_release = qx(lsb_release -d) if which("lsb_release");
866 $system_release =~ s/Description:\s*// if ($system_release);
867 $system_release = catcheck("/etc/system-release") if !$system_release;
868 $system_release = catcheck("/etc/redhat-release") if !$system_release;
869 $system_release = catcheck("/etc/lsb-release") if !$system_release;
870 $system_release = catcheck("/etc/gentoo-release") if !$system_release;
871
872 # This seems more common than LSB these days
873 if (!$system_release) {
874         my %os_var;
875         if (open IN, "cat /etc/os-release|") {
876                 while (<IN>) {
877                         if (m/^([\w\d\_]+)=\"?([^\"]*)\"?\n/) {
878                                 $os_var{$1}=$2;
879                         }
880                 }
881                 $system_release = $os_var{"NAME"};
882                 if (defined($os_var{"VERSION_ID"})) {
883                         $system_release .= " " . $os_var{"VERSION_ID"} if (defined($os_var{"VERSION_ID"}));
884                 } else {
885                         $system_release .= " " . $os_var{"VERSION"};
886                 }
887         }
888 }
889 $system_release = catcheck("/etc/issue") if !$system_release;
890 $system_release =~ s/\s+$//;
891
892 check_needs;