blob: bcd6f7978f8d5c8306ad82b4491a01f87cac3114 [file] [log] [blame]
Mauro Carvalho Chehab24071ac2017-07-17 18:46:36 -03001#!/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
Mauro Carvalho Chehab5be33182017-07-17 18:46:37 -030016my $virtenv_dir = "sphinx_1.4";
17
Mauro Carvalho Chehab24071ac2017-07-17 18:46:36 -030018#
19# Static vars
20#
21
22my %missing;
23my $system_release;
24my $need = 0;
25my $optional = 0;
26my $need_symlink = 0;
27my $need_sphinx = 0;
28my $install = "";
29
30#
31# Command line arguments
32#
33
34my $pdf = 1;
35my $virtualenv = 1;
36
37#
38# List of required texlive packages on Fedora and OpenSuse
39#
40
41my %texlive = (
42 'adjustbox.sty' => 'texlive-adjustbox',
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 check_program("virtualenv", 0) if (!findprog("virtualenv-3"));
215 check_program("pip", 0) if (!findprog("pip3"));
216 $need_sphinx = 1;
217 } else {
218 add_package("python-sphinx", 0);
219 }
220}
221
222#
223# Ancillary subroutines
224#
225
226sub catcheck($)
227{
228 my $res = "";
229 $res = qx(cat $_[0]) if (-r $_[0]);
230 return $res;
231}
232
233sub which($)
234{
235 my $file = shift;
236 my @path = split ":", $ENV{PATH};
237
238 foreach my $dir(@path) {
239 my $name = $dir.'/'.$file;
240 return $name if (-x $name );
241 }
242 return undef;
243}
244
245#
246# Subroutines that check distro-specific hints
247#
248
249sub give_debian_hints()
250{
251 my %map = (
252 "python-sphinx" => "python3-sphinx",
253 "sphinx_rtd_theme" => "python3-sphinx-rtd-theme",
254 "virtualenv" => "virtualenv",
255 "pip" => "python3-pip",
256 "dot" => "graphviz",
257 "convert" => "imagemagick",
258 "Pod::Usage" => "perl-modules",
259 "xelatex" => "texlive-xetex",
260 );
261
262 if ($pdf) {
263 check_missing_file("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
264 "fonts-dejavu", 1);
265 }
266
267 check_program("dvipng", 1) if ($pdf);
268 check_missing(\%map);
269
270 return if (!$need && !$optional);
271 printf("You should run:\n\n\tsudo apt-get install $install\n");
272}
273
274sub give_redhat_hints()
275{
276 my %map = (
277 "python-sphinx" => "python3-sphinx",
278 "sphinx_rtd_theme" => "python3-sphinx_rtd_theme",
279 "virtualenv" => "python3-virtualenv",
280 "pip" => "python3-pip",
281 "dot" => "graphviz",
282 "convert" => "ImageMagick",
283 "Pod::Usage" => "perl-Pod-Usage",
284 "xelatex" => "texlive-xetex-bin",
285 );
286
287 my @fedora_tex_pkgs = (
288 "texlive-collection-fontsrecommended",
289 "texlive-collection-latex",
290 "dejavu-sans-fonts",
291 "dejavu-serif-fonts",
292 "dejavu-sans-mono-fonts",
293 );
294
295 check_rpm_missing(\@fedora_tex_pkgs, 1) if ($pdf);
296 check_missing_tex(1) if ($pdf);
297 check_missing(\%map);
298
299 return if (!$need && !$optional);
300 printf("You should run:\n\n\tsudo dnf install -y $install\n");
301}
302
303sub give_opensuse_hints()
304{
305 my %map = (
306 "python-sphinx" => "python3-sphinx",
307 "sphinx_rtd_theme" => "python3-sphinx_rtd_theme",
308 "virtualenv" => "python3-virtualenv",
309 "pip" => "python3-pip",
310 "dot" => "graphviz",
311 "convert" => "ImageMagick",
312 "Pod::Usage" => "perl-Pod-Usage",
313 "xelatex" => "texlive-xetex-bin",
314 );
315
316 my @suse_tex_pkgs = (
317 "texlive-babel-english",
318 "texlive-caption",
319 "texlive-colortbl",
320 "texlive-courier",
321 "texlive-dvips",
322 "texlive-helvetic",
323 "texlive-makeindex",
324 "texlive-metafont",
325 "texlive-metapost",
326 "texlive-palatino",
327 "texlive-preview",
328 "texlive-times",
329 "texlive-zapfchan",
330 "texlive-zapfding",
331 );
332
333 check_rpm_missing(\@suse_tex_pkgs, 1) if ($pdf);
334 check_missing_tex(1) if ($pdf);
335 check_missing(\%map);
336
337 return if (!$need && !$optional);
338 printf("You should run:\n\n\tsudo zypper install --no-recommends $install\n");
339}
340
341sub give_arch_linux_hints()
342{
343 my %map = (
344 "sphinx_rtd_theme" => "python-sphinx_rtd_theme",
345 "virtualenv" => "python-virtualenv",
346 "pip" => "python-pip",
347 "dot" => "graphviz",
348 "convert" => "imagemagick",
349 "xelatex" => "texlive-bin",
350 );
351
352 my @archlinux_tex_pkgs = (
353 "texlive-core",
354 "texlive-latexextra",
355 "ttf-dejavu",
356 );
357 check_pacman_missing(\@archlinux_tex_pkgs, 1) if ($pdf);
358 check_missing(\%map);
359
360 return if (!$need && !$optional);
361 printf("You should run:\n\n\tsudo pacman -S $install\n");
362}
363
364sub give_gentoo_hints()
365{
366 my %map = (
367 "sphinx_rtd_theme" => "dev-python/sphinx_rtd_theme",
368 "virtualenv" => "dev-python/virtualenv",
369 "pip" => "dev-python/pip",
370 "dot" => "media-gfx/graphviz",
371 "convert" => "media-gfx/imagemagick",
372 "xelatex" => "dev-texlive/texlive-xetex media-fonts/dejavu",
373 );
374
375 check_missing_file("/usr/share/fonts/dejavu/DejaVuSans.ttf",
376 "media-fonts/dejavu", 1) if ($pdf);
377
378 check_missing(\%map);
379
380 return if (!$need && !$optional);
381 printf("You should run:\n\n\tsudo emerge --ask $install\n");
382}
383
384sub check_distros()
385{
386 # Distro-specific hints
387 if ($system_release =~ /Red Hat Enterprise Linux/) {
388 give_redhat_hints;
389 return;
390 }
391 if ($system_release =~ /Fedora/) {
392 give_redhat_hints;
393 return;
394 }
395 if ($system_release =~ /Ubuntu/) {
396 give_debian_hints;
397 return;
398 }
399 if ($system_release =~ /Debian/) {
400 give_debian_hints;
401 return;
402 }
403 if ($system_release =~ /openSUSE/) {
404 give_opensuse_hints;
405 return;
406 }
407 if ($system_release =~ /Arch Linux/) {
408 give_arch_linux_hints;
409 return;
410 }
411 if ($system_release =~ /Gentoo/) {
412 give_gentoo_hints;
413 return;
414 }
415
416 #
417 # Fall-back to generic hint code for other distros
418 # That's far from ideal, specially for LaTeX dependencies.
419 #
420 my %map = (
421 "sphinx-build" => "sphinx"
422 );
423 check_missing_tex(1) if ($pdf);
424 check_missing(\%map);
425 print "I don't know distro $system_release.\n";
426 print "So, I can't provide you a hint with the install procedure.\n";
427 print "There are likely missing dependencies.\n";
428}
429
430#
431# Common dependencies
432#
433
434sub check_needs()
435{
436 if ($system_release) {
437 print "Checking if the needed tools for $system_release are available\n";
438 } else {
439 print "Checking if the needed tools are present\n";
440 }
441
442 # Check for needed programs/tools
443 check_sphinx();
444 check_perl_module("Pod::Usage", 0);
445 check_program("make", 0);
446 check_program("gcc", 0);
447 check_python_module("sphinx_rtd_theme", 1) if (!$virtualenv);
448 check_program("xelatex", 1) if ($pdf);
449 check_program("dot", 1);
450 check_program("convert", 1);
451
452 check_distros();
453
454 if ($need_symlink) {
455 printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n",
456 which("sphinx-build-3");
457 }
458 if ($need_sphinx) {
Mauro Carvalho Chehab5be33182017-07-17 18:46:37 -0300459 my $activate = "$virtenv_dir/bin/activate";
460 if (-e "$ENV{'PWD'}/$activate") {
461 printf "\nNeed to activate virtualenv with:\n";
462 printf "\t. $activate\n";
463 } else {
464 my $virtualenv = findprog("virtualenv-3");
465 $virtualenv = findprog("virtualenv") if (!$virtualenv);
466 $virtualenv = "virtualenv" if (!$virtualenv);
Mauro Carvalho Chehab24071ac2017-07-17 18:46:36 -0300467
Mauro Carvalho Chehab5be33182017-07-17 18:46:37 -0300468 printf "\t$virtualenv $virtenv_dir\n";
469 printf "\t. $activate\n";
470 printf "\tpip install 'docutils==0.12'\n";
471 printf "\tpip install 'Sphinx==1.4.9'\n";
472 printf "\tpip install sphinx_rtd_theme\n";
473 $need++;
474 }
Mauro Carvalho Chehab24071ac2017-07-17 18:46:36 -0300475 }
476 printf "\n";
477
478 print "All optional dependenties are met.\n" if (!$optional);
479
480 if ($need == 1) {
481 die "Can't build as $need mandatory dependency is missing";
482 } elsif ($need) {
483 die "Can't build as $need mandatory dependencies are missing";
484 }
485
486 print "Needed package dependencies are met.\n";
487}
488
489#
490# Main
491#
492
493while (@ARGV) {
494 my $arg = shift(@ARGV);
495
496 if ($arg eq "--no-virtualenv") {
497 $virtualenv = 0;
498 } elsif ($arg eq "--no-pdf"){
499 $pdf = 0;
500 } else {
501 print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf>\n\n";
502 exit -1;
503 }
504}
505
506#
507# Determine the system type. There's no standard unique way that would
508# work with all distros with a minimal package install. So, several
509# methods are used here.
510#
511# By default, it will use lsb_release function. If not available, it will
512# fail back to reading the known different places where the distro name
513# is stored
514#
515
516$system_release = qx(lsb_release -d) if which("lsb_release");
517$system_release =~ s/Description:\s*// if ($system_release);
518$system_release = catcheck("/etc/system-release") if !$system_release;
519$system_release = catcheck("/etc/redhat-release") if !$system_release;
520$system_release = catcheck("/etc/lsb-release") if !$system_release;
521$system_release = catcheck("/etc/gentoo-release") if !$system_release;
522$system_release = catcheck("/etc/issue") if !$system_release;
523$system_release =~ s/\s+$//;
524
525check_needs;