epic-s6ts
[kivitendo-erp.git] / scripts / installation_check.pl
1 #!/usr/bin/perl -w
2
3 our $master_templates;
4 BEGIN {
5   use FindBin;
6
7   unshift(@INC, $FindBin::Bin . '/../modules/override'); # Use our own versions of various modules (e.g. YAML).
8   push   (@INC, $FindBin::Bin . '/..');                  # '.' will be removed from @INC soon.
9
10   # this is a default dir. may be wrong in your installation, change it then
11   $master_templates = $FindBin::Bin . '/../templates/print/';
12 }
13
14 use strict;
15 use Getopt::Long;
16 use List::MoreUtils qw(uniq);
17 use Pod::Usage;
18 use Term::ANSIColor;
19 use Text::Wrap;
20
21 unless (eval { require Config::Std; 1 }){
22   print STDERR <<EOL ;
23 +------------------------------------------------------------------------------+
24   Perl Modul Config::Std could not be loaded.
25
26   Debian: you may install the needed *.deb package with:
27     apt-get install libconfig-std-perl
28
29   Red Hat/Fedora/CentOS: you may install the needed *.rpm package with:
30     yum install perl-Config-Std
31
32   SUSE: you may install the needed *.rpm package with:
33     zypper install perl-Config-Std
34
35 +------------------------------------------------------------------------------+
36 EOL
37
38   exit 72;
39 }
40
41 use SL::InstallationCheck;
42 use SL::LxOfficeConf;
43
44 my @missing_modules;
45 my %check;
46 Getopt::Long::Configure ("bundling");
47 GetOptions(
48   "v|verbose"   => \ my $v,
49   "a|all"       => \ $check{a},
50   "o|optional!" => \ $check{o},
51   "d|devel!"    => \ $check{d},
52   "l|latex!"    => \ $check{l},
53   "r|required!" => \ $check{r},
54   "h|help"      => sub { pod2usage(-verbose => 2) },
55   "c|color!"    => \ ( my $c = 1 ),
56   "i|install-command!"  => \ my $apt,
57   "s|silent"    => \ $check{s},
58 );
59
60 my %install_methods = (
61   apt    => { key => 'debian', install => 'sudo apt install', system => "Debian, Ubuntu" },
62   yum    => { key => 'fedora', install => 'sudo yum install',     system => "RHEL, Fedora, CentOS" },
63   zypper => { key => 'suse',   install => 'sudo zypper install',  system => "SLES, openSUSE" },
64   cpan   => { key => 'name',   install => "sudo cpan",            system => "CPAN" },
65 );
66
67 # if nothing is requested check "required"
68 my $default_run;
69 if (!defined $check{a}
70  && !defined $check{l}
71  && !defined $check{o}
72  && !defined $check{d}) {
73   $check{r} = 1;
74   $default_run ='1';  # no parameter, therefore print a note after default run
75 }
76
77 if ($check{a}) {
78   $check{$_} //= 1 for qw(o d l r);
79 }
80
81
82 $| = 1;
83
84 if (!SL::LxOfficeConf->read(undef, 'may fail')) {
85   print_header('Could not load the config file. If you have dependencies from any features enabled in the configuration these will still show up as optional because of this. Please rerun this script after installing the dependencies needed to load the configuration.')
86 } else {
87   SL::InstallationCheck::check_for_conditional_dependencies();
88 }
89
90 if ($check{r}) {
91   print_header('Checking Required Modules');
92   check_module($_, required => 1) for @SL::InstallationCheck::required_modules;
93   check_pdfinfo();
94 }
95 if ($check{o}) {
96   print_header('Checking Optional Modules');
97   check_module($_, optional => 1) for @SL::InstallationCheck::optional_modules;
98 }
99 if ($check{d}) {
100   print_header('Checking Developer Modules');
101   check_module($_, devel => 1) for @SL::InstallationCheck::developer_modules;
102 }
103 if ($check{l}) {
104   check_latex();
105 }
106
107 my $fail = @missing_modules;
108 print_header('Result');
109 print_line('All', $fail ? 'NOT ok' : 'OK', $fail ? 'red' : 'green');
110
111 if ($default_run && !$check{s}) {
112   if (@missing_modules) {
113     $apt = 1;
114   print <<"EOL";
115
116 HEY! It seems there are modules MISSING! Look for the red lines with "NOT ok"
117 above. You'll want to fix those, I've enabled --install-command for you...
118 EOL
119   } else {
120   print <<"EOL";
121
122 Standard check done, everything is OK and up to date. Have a look at the --help
123 section of this script to see some more advanced checks for developer and
124 optional dependencies, as well as LaTeX packages you might need.
125 EOL
126   }
127 }
128
129 if (@missing_modules && $apt && !$check{s}) {
130   print "\nHere are some sample installation lines, choose one appropriate for your system:\n\n";
131   local $Text::Wrap::separator = " \\\n";
132
133   for (keys %install_methods) {
134     my $method = $install_methods{$_};
135     if (my @install_candidates = grep $_, map { $_->{$method->{key}} } @missing_modules) {
136       print "$method->{system}:\n";
137       print wrap("  ", "    ",  $method->{install}, @install_candidates);
138       print $/;
139     }
140   }
141 }
142
143 exit !!@missing_modules;
144
145 sub check_latex {
146   my ($res) = check_kpsewhich();
147   print_result("Looking for LaTeX kpsewhich", $res);
148
149   # no pdfx -> no zugferd possible
150   my $ret = kpsewhich('template/print/', 'sty', 'pdfx');
151   die "Cannot use pdfx. Please install this package first (debian: apt install texlive-latex-extra)"  if $ret;
152   # check version 2018
153   my $latex = $::lx_office_conf{applications}->{latex} || 'pdflatex';
154   my $pdfx = (system ${latex} . ' --interaction=batchmode "\documentclass{minimal} \RequirePackage{pdfx} \csname @ifpackagelater\endcsname{pdfx}{2018/12/22}{}{\show\relax} \begin{document} \end{document}"');
155
156   print_result ("Looking for pdfx version 2018 or higher", !$pdfx);
157   push @missing_modules, \(name => 'pdfx') if $pdfx;
158
159   if ($res) {
160     check_template_dir($_) for SL::InstallationCheck::template_dirs($master_templates);
161   }
162   print STDERR <<EOL if $pdfx;
163 +------------------------------------------------------------------------------+
164   Your pdfx version is too old. You cannot use ZuGFeRD or modern (2018+)
165   templates. Please consider using a more recent LaTeX environment.
166   Verify with:
167   pdflatex --interaction=batchmode "\RequirePackage{pdfx}[2018/12/22]"
168 +------------------------------------------------------------------------------+
169 EOL
170 }
171
172 sub check_template_dir {
173   my ($dir) = @_;
174   my $path  = $master_templates . $dir;
175
176   print_header("Checking LaTeX Dependencies for Master Templates '$dir'");
177   kpsewhich($path, 'cls', $_) for SL::InstallationCheck::classes_from_latex($path, '\documentclass');
178
179   my @sty = sort { $a cmp $b } uniq (
180     SL::InstallationCheck::classes_from_latex($path, '\usepackage'),
181     qw(textcomp ulem embedfile)
182   );
183   kpsewhich($path, 'sty', $_) for @sty;
184 }
185
186 our $mastertemplate_path = './templates/print/';
187
188 sub check_kpsewhich {
189   return 1 if SL::InstallationCheck::check_kpsewhich();
190
191   print STDERR <<EOL if $v && !$check{s};
192 +------------------------------------------------------------------------------+
193   Can't find kpsewhich, is there a proper installed LaTeX?
194   On Debian you may run "aptitude install texlive-base-bin"
195 +------------------------------------------------------------------------------+
196 EOL
197   return 0;
198 }
199
200 sub kpsewhich {
201   my ($dw, $type, $package) = @_;
202   $package =~ s/[^-_0-9A-Za-z]//g;
203   my $type_desc = $type eq 'cls' ? 'document class' : 'package';
204
205   eval { require String::ShellQuote; 1 } or warn "can't load String::ShellQuote" && return;
206      $dw         = String::ShellQuote::shell_quote $dw;
207   my $e_package  = String::ShellQuote::shell_quote $package;
208   my $e_type     = String::ShellQuote::shell_quote $type;
209
210   my $exit = system(qq|TEXINPUTS=".:$dw:" kpsewhich $e_package.$e_type > /dev/null|);
211   my $res  = $exit > 0 ? 0 : 1;
212
213   print_result("Looking for LaTeX $type_desc $package", $res);
214   if (!$res) {
215     print STDERR <<EOL if $v && !$check{s};
216 +------------------------------------------------------------------------------+
217   LaTeX $type_desc $package could not be loaded.
218
219   On Debian you may find the needed *.deb package with:
220     apt-file search $package.$type
221
222   Maybe you need to install apt-file first by:
223     aptitude install apt-file && apt-file update
224 +------------------------------------------------------------------------------+
225 EOL
226   }
227 }
228
229 sub check_pdfinfo {
230   my $line = "Looking for pdfinfo executable";
231   my $shell_out = `pdfinfo -v 2>&1 | grep version 2> /dev/null`;
232   my ($label,$vers,$ver_string)  = split / /,$shell_out;
233   if ( $label && $label eq 'pdfinfo' ) {
234     chop $ver_string;
235     print_line($line, $ver_string, 'green');
236   } else {
237     print_line($line, 'not installed','red');
238     my %modinfo = ( debian => 'poppler-utils' );
239     push @missing_modules, \%modinfo;
240
241   }
242 }
243
244 sub check_module {
245   my ($module, %role) = @_;
246
247   my $line = "Looking for $module->{fullname}";
248   $line   .= " (from $module->{dist_name})" if $module->{dist_name};
249   my ($res, $ver) = SL::InstallationCheck::module_available($module->{"name"}, $module->{version});
250   if ($res) {
251     my $ver_string = ref $ver && $ver->can('numify') ? $ver->numify : $ver ? $ver : 'no version';
252     print_line($line, $ver_string, 'green');
253   } else {
254     print_result($line, $res);
255   }
256
257
258   return if $res;
259
260   push @missing_modules, $module;
261
262   my $needed_text =
263       $role{optional} ? 'It is OPTIONAL for kivitendo but RECOMMENDED for improved functionality.'
264     : $role{required} ? 'It is NEEDED by kivitendo and must be installed.'
265     : $role{devel}    ? 'It is OPTIONAL for kivitendo and only useful for developers.'
266     :                   'It is not listed as a dependency yet. Please tell this the developers.';
267
268   my @source_texts = module_source_texts($module);
269   local $" = $/;
270   print STDERR <<EOL if $v && !$check{s};
271 +------------------------------------------------------------------------------+
272   $module->{fullname} could not be loaded.
273
274   This module is either too old or not available on your system.
275   $needed_text
276
277   Here are some ideas how to get it:
278
279 @source_texts
280 +------------------------------------------------------------------------------+
281 EOL
282 }
283
284 sub module_source_texts {
285   my ($module) = @_;
286   my @texts;
287   for my $key (keys %install_methods) {
288     my $method = $install_methods{$key};
289     push @texts, <<"EOL" if $module->{$method->{key}};
290   - Using $method->{system} you can install it with $key:
291       $method->{install} $module->{$method->{key}}
292 EOL
293   }
294   push @texts, <<EOL if $module->{url};
295   - You can download it from this URL and install it manually:
296       $module->{url}
297 EOL
298
299   return @texts;
300 }
301
302 sub mycolor {
303   return $_[0] unless $c;
304   return colored(@_);
305 }
306
307 sub print_result {
308   my ($test, $exit) = @_;
309   if ($exit) {
310     print_line($test, 'ok', 'green');
311   } else {
312     print_line($test, 'NOT ok', 'red');
313   }
314 }
315
316 sub print_line {
317   my ($text, $res, $color) = @_;
318   return if $check{s};
319   print $text, " ", ('.' x (78 - length($text) - length($res))), " ", mycolor($res, $color), $/;
320 }
321
322 sub print_header {
323   return if $check{s};
324   print $/;
325   print "$_[0]:", $/;
326 }
327
328 1;
329
330 __END__
331
332 =encoding UTF-8
333
334 =head1 NAME
335
336 scripts/installation_check.pl - check kivitendo dependencies
337
338 =head1 SYNOPSIS
339
340   scripts/installation_check.pl [OPTION]
341
342 =head1 DESCRIPTION
343
344 Check dependencys. List all perl modules needed by kivitendo, probes for them,
345 and warns if one is not available.  List all LaTeX document classes and
346 packages needed by kivitendo master templates, probes for them, and warns if
347 one is not available.
348
349
350 =head1 OPTIONS
351
352 =over 4
353
354 =item C<-a, --all>
355
356 Probe for all perl modules and all LaTeX master templates.
357
358 =item C<-c, --color>
359
360 Color output. Default on.
361
362 =item C<--no-color>
363
364 No color output. Helpful to avoid terminal escape problems.
365
366 =item C<-d, --devel>
367
368 Probe for perl developer dependencies. (Used for console  and tags file)
369
370 =item C<--no-devel>
371
372 Don't probe for perl developer dependencies. (Useful in combination with --all)
373
374 =item C<-h, --help>
375
376 Display this help.
377
378 =item C<-o, --optional>
379
380 Probe for optional modules.
381
382 =item C<--no-optional>
383
384 Don't probe for optional perl modules. (Useful in combination with --all)
385
386 =item C<-r, --required>
387
388 Probe for required perl modules (default).
389
390 =item C<--no-required>
391
392 Don't probe for required perl modules. (Useful in combination with --all)
393
394 =item C<-l. --latex>
395
396 Probe for LaTeX documentclasses and packages in master templates.
397
398 =item C<--no-latex>
399
400 Don't probe for LaTeX document classes and packages in master templates. (Useful in combination with --all)
401
402 =item C<-v. --verbose>
403
404 Print additional info for missing dependencies
405
406 =item C<-i, --install-command>
407
408 Tries to generate installation commands for the most common package managers.
409 Note that these lists can be slightly off, but it should still save you a lot
410 of typing.
411
412 =back
413
414 =head1 BUGS, CAVEATS and TODO
415
416 =over 4
417
418 =item *
419
420 Fedora packages not listed yet.
421
422 =item *
423
424 Not possible yet to generate a combined cpan/apt-get string to install all needed.
425
426 =item *
427
428 Not able to handle devel cpan modules yet.
429
430 =item *
431
432 Version requirements not fully tested yet.
433
434 =back
435
436 =head1 AUTHOR
437
438   Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
439   Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
440   Wulf Coulmann E<lt>wulf@coulmann.deE<gt>
441
442 =cut