sphinx-pre-install 25 KB

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