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