sphinx-pre-install 14 KB

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