8 use lib
'/omd/versions/default/lib/perl5/lib/perl5';
9 use lib
'/var/tmp/p5_dist/dest/lib/perl5';
10 use Storable qw
/lock_store lock_retrieve/;
13 ####################################
15 $Data::Dumper
::Sortkeys
= 1;
17 my $additional_deps = {
18 'IO' => { 'ExtUtils::ParseXS' => '3.21' },
21 ####################################
22 # is this a core module?
25 #my @v = split/\./, $Config{'version'};
26 #my $v = $v[0] + $v[1]/1000;
27 # use fixed version: 5.008, otherwise we would to sort modules on the oldes possible host
29 if(exists $Module::CoreList
::version
{$v}{$module}) {
30 return $Module::CoreList
::version
{$v}{$module} || 0;
35 ####################################
39 print "[INFO] cmd: $cmd\n" if $verbose;
41 open(my $ph, '-|', $cmd." 2>&1") or die("cannot execute cmd: $cmd");
42 while(my $line = <$ph>) {
46 if($?
!= 0 and !defined $force) { my $rc = $?
>>8; die("cmd failed (rc:$rc): $cmd\n$out") };
50 ####################################
51 # get all dependencies for a tarball
52 # needs a filename like: Storable-2.21.tar.gz
54 my($file, $download, $quiet) = @_;
55 $quiet = 0 unless defined $quiet;
60 return if defined $already_checked{$file};
61 $already_checked{$file} = 1;
63 print " -> checking dependecies for: $file\n" unless $quiet;
64 print "." if $quiet == 1;
65 my $meta = get_meta_for_tarball
($file);
67 my $deps = BuildHelper
::get_deps_from_meta
($meta);
68 add_additional_deps
($file, $deps);
69 $deps_cache{$file} = $deps;
70 for my $dep (keys %{$deps}) {
71 my $depv = $deps->{$dep};
72 my $cv = is_core_module
($dep);
73 if(defined $cv and $depv == 0) {
74 print " -> $dep ($depv) skipped zero core dependency\n" unless $quiet;
77 print " -> $dep ($depv)\n" unless $quiet;
79 BuildHelper
::download_module
($dep, $depv);
85 ####################################
86 # download all dependencies for a tarball
87 # needs a filename like: Storable-2.21.tar.gz
90 return BuildHelper
::get_deps
($file, 1);
93 ####################################
95 # needs a module name like: IO::All
99 my $no_dep = shift || 0;
100 my $quiet = shift || 0;
102 print "[INFO] download_module($mod, $ver)\n" if $verbose;
104 our %already_downloaded;
107 return \
@downloaded if defined $already_downloaded{$mod.$ver};
108 $already_downloaded{$mod.$ver} = 1;
110 # we dont need core modules or perl dependency
111 if($mod eq 'perl') { return \
@downloaded; }
113 my $urlpath = BuildHelper
::get_url_for_module
($mod);
114 return \
@downloaded if defined $already_downloaded{$urlpath};
115 if($urlpath =~ m/\/perl\
-[\d\
.]+\
.tar\
.gz
/) {
116 $already_downloaded{$urlpath} = 1;
117 print " -> links to perl\n";
120 my $tarball=$urlpath; $tarball =~ s/^.*\///g
;
122 if( ! -f
$tarball and !defined $already_downloaded{$urlpath}) {
123 BuildHelper
::cmd
('wget --retry-connrefused -q "http://search.cpan.org'.$urlpath.'"');
124 $already_downloaded{$urlpath} = 1;
125 BuildHelper
::download_deps
($tarball) unless $no_dep == 1;
126 push @downloaded, $tarball;
127 print "downloaded $tarball\n" unless $quiet;
129 if(!defined $deps_checked{$tarball}) {
130 print "$tarball already downloaded\n";
131 #print "rechecking dependency\n";
132 #BuildHelper::download_deps($tarball);
133 $deps_checked{$tarball} = 1;
135 print "$tarball already downloaded\n";
141 ####################################
144 BuildHelper
::cmd
('wget --retry-connrefused -q "http://thruk.org/libs/src/'.$module.'"');
148 ####################################
150 # needs a filename like: Storable-2.21.tar.gz
151 # returns module name and version
154 my($module,$version) = ($file, 0);
156 if($file =~ m/\-([0-9\.]*)(\.[\w\d]+)*(\.tar\.gz|\.tgz|\.zip)/) {
160 $module =~ s/\-[0-9\.\w]*(\.tar\.gz|\.tgz|\.zip)//g;
161 $module =~ s/\-/::/g;
162 $module = translate_module_name
($module);
164 return($module,$version);
167 ####################################
168 # return real module name
169 # TODO: get real name from http://search.cpan.org/search?mode=dist&query=<module>
170 sub translate_module_name
{
173 'Filter' => 'Filter::exec',
174 'IO::Compress' => 'IO::Compress::Base',
175 'IO::stringy' => 'IO::Scalar',
176 'Scalar::List::Utils' => 'List::Util::XS',
177 'libwww::perl' => 'LWP',
178 'Template::Toolkit' => 'Template',
179 'TermReadKey' => 'Term::ReadKey',
180 'Gearman' => 'Gearman::Client',
181 'PathTools' => 'File::Spec',
182 'libnet' => 'Net::Cmd',
183 'podlators' => 'Pod::Man',
184 'Text::Tabs+Wrap' => 'Text::Tabs',
186 $name =~ s/^inc::Module::Install.*?/Module::Install/g;
187 return $tr->{$name} if defined $tr->{$name};
191 ####################################
192 # return filename from list
194 my($mod, $files, $version) = @_;
195 if($version > 0 and defined $files->{$mod}) {
196 my($m,$v) = file_to_module
($files->{$mod});
197 return $files->{$mod} if version_compare
($v, $version);
199 return $files->{$mod} if defined $files->{$mod};
203 ####################################
211 if(-f
'.deps.cache') {
212 # this may fail due to mismatching hosts systems
214 $data = lock_retrieve
('.deps.cache') or die("cannot read .deps.cache: $!");
218 # remove all tarballs from cache which no longer exist
219 for my $tf (keys %{$data->{'deps'}}) {
221 delete $data->{'deps'}->{$tf};
222 for my $key (keys %{$data->{'files'}}) {
223 delete $data->{'files'}->{$key} if $data->{'files'}->{$key} eq $tf;
230 chdir("src") or die("cannot change to src dir");
231 my @tarballs = glob("*.tgz *.tar.gz *.zip");
233 %deps_cache = %{$data->{'deps'}} if defined $data->{'deps'};
234 %deps_files = %{$data->{'files'}} if defined $data->{'files'};
235 for my $tarball (@tarballs) {
236 BuildHelper
::get_deps
($tarball, undef, $quiet) unless defined $deps_cache{$tarball};
239 chdir($cwd) or die("cannot change dir back");
241 lock_store
({'files' => \
%deps_files, 'deps' => \
%deps_cache}, '.deps.cache');
243 return(\
%deps_cache, \
%deps_files);
246 ####################################
247 sub sort_by_dependency
{
252 # ExtUtils-MakeMaker has to go first
253 for my $m (sort keys %{$modules}) {
254 if($m =~ m/^ExtUtils\-MakeMaker\-\d+/) {
256 delete $modules->{$m};
260 my $num = scalar keys %{$modules};
262 for my $m (sort keys %{$modules}) {
263 for my $s (sort keys %{$modules->{$m}}) {
264 if(grep {/$s/} @sorted) {
265 delete $modules->{$m}->{$s};
268 if(scalar keys %{$modules->{$m}} == 0) {
270 delete $modules->{$m};
273 my $new = scalar keys %{$modules};
275 print Dumper
$modules;
276 die("circular dependency");
284 ####################################
285 # compare two version strings
286 # return 1 if $v1 >= $v2
287 sub version_compare
{
289 return 0 if !defined $v1 or $v1 eq 'undef';
290 return 1 if !defined $v2 or $v2 eq 'undef';
291 $v1 =~ s/^(\d+\.\d+).*$/$1/;
292 $v2 =~ s/^(\d+\.\d+).*$/$1/;
293 return 1 if $v1 >= $v2;
297 ####################################
303 # 1st clean up and resolve modules to files
305 for my $file (keys %{$deps}) {
306 $modules->{$file} = {};
307 for my $dep (keys %{$deps->{$file}}) {
308 next if $dep eq 'perl';
309 next if $dep eq 'strict';
310 next if $dep eq 'warnings';
311 next if $dep eq 'lib';
313 next if $dep eq 'IPC::Open'; # core module but not recognized
314 my $cv = is_core_module
($dep);
315 my $dv = $deps->{$file}->{$dep};
316 # next when dependency is a core module and we require version 0
317 next if $dv == 0 and defined $cv;
319 next if version_compare
($cv, $dv);
321 my $fdep = module_to_file
($dep, $files, $deps->{$file}->{$dep});
323 next if $fdep eq $file;
324 $modules->{$file}->{$fdep} = 1
326 if($dep !~ m/^Test::/
327 and !defined is_core_module
($dep)) {
328 warn("cannot resolve dependency '$dep' to file, referenced by: $file\n");
334 my @sorted = sort_by_dependency
($modules);
339 ####################################
341 sub get_url_for_module
{
344 $mod = translate_module_name
($mod);
345 return $url_cache{$mod} if exists $url_cache{$mod};
346 for my $url ('http://search.cpan.org/perldoc?'.$mod, 'http://search.cpan.org/dist/'.$mod) {
347 my $out = BuildHelper
::cmd
("wget --retry-connrefused -O - '".$url."'", 1);
348 if($out =~ m/href="(\/CPAN\
/authors\/id\
/.*?\/.*?
(\
.tar\
.gz
|\
.tgz
|\
.zip
))">/) {
349 $url_cache{$mod} = $1;
353 print "cannot find
$mod on cpan
\n";
357 ####################################
361 my $PERL = shift || '/usr/bin/perl';
362 my $verbose = shift || 0;
364 my $max = shift || 1;
365 die("error
: $file does
not exist
in ".`pwd`) unless -e $file;
366 die("error
: module name missing
") unless defined $file;
368 my $LOG = "install
.log";
369 printf("*** (%3s/%s) ", $x, $max);
370 printf("%-55s
", $file);
372 my($modname, $modvers) = file_to_module($file);
374 if( $modname eq "DBD
::Oracle
") {
375 if(defined $ENV{'ORACLE_HOME'} and -f $ENV{'ORACLE_HOME'}."/lib/libclntsh
.so
") {
376 $ENV{'LD_LIBRARY_PATH'} = $ENV{'ORACLE_HOME'}."/lib
";
377 $ENV{'PATH'} = $ENV{'PATH'}.":".$ENV{'ORACLE_HOME'}."/bin
";
384 my $core = BuildHelper::is_core_module($modname);
385 if(BuildHelper::version_compare($core, $modvers)) {
386 print "skipped core module
$core >= $modvers\n";
391 `grep $file $TARGET/modlist.txt 2>&1`;
392 $installed = 1 if $? == 0;
393 if( $installed and $modname ne 'Catalyst::Runtime' ) {
394 print "already installed
\n";
399 my $dir = BuildHelper::unpack($file);
403 print "installing
... ";
405 my $makefile_opts = '';
406 if($modname eq 'XML::LibXML') {
407 $makefile_opts = 'FORCE=1';
411 local $SIG{ALRM} = sub { die "timeout on
: $file\n" };
412 alarm(120); # single module should not take longer than 1 minute
413 if( -f "Build
.PL
" ) {
414 `$PERL Build.PL >> $LOG 2>&1 && ./Build >> $LOG 2>&1 && ./Build install >> $LOG 2>&1`;
415 if($? != 0 ) { die("error
: rc
$?
\n".`cat $LOG`."\n"); }
416 } elsif( -f "Makefile
.PL
" ) {
417 `sed -i -e 's/auto_install;//g' Makefile.PL`;
419 # retry because sometimes Makefile.PL will be rebuild due to broken time
420 for my $retry (1..3) {
421 `echo "\n\n\n" | $PERL Makefile.PL $makefile_opts >> $LOG 2>&1 && make -j 5 >> $LOG 2>&1 && make install >> $LOG 2>&1`;
425 if($rc != 0 ) { die("error
: rc
$rc\n".`cat $LOG`."\n"); }
427 die("error
: no Build
.PL
or Makefile
.PL found
in $file!\n");
429 `grep '^==> Auto-install the' $LOG >/dev/null 2>&1 | grep -v optional`;
430 if($? == 0 ) { die("dependency error
: rc
$?
\n".`cat $LOG`."\n"); }
436 `echo $file >> $TARGET/modlist.txt`;
440 my $duration = $end - $start;
441 print "ok
(".$duration."s
)\n";
442 my $grepv = "grep -v
'Test::' | grep -v
'Linux::Inotify2' | grep -v
'IO::KQueue' | grep -v
'prerequisite Carp' | grep -v ExtUtils
::Install
";
443 system("grep 'Warning: prerequisite' $LOG | $grepv"); # or die('dependency error');
444 system("grep 'is not installed' $LOG | grep ' ! ' | $grepv"); # or die('dependency error');
445 system("grep 'is installed, but we need version' $LOG | grep ' ! ' | $grepv"); # or die('dependency error');
446 system("grep 'is not a known MakeMaker parameter' $LOG | grep INSTALL_BASE
| $grepv") or die('build error');
449 chomp(my $pwd = `pwd`);
450 print "installation took to long
, see
$pwd/$dir/$LOG for details
\n";
455 # makes Module::Build 1000 times faster
456 if($modname eq 'JSON::XS') {
457 $ENV{'PERL_JSON_BACKEND'} = 'JSON::XS';
459 if($modname eq 'YAML::LibYAML') {
460 $ENV{'PERL_YAML_BACKEND'} = 'YAML::XS';
466 ####################################
467 # return meta content from unpacked package
468 sub get_meta_for_dir {
476 BuildHelper::cmd("yes n
| perl Makefile
.PL
", 1) if -e 'Makefile.PL';
477 BuildHelper::cmd("yes n
| perl Build
.PL
", 1) if -e 'Build.PL';
484 if(-s "$dir/MYMETA
.json
") {
487 $meta = JSON::from_json(`cat $dir/MYMETA.json | tr '\n' ' '`);
489 print Dumper $@ if $@;
491 elsif(-s "$dir/MYMETA
.yml
") {
493 $meta = YAML::LoadFile("$dir/META
.yml
");
495 print Dumper $@ if $@;
497 elsif(-s "$dir/META
.json
") {
500 $meta = JSON::from_json(`cat $dir/META.json | tr '\n' ' '`);
502 print Dumper $@ if $@;
504 elsif(-s "$dir/META
.yml
") {
507 $meta = YAML::LoadFile("$dir/META
.yml
");
509 print Dumper $@ if $@;
511 $meta->{requires} = {} unless defined $meta->{requires};
512 if(-s "$dir/Makefile
.PL
") {
513 my $content = `cat $dir/Makefile.PL`;
514 if($content =~ m/WriteMakefile\s*\(/) {
515 if($content =~ m/'PREREQ_PM'\s*=>\s*\{(.*?)}/) {
517 $mod_str =~ s/\n/ /g;
518 my %modules = $mod_str =~ m/\'(.*?)'\s*=>\s*\'(.*?)\'/;
519 %{$meta->{requires}} = (%{$meta->{requires}}, %modules);
522 if($content =~ m/^\s*requires\s+/m) {
523 my %modules = $content =~ m/^\s*requires\s+(.*?)\s*=>\s*(.*?);/gm;
524 %{$meta->{requires}} = (%{$meta->{requires}}, %modules);
526 if($content =~ m/^\s*use\s+[a-zA-Z:]+\s*/m) {
527 my %modules = $content =~ m/^\s*use\s+([a-zA-Z:]+)\s*([\d\.]+)/gm;
528 delete $modules{'inc::Module::Install'};
529 delete $modules{'inc::Module::Install::DSL'};
530 %{$meta->{requires}} = (%{$meta->{requires}}, %modules);
534 # add deps from the Makefile
535 if(-s "$dir/Makefile
") {
536 my $prereq = `grep PREREQ_PM $dir/Makefile`;
538 $prereq =~ s/^#\s+PREREQ_PM\s*=>\s*({.*}).*$/\$req = $1;/g;
539 $prereq =~ s/([\w:]+)=/'$1'=/g;
543 for my $mod (keys %{$req}) {
544 $meta->{requires}->{$mod} = $req->{$mod};
549 $meta->{requires} = {} unless defined $meta->{requires};
550 $meta->{build_requires} = {} unless defined $meta->{build_requires};
551 $meta->{configure_requires} = {} unless defined $meta->{configure_requires};
552 $meta->{prereqs}->{'build'}->{'requires'} = {} unless defined $meta->{prereqs}->{'build'}->{'requires'};
553 $meta->{prereqs}->{'configure'}->{'requires'} = {} unless defined $meta->{prereqs}->{'configure'}->{'requires'};
554 $meta->{prereqs}->{'runtime'}->{'requires'} = {} unless defined $meta->{prereqs}->{'runtime'}->{'requires'};
556 $meta->{requires}->{'Module::Build'} = 1 if -s $dir.'/Build.PL';
558 if(!defined $meta->{name}) {
559 $meta->{name} = $dir;
560 $meta->{name} =~ s/^(.*)\-.*?$/$1/;
561 $meta->{name} =~ s/\-/::/g;
567 ####################################
568 # return dependencies from meta data
569 sub get_deps_from_meta {
570 my($meta, $all) = @_;
571 my %deps = (%{$meta->{requires}},
572 %{$meta->{build_requires}},
573 %{$meta->{configure_requires}},
574 %{$meta->{prereqs}->{'build'}->{'requires'}},
575 %{$meta->{prereqs}->{'configure'}->{'requires'}},
576 %{$meta->{prereqs}->{'runtime'}->{'requires'}},
578 my $stripped_deps = {};
579 for my $dep (keys %deps) {
580 my $val = $deps{$dep};
581 $dep =~ s/('|")//gmx;
582 $val =~ s/('|")//gmx;
583 next if $dep eq 'perl';
584 next if $dep eq 'warnings';
585 next if $dep eq 'strict';
586 next if $dep eq 'lib';
589 next if $dep eq 'IPC::Open'; # core module but not recognized
590 next if $dep =~ m/^Test::/;
591 next if $dep eq 'Test';
593 $stripped_deps->{$dep} = $val;
595 return $stripped_deps;
598 ####################################
599 # add additional dependencies
600 sub add_additional_deps {
601 my($file, $deps) = @_;
602 my($modname, $modvers) = file_to_module($file);
603 if($additional_deps->{$modname}) {
604 for my $key (keys %{$additional_deps->{$modname}}) {
605 $deps->{$key} = $additional_deps->{$modname}->{$key};
611 ####################################
612 # return dependencies from meta data
615 if($file =~ m/\.zip$/gmx) {
616 BuildHelper::cmd("unzip
$file");
618 BuildHelper::cmd("tar zxf
$file");
621 $dir =~ s/(\.tar\.gz|\.tgz|\.zip)//g;
627 ####################################
628 # find orphaned packages
630 my($deps, $files, $verbose) = @_;
632 for my $file (keys %{$deps}) {
633 # verify this module is used somewhere
635 for my $file2 (keys %{$deps}) {
636 next if $file eq $file2;
637 for my $dep2 (keys %{$deps->{$file2}}) {
638 next if $dep2 eq 'perl';
639 next if $dep2 eq 'strict';
640 next if $dep2 eq 'warnings';
641 next if $dep2 eq 'lib';
642 next if $dep2 eq 'v';
643 my $fdep2 = BuildHelper::module_to_file($dep2, $files, $deps->{$file2}->{$dep2});
645 $found = 1 if $fdep2 eq $file;
646 if($found && $verbose) { print "$file is used by
$file2\n"; }
651 $orphaned->{$file} = 1 unless $found;
656 ####################################
660 if($in =~ m/Makefile\.PL$/mx) {
662 $dir =~ s/Makefile\.PL$//gmx;
663 $meta = BuildHelper::get_meta_for_dir($dir);
665 elsif($in =~ m/(\.tar.gz|\.tgz|\.zip)$/mx) {
666 $meta = get_meta_for_tarball($in);
669 die("unsupported file
: ".$in);
674 ####################################
675 sub get_meta_for_tarball {
680 my $dir = BuildHelper::unpack($file);
681 my $meta = BuildHelper::get_meta_for_dir($dir);
683 for my $f (split/\n/,`find -name \*.pm; find -name \*.PL`) {
684 next if $f =~ m|/inc/|mxo; # skip inc modules included by Module::Install packaged modules
685 open(my $fh, '<', $f);
686 while(my $line = <$fh>) {
687 if($line =~ m/^package\s+(.*?)(\s|;|#)/) {
688 $deps_files{$1} = $file;
694 BuildHelper::cmd("rm
-fr
$dir");