linux/scripts/sphinx-pre-install
<<
>>
Prefs
   1#!/usr/bin/perl
   2use strict;
   3
   4# Copyright (c) 2017 Mauro Carvalho Chehab <mchehab@kernel.org>
   5#
   6# This program is free software; you can redistribute it and/or
   7# modify it under the terms of the GNU General Public License
   8# as published by the Free Software Foundation; either version 2
   9# of the License, or (at your option) any later version.
  10#
  11# This program is distributed in the hope that it will be useful,
  12# but WITHOUT ANY WARRANTY; without even the implied warranty of
  13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14# GNU General Public License for more details.
  15
  16my $virtenv_dir = "sphinx_1.4";
  17my $requirement_file = "Documentation/sphinx/requirements.txt";
  18
  19#
  20# Static vars
  21#
  22
  23my %missing;
  24my $system_release;
  25my $need = 0;
  26my $optional = 0;
  27my $need_symlink = 0;
  28my $need_sphinx = 0;
  29my $install = "";
  30
  31#
  32# Command line arguments
  33#
  34
  35my $pdf = 1;
  36my $virtualenv = 1;
  37
  38#
  39# List of required texlive packages on Fedora and OpenSuse
  40#
  41
  42my %texlive = (
  43        'amsfonts.sty'       => 'texlive-amsfonts',
  44        'amsmath.sty'        => 'texlive-amsmath',
  45        'amssymb.sty'        => 'texlive-amsfonts',
  46        'amsthm.sty'         => 'texlive-amscls',
  47        'anyfontsize.sty'    => 'texlive-anyfontsize',
  48        'atbegshi.sty'       => 'texlive-oberdiek',
  49        'bm.sty'             => 'texlive-tools',
  50        'capt-of.sty'        => 'texlive-capt-of',
  51        'cmap.sty'           => 'texlive-cmap',
  52        'ecrm1000.tfm'       => 'texlive-ec',
  53        'eqparbox.sty'       => 'texlive-eqparbox',
  54        'eu1enc.def'         => 'texlive-euenc',
  55        'fancybox.sty'       => 'texlive-fancybox',
  56        'fancyvrb.sty'       => 'texlive-fancyvrb',
  57        'float.sty'          => 'texlive-float',
  58        'fncychap.sty'       => 'texlive-fncychap',
  59        'footnote.sty'       => 'texlive-mdwtools',
  60        'framed.sty'         => 'texlive-framed',
  61        'luatex85.sty'       => 'texlive-luatex85',
  62        'multirow.sty'       => 'texlive-multirow',
  63        'needspace.sty'      => 'texlive-needspace',
  64        'palatino.sty'       => 'texlive-psnfss',
  65        'parskip.sty'        => 'texlive-parskip',
  66        'polyglossia.sty'    => 'texlive-polyglossia',
  67        'tabulary.sty'       => 'texlive-tabulary',
  68        'threeparttable.sty' => 'texlive-threeparttable',
  69        'titlesec.sty'       => 'texlive-titlesec',
  70        'ucs.sty'            => 'texlive-ucs',
  71        'upquote.sty'        => 'texlive-upquote',
  72        'wrapfig.sty'        => 'texlive-wrapfig',
  73);
  74
  75#
  76# Subroutines that checks if a feature exists
  77#
  78
  79sub check_missing(%)
  80{
  81        my %map = %{$_[0]};
  82
  83        foreach my $prog (sort keys %missing) {
  84                my $is_optional = $missing{$prog};
  85
  86                if ($is_optional) {
  87                        print "Warning: better to also install \"$prog\".\n";
  88                } else {
  89                        print "ERROR: please install \"$prog\", otherwise, build won't work.\n";
  90                }
  91                if (defined($map{$prog})) {
  92                        $install .= " " . $map{$prog};
  93                } else {
  94                        $install .= " " . $prog;
  95                }
  96        }
  97
  98        $install =~ s/^\s//;
  99}
 100
 101sub add_package($$)
 102{
 103        my $package = shift;
 104        my $is_optional = shift;
 105
 106        $missing{$package} = $is_optional;
 107        if ($is_optional) {
 108                $optional++;
 109        } else {
 110                $need++;
 111        }
 112}
 113
 114sub check_missing_file($$$)
 115{
 116        my $file = shift;
 117        my $package = shift;
 118        my $is_optional = shift;
 119
 120        return if(-e $file);
 121
 122        add_package($package, $is_optional);
 123}
 124
 125sub findprog($)
 126{
 127        foreach(split(/:/, $ENV{PATH})) {
 128                return "$_/$_[0]" if(-x "$_/$_[0]");
 129        }
 130}
 131
 132sub check_program($$)
 133{
 134        my $prog = shift;
 135        my $is_optional = shift;
 136
 137        return if findprog($prog);
 138
 139        add_package($prog, $is_optional);
 140}
 141
 142sub check_perl_module($$)
 143{
 144        my $prog = shift;
 145        my $is_optional = shift;
 146
 147        my $err = system("perl -M$prog -e 1 2>/dev/null /dev/null");
 148        return if ($err == 0);
 149
 150        add_package($prog, $is_optional);
 151}
 152
 153sub check_python_module($$)
 154{
 155        my $prog = shift;
 156        my $is_optional = shift;
 157
 158        my $err = system("python3 -c 'import $prog' 2>/dev/null /dev/null");
 159        return if ($err == 0);
 160        my $err = system("python -c 'import $prog' 2>/dev/null /dev/null");
 161        return if ($err == 0);
 162
 163        add_package($prog, $is_optional);
 164}
 165
 166sub check_rpm_missing($$)
 167{
 168        my @pkgs = @{$_[0]};
 169        my $is_optional = $_[1];
 170
 171        foreach my $prog(@pkgs) {
 172                my $err = system("rpm -q '$prog' 2>/dev/null >/dev/null");
 173                add_package($prog, $is_optional) if ($err);
 174        }
 175}
 176
 177sub check_pacman_missing($$)
 178{
 179        my @pkgs = @{$_[0]};
 180        my $is_optional = $_[1];
 181
 182        foreach my $prog(@pkgs) {
 183                my $err = system("pacman -Q '$prog' 2>/dev/null >/dev/null");
 184                add_package($prog, $is_optional) if ($err);
 185        }
 186}
 187
 188sub check_missing_tex($)
 189{
 190        my $is_optional = shift;
 191        my $kpsewhich = findprog("kpsewhich");
 192
 193        foreach my $prog(keys %texlive) {
 194                my $package = $texlive{$prog};
 195                if (!$kpsewhich) {
 196                        add_package($package, $is_optional);
 197                        next;
 198                }
 199                my $file = qx($kpsewhich $prog);
 200                add_package($package, $is_optional) if ($file =~ /^\s*$/);
 201        }
 202}
 203
 204sub check_sphinx()
 205{
 206        return if findprog("sphinx-build");
 207
 208        if (findprog("sphinx-build-3")) {
 209                $need_symlink = 1;
 210                return;
 211        }
 212
 213        if ($virtualenv) {
 214                my $prog = findprog("virtualenv-3");
 215                $prog = findprog("virtualenv-3.5") if (!$prog);
 216
 217                check_program("virtualenv", 0) if (!$prog);
 218                $need_sphinx = 1;
 219        } else {
 220                add_package("python-sphinx", 0);
 221        }
 222}
 223
 224#
 225# Ancillary subroutines
 226#
 227
 228sub catcheck($)
 229{
 230  my $res = "";
 231  $res = qx(cat $_[0]) if (-r $_[0]);
 232  return $res;
 233}
 234
 235sub which($)
 236{
 237        my $file = shift;
 238        my @path = split ":", $ENV{PATH};
 239
 240        foreach my $dir(@path) {
 241                my $name = $dir.'/'.$file;
 242                return $name if (-x $name );
 243        }
 244        return undef;
 245}
 246
 247#
 248# Subroutines that check distro-specific hints
 249#
 250
 251sub give_debian_hints()
 252{
 253        my %map = (
 254                "python-sphinx"         => "python3-sphinx",
 255                "sphinx_rtd_theme"      => "python3-sphinx-rtd-theme",
 256                "virtualenv"            => "virtualenv",
 257                "dot"                   => "graphviz",
 258                "convert"               => "imagemagick",
 259                "Pod::Usage"            => "perl-modules",
 260                "xelatex"               => "texlive-xetex",
 261                "rsvg-convert"          => "librsvg2-bin",
 262        );
 263
 264        if ($pdf) {
 265                check_missing_file("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
 266                                   "fonts-dejavu", 1);
 267        }
 268
 269        check_program("dvipng", 1) if ($pdf);
 270        check_missing(\%map);
 271
 272        return if (!$need && !$optional);
 273        printf("You should run:\n\n\tsudo apt-get install $install\n");
 274}
 275
 276sub give_redhat_hints()
 277{
 278        my %map = (
 279                "python-sphinx"         => "python3-sphinx",
 280                "sphinx_rtd_theme"      => "python3-sphinx_rtd_theme",
 281                "virtualenv"            => "python3-virtualenv",
 282                "dot"                   => "graphviz",
 283                "convert"               => "ImageMagick",
 284                "Pod::Usage"            => "perl-Pod-Usage",
 285                "xelatex"               => "texlive-xetex-bin",
 286                "rsvg-convert"          => "librsvg2-tools",
 287        );
 288
 289        my @fedora26_opt_pkgs = (
 290                "graphviz-gd",          # Fedora 26: needed for PDF support
 291        );
 292
 293        my @fedora_tex_pkgs = (
 294                "texlive-collection-fontsrecommended",
 295                "texlive-collection-latex",
 296                "dejavu-sans-fonts",
 297                "dejavu-serif-fonts",
 298                "dejavu-sans-mono-fonts",
 299        );
 300
 301        #
 302        # Checks valid for RHEL/CentOS version 7.x.
 303        #
 304        if (! $system_release =~ /Fedora/) {
 305                $map{"virtualenv"} = "python-virtualenv";
 306        }
 307
 308        my $release;
 309
 310        $release = $1 if ($system_release =~ /Fedora\s+release\s+(\d+)/);
 311
 312        check_rpm_missing(\@fedora26_opt_pkgs, 1) if ($pdf && $release >= 26);
 313        check_rpm_missing(\@fedora_tex_pkgs, 1) if ($pdf);
 314        check_missing_tex(1) if ($pdf);
 315        check_missing(\%map);
 316
 317        return if (!$need && !$optional);
 318
 319        if ($release >= 18) {
 320                # dnf, for Fedora 18+
 321                printf("You should run:\n\n\tsudo dnf install -y $install\n");
 322        } else {
 323                # yum, for RHEL (and clones) or Fedora version < 18
 324                printf("You should run:\n\n\tsudo yum install -y $install\n");
 325        }
 326}
 327
 328sub give_opensuse_hints()
 329{
 330        my %map = (
 331                "python-sphinx"         => "python3-sphinx",
 332                "sphinx_rtd_theme"      => "python3-sphinx_rtd_theme",
 333                "virtualenv"            => "python3-virtualenv",
 334                "dot"                   => "graphviz",
 335                "convert"               => "ImageMagick",
 336                "Pod::Usage"            => "perl-Pod-Usage",
 337                "xelatex"               => "texlive-xetex-bin",
 338                "rsvg-convert"          => "rsvg-view",
 339        );
 340
 341        my @suse_tex_pkgs = (
 342                "texlive-babel-english",
 343                "texlive-caption",
 344                "texlive-colortbl",
 345                "texlive-courier",
 346                "texlive-dvips",
 347                "texlive-helvetic",
 348                "texlive-makeindex",
 349                "texlive-metafont",
 350                "texlive-metapost",
 351                "texlive-palatino",
 352                "texlive-preview",
 353                "texlive-times",
 354                "texlive-zapfchan",
 355                "texlive-zapfding",
 356        );
 357
 358        check_rpm_missing(\@suse_tex_pkgs, 1) if ($pdf);
 359        check_missing_tex(1) if ($pdf);
 360        check_missing(\%map);
 361
 362        return if (!$need && !$optional);
 363        printf("You should run:\n\n\tsudo zypper install --no-recommends $install\n");
 364}
 365
 366sub give_mageia_hints()
 367{
 368        my %map = (
 369                "python-sphinx"         => "python3-sphinx",
 370                "sphinx_rtd_theme"      => "python3-sphinx_rtd_theme",
 371                "virtualenv"            => "python3-virtualenv",
 372                "dot"                   => "graphviz",
 373                "convert"               => "ImageMagick",
 374                "Pod::Usage"            => "perl-Pod-Usage",
 375                "xelatex"               => "texlive",
 376                "rsvg-convert"          => "librsvg2-tools",
 377        );
 378
 379        my @tex_pkgs = (
 380                "texlive-fontsextra",
 381        );
 382
 383        check_rpm_missing(\@tex_pkgs, 1) if ($pdf);
 384        check_missing(\%map);
 385
 386        return if (!$need && !$optional);
 387        printf("You should run:\n\n\tsudo urpmi $install\n");
 388}
 389
 390sub give_arch_linux_hints()
 391{
 392        my %map = (
 393                "sphinx_rtd_theme"      => "python-sphinx_rtd_theme",
 394                "virtualenv"            => "python-virtualenv",
 395                "dot"                   => "graphviz",
 396                "convert"               => "imagemagick",
 397                "xelatex"               => "texlive-bin",
 398                "rsvg-convert"          => "extra/librsvg",
 399        );
 400
 401        my @archlinux_tex_pkgs = (
 402                "texlive-core",
 403                "texlive-latexextra",
 404                "ttf-dejavu",
 405        );
 406        check_pacman_missing(\@archlinux_tex_pkgs, 1) if ($pdf);
 407        check_missing(\%map);
 408
 409        return if (!$need && !$optional);
 410        printf("You should run:\n\n\tsudo pacman -S $install\n");
 411}
 412
 413sub give_gentoo_hints()
 414{
 415        my %map = (
 416                "sphinx_rtd_theme"      => "dev-python/sphinx_rtd_theme",
 417                "virtualenv"            => "dev-python/virtualenv",
 418                "dot"                   => "media-gfx/graphviz",
 419                "convert"               => "media-gfx/imagemagick",
 420                "xelatex"               => "dev-texlive/texlive-xetex media-fonts/dejavu",
 421                "rsvg-convert"          => "gnome-base/librsvg",
 422        );
 423
 424        check_missing_file("/usr/share/fonts/dejavu/DejaVuSans.ttf",
 425                           "media-fonts/dejavu", 1) if ($pdf);
 426
 427        check_missing(\%map);
 428
 429        return if (!$need && !$optional);
 430
 431        printf("You should run:\n\n");
 432        printf("\tsudo su -c 'echo \"media-gfx/imagemagick svg png\" > /etc/portage/package.use/imagemagick'\n");
 433        printf("\tsudo su -c 'echo \"media-gfx/graphviz cairo pdf\" > /etc/portage/package.use/graphviz'\n");
 434        printf("\tsudo emerge --ask $install\n");
 435
 436}
 437
 438sub check_distros()
 439{
 440        # Distro-specific hints
 441        if ($system_release =~ /Red Hat Enterprise Linux/) {
 442                give_redhat_hints;
 443                return;
 444        }
 445        if ($system_release =~ /CentOS/) {
 446                give_redhat_hints;
 447                return;
 448        }
 449        if ($system_release =~ /Scientific Linux/) {
 450                give_redhat_hints;
 451                return;
 452        }
 453        if ($system_release =~ /Oracle Linux Server/) {
 454                give_redhat_hints;
 455                return;
 456        }
 457        if ($system_release =~ /Fedora/) {
 458                give_redhat_hints;
 459                return;
 460        }
 461        if ($system_release =~ /Ubuntu/) {
 462                give_debian_hints;
 463                return;
 464        }
 465        if ($system_release =~ /Debian/) {
 466                give_debian_hints;
 467                return;
 468        }
 469        if ($system_release =~ /openSUSE/) {
 470                give_opensuse_hints;
 471                return;
 472        }
 473        if ($system_release =~ /Mageia/) {
 474                give_mageia_hints;
 475                return;
 476        }
 477        if ($system_release =~ /Arch Linux/) {
 478                give_arch_linux_hints;
 479                return;
 480        }
 481        if ($system_release =~ /Gentoo/) {
 482                give_gentoo_hints;
 483                return;
 484        }
 485
 486        #
 487        # Fall-back to generic hint code for other distros
 488        # That's far from ideal, specially for LaTeX dependencies.
 489        #
 490        my %map = (
 491                "sphinx-build" => "sphinx"
 492        );
 493        check_missing_tex(1) if ($pdf);
 494        check_missing(\%map);
 495        print "I don't know distro $system_release.\n";
 496        print "So, I can't provide you a hint with the install procedure.\n";
 497        print "There are likely missing dependencies.\n";
 498}
 499
 500#
 501# Common dependencies
 502#
 503
 504sub check_needs()
 505{
 506        if ($system_release) {
 507                print "Detected OS: $system_release.\n";
 508        } else {
 509                print "Unknown OS\n";
 510        }
 511
 512        # RHEL 7.x and clones have Sphinx version 1.1.x and incomplete texlive
 513        if (($system_release =~ /Red Hat Enterprise Linux/) ||
 514            ($system_release =~ /CentOS/) ||
 515            ($system_release =~ /Scientific Linux/) ||
 516            ($system_release =~ /Oracle Linux Server/)) {
 517                $virtualenv = 1;
 518                $pdf = 0;
 519
 520                printf("NOTE: On this distro, Sphinx and TexLive shipped versions are incompatible\n");
 521                printf("with doc build. So, use Sphinx via a Python virtual environment.\n\n");
 522                printf("This script can't install a TexLive version that would provide PDF.\n");
 523        }
 524
 525        # Check for needed programs/tools
 526        check_sphinx();
 527        check_perl_module("Pod::Usage", 0);
 528        check_program("make", 0);
 529        check_program("gcc", 0);
 530        check_python_module("sphinx_rtd_theme", 1) if (!$virtualenv);
 531        check_program("xelatex", 1) if ($pdf);
 532        check_program("dot", 1);
 533        check_program("convert", 1);
 534        check_program("rsvg-convert", 1) if ($pdf);
 535
 536        check_distros();
 537
 538        if ($need_symlink) {
 539                printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n",
 540                       which("sphinx-build-3");
 541        }
 542        if ($need_sphinx) {
 543                my $activate = "$virtenv_dir/bin/activate";
 544                if (-e "$ENV{'PWD'}/$activate") {
 545                        printf "\nNeed to activate virtualenv with:\n";
 546                        printf "\t. $activate\n";
 547                } else {
 548                        my $virtualenv = findprog("virtualenv-3");
 549                        $virtualenv = findprog("virtualenv-3.5") if (!$virtualenv);
 550                        $virtualenv = findprog("virtualenv") if (!$virtualenv);
 551                        $virtualenv = "virtualenv" if (!$virtualenv);
 552
 553                        printf "\t$virtualenv $virtenv_dir\n";
 554                        printf "\t. $activate\n";
 555                        printf "\tpip install -r $requirement_file\n";
 556                        $need++;
 557                }
 558        }
 559        printf "\n";
 560
 561        print "All optional dependenties are met.\n" if (!$optional);
 562
 563        if ($need == 1) {
 564                die "Can't build as $need mandatory dependency is missing";
 565        } elsif ($need) {
 566                die "Can't build as $need mandatory dependencies are missing";
 567        }
 568
 569        print "Needed package dependencies are met.\n";
 570}
 571
 572#
 573# Main
 574#
 575
 576while (@ARGV) {
 577        my $arg = shift(@ARGV);
 578
 579        if ($arg eq "--no-virtualenv") {
 580                $virtualenv = 0;
 581        } elsif ($arg eq "--no-pdf"){
 582                $pdf = 0;
 583        } else {
 584                print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf>\n\n";
 585                exit -1;
 586        }
 587}
 588
 589#
 590# Determine the system type. There's no standard unique way that would
 591# work with all distros with a minimal package install. So, several
 592# methods are used here.
 593#
 594# By default, it will use lsb_release function. If not available, it will
 595# fail back to reading the known different places where the distro name
 596# is stored
 597#
 598
 599$system_release = qx(lsb_release -d) if which("lsb_release");
 600$system_release =~ s/Description:\s*// if ($system_release);
 601$system_release = catcheck("/etc/system-release") if !$system_release;
 602$system_release = catcheck("/etc/redhat-release") if !$system_release;
 603$system_release = catcheck("/etc/lsb-release") if !$system_release;
 604$system_release = catcheck("/etc/gentoo-release") if !$system_release;
 605$system_release = catcheck("/etc/issue") if !$system_release;
 606$system_release =~ s/\s+$//;
 607
 608check_needs;
 609