+unset command_not_found_handle
[nethome.git] / bin / hammock
1 #! /usr/bin/perl
2
3 use strict;
4 use warnings;
5 use POSIX qw(&strftime);
6 use Getopt::Long qw(:config no_ignore_case);
7 use Carp qw(&carp);
8 my $start=time();
9
10 my $gdbcvsmaster=$ENV{"HOME"}."/redhat/gdb-cvs-master";
11 my $binutilscvsmaster=$ENV{"HOME"}."/redhat/binutils-cvs-master";
12 my $archermaster=$ENV{"HOME"}."/redhat/gdb-master";
13 my $fedoragitroot=q{git://pkgs.fedoraproject.org};
14 my $rhelgitroot=q{git://pkgs.devel.redhat.com/rpms};
15 my $arch_i386=qr{(?:x86|i\d86|ia32)}io;
16 my $arch_x86_64=qr{(?:x8664|x86_64|em64t)}io;
17 my $resultdir=$ENV{"HOME"}."/.hammock-result";
18 my @arches=qw(i386 x86_64);
19
20 my $error=0;
21 sub error
22 {
23   carp @_;
24   $error++;
25 }
26
27 my $userid;
28 my $force;
29 my $parallel=1;
30 my @distro;
31 my @componentdistro;
32 my @path;
33 my @arch;
34 my $component;
35 my $srcrpm;
36 my @file;
37 my @target;
38 my $configure;
39 my $branch;
40 # FIXME: Connect make paralellization to the children.
41 my $distrojobs;
42 my $gdbcvspie;
43 my $strip;
44 # FIXME: 20100911servpatched-f14:
45 # readchar: Connection reset by peer
46 # Remote side has terminated connection.  GDBserver will reopen the connection.
47 # Can't bind address: Address already in use.
48 # <last 2 lines repearing>
49 my $gdbserver;
50 my $valgrind;
51 my $bfd32;
52 my $gdbindex;
53 my $dwarf;
54 # Use --dwarf=40 for: --dwarf=4 -fno-debug-types-section
55 # Use --dwarf=41 for: --dwarf=4    -fdebug-types-section
56 my $stabs;
57 my $debug_types_section;
58 my $orphanripper=1;
59 my $options;
60 die if !GetOptions(
61   "i|userid=s"=>\$userid,
62     "force"=>\$force,
63     "serial"=>sub { $parallel=0; },
64     "parallel"=>sub { $parallel=1; },
65   "d|distro=s{,}"=>\@distro,
66     "cd|componentdistro=s{,}"=>\@componentdistro,
67   "p|path=s{,}"=>\@path,
68   "a|arch=s{,}"=>\@arch,
69   "c|component=s"=>\$component,
70   "s|srcrpm=s"=>\$srcrpm,
71     "file=s{,}"=>\@file,
72     "target=s{,}"=>\@target,
73   "D|distrojobs=s"=>\$distrojobs,
74     "configure=s"=>\$configure,
75     "branch=s"=>\$branch,
76     "gdbcvspie"=>\$gdbcvspie,
77     "strip"=>\$strip,
78     "gdbserver"=>\$gdbserver,
79     "valgrind"=>\$valgrind,
80     "bfd32"=>\$bfd32,
81     "gdbindex"=>\$gdbindex,
82     "dwarf=i"=>\$dwarf,
83     "stabs=i"=>\$stabs,
84     "orphanripper!"=>\$orphanripper,
85     "options=s"=>\$options,
86 );
87 $component and ($component=~m{^(?:(?:fedora|rhel)(?:gdb|binutils|glibc)|gdbcvs|binutilscvs|archer-.*|/home/.*)$} or die "-c|--component required to be: fedoragdb|rhelgdb|fedorabinutils|rhelbinutils|fedoraglibc|rhelglibc|gdbcvs|binutilscvs|archer-*|/home/*");
88 $component and $component=~m{^/home/} and (-f "$component/gdb/gdbtypes.c" or die "$component/gdb/gdbtypes.c not found");
89 $component and $srcrpm and die "-c|--component excludes -s|--srcrpm";
90 $component or $srcrpm or die "-c|--component or -s|--srcrpm required";
91 $component||="";  # Make `eq' not complaining.
92 my $cvsbranch;
93 if ($component=~/^(gdb|binutils)cvs$/) {
94   $cvsbranch=(!$branch?"-A":"-r $branch");
95 } else {
96   die "--branch currently unsupported for non-CVS sources" if $branch;
97 }
98 !$gdbcvspie or $component eq "gdbcvs" or die "--gdbcvspie requires -c gdbcvs";
99 @target and ($srcrpm or $component=~/^(?:fedora|rhel)/) and die "--target is available only for baretestsuite modes";
100 $strip and ($srcrpm or $component=~/^(?:fedora|rhel)/) and die "--strip is available only for baretestsuite modes";
101 $options and ($srcrpm or $component=~/^(?:fedora|rhel)/) and die "--options is available only for baretestsuite modes";
102 $gdbserver and $component!~/^(?:gdbcvs|archer-.*)$/ and die "--gdbserver is available only for gdbcvs or archer-*";
103 $valgrind and $component!~/^(?:gdbcvs|archer-.*)$/ and die "--valgrind is available only for gdbcvs or archer-*";
104 $gdbserver and $valgrind and die "--gdbserver and --valgrind are mutually exclusive";
105 ($gdbserver || $valgrind) and $gdbindex and die "--gdbserver|--valgrind and --gdbindex are mutually exclusive";
106 do { $debug_types_section=0; $dwarf=4; } if ($dwarf||0)==40;
107 do { $debug_types_section=1; $dwarf=4; } if ($dwarf||0)==41;
108 !defined $dwarf or ($dwarf>=2 && $dwarf<=4) or die "--dwarf requires DWARF version number";
109 !defined $stabs or ($stabs>=0 && $stabs<=2) or die "--stabs is 0=off or 1=-gstabs or 2=-gstabs+";
110 ($gdbserver || $valgrind || $gdbindex) and ($dwarf || $stabs) and die "--gdbserver|--valgrind|--gdbindex and --dwarf|--stabs are mutually exclusive";
111 $orphanripper=($orphanripper?"orphanripper":"");
112 my %target;
113 do { error "Duplicate target: $_\n" if $target{$_}++; } for @target;
114 @arch="x86_64" if @target&&!@arch;
115 for my $file (@file) {
116   -f $file and -r $file or error "-f|--file $file not readable: $!";
117 }
118 push @file,"/home/jkratoch/redhat/fedora/gdb/master/gdb-index-assert.patch" if $component=~/^(?:gdbcvs|archer-)/;
119 push @file,"/home/jkratoch/redhat/fedora/gdb/master/gdb-dejagnu-go.patch"   if $component=~/^(?:gdbcvs|archer-)/;
120 $distrojobs=(@target?1:2) if !defined $distrojobs;
121 $distrojobs=~/^\d+$/ or die "-D|distrojobs must be a number: $distrojobs";
122 $distrojobs>=1 or die "-D|distrojobs must be positive: $distrojobs";
123 error "Excessive arguments: @ARGV" if @ARGV;
124 @arch=@arches if !@arch;
125 my $path=join(":",@path) if @path;
126 @componentdistro and @componentdistro!=@distro and die "--cd|--componentdistro must have the same elements count as -d|--distro";
127
128 ###unshift @file,"/home/jkratoch/t/gdbservergnulib.patch" if $component eq "gdbcvs";
129
130 sub distro_normalize($;$)
131 {
132   my($name,$force)=@_;
133   local $_=$name;
134
135   s{^/var/lib/mock/+}{};
136   s{/+$}{};
137   s/^.*$/\L$&/s;
138   s/^(?:devel|rawhide)\b/fedora-rawhide/;
139   s/^(?:epel|centos)-?(\d)/epel-$1/;
140   s/^(?:rhel)-?(\d)/rhel-$1/;
141   s/^(?:f|fedora)-?(\d)/fedora-$1/;
142   my @archuse;
143   @archuse="" if -d "/var/lib/mock/$_";
144   @archuse="i386" if s/-$arch_i386$//o;
145   @archuse="x86_64" if s/-$arch_x86_64$//o;
146
147   for my $arch (@archuse?@archuse:@arch) {
148     my $dir="/var/lib/mock/$_".(!$arch?"":"-$arch");
149     $force or -d $dir or error "No distro: $dir";
150   }
151
152   return ($_,@archuse);
153 }
154
155 # epel-\d-i386|fedora-\d-i386|fedora-rawhide-i386
156 my @distrouse;
157 for my $distroi (0..$#distro) {
158   my $distro=$distro[$distroi];
159   my $componentdistro=$componentdistro[$distroi];
160   my @archuse;
161   if ($componentdistro) {
162     my $force=($componentdistro=~s/!$//);
163     ($componentdistro,@archuse)=distro_normalize $componentdistro,$force;
164     @archuse and die "--cd|--componentdistro must have no arch: ".join(" ",@archuse);
165   }
166   ($distro,@archuse)=distro_normalize $distro;
167   $componentdistro||=$distro;
168   @archuse=@arch if !@archuse;
169   for my $archuse (@archuse) {
170     for my $target (@target?@target:undef()) {
171       push @distrouse,{"distro"=>$distro.(!$archuse?"":"-$archuse"),"componentdistro"=>$componentdistro,"target"=>$target};
172     }
173   }
174 }
175
176 # "-p", "mayexist"
177 sub newdir($;@)
178 {
179   my($dir,@opt)=@_;
180
181   my %opt=map(($_=>1),@opt);
182   warn "+ mkdir".($opt{"-p"} ? " -p" : "")." $dir\n";
183   mkdir $dir or ($opt{"mayexist"} && $!{EEXIST}) or die "mkdir $dir: $!";
184 }
185
186 my $log;
187 # "bare"
188 sub spawn($;%)
189 {
190   my($cmd,@opt)=@_;
191
192   my %opt=map(($_=>1),@opt);
193   my $ok;
194   if (!$opt{"bare"}) {
195     $cmd="set -ex; $cmd";
196     $ok="$log.ok" if $log;
197     $cmd="($cmd; touch $ok) 2>&1|tee -a $log; test -f $ok" if $log;
198     unlink $ok if $ok;
199   } else {
200     warn "+ $cmd\n";
201   }
202   # warn "+ $cmd\n";
203   system $cmd and die "$cmd: $!";
204   unlink $ok if $ok;
205 }
206
207 my $basedir=$ENV{"HOME"}."/hammock";
208 newdir $basedir,"mayexist";
209 my $idbase=strftime("%Y%m%d",localtime());
210 my $id;
211 my $dir;
212 for my $seq (defined $userid ? $userid : (0..99)) {
213   $id=$idbase.(defined $userid ? $seq : sprintf("%02d",$seq));
214   $dir="$basedir/$id";
215   last if ! -e $dir;
216 }
217 spawn "chmod -R u+w $dir; rm -rf $dir" if -d $dir && $force && defined $userid;
218 error "Directory not free: $dir" if !$id || !$dir || -e $dir;
219 print STDERR "ID = $id | dir = $dir\n";
220 error "No distros specified" if !@distrouse;
221 die "$error errors seen, aborted" if $error;
222
223 sub writefile
224 {
225   my($fname,$content)=@_;
226
227   local *F;
228   open F,">$fname" or die $fname;
229   print F $content or die $fname;
230   close F or die $fname;
231 }
232
233 sub readfile
234 {
235   my($fname)=@_;
236
237   local *F;
238   open F,"$fname" or die $fname;
239   local $/=undef();
240   defined(my $r=<F>) or die $fname;
241   close F or die $fname;
242   return $r;
243 }
244
245 sub ln($$)
246 {
247   my($old,$new)=@_;
248   link $old,$new or spawn "cp -p '$old' '$new'";
249 }
250
251 # /etc/cgconfig.conf
252 spawn "cgclassify -g '*':hammock $$ || :";
253
254 spawn "renice +19 -p $$";
255 spawn "ionice -c3 -p $$";
256 newdir $dir;
257 $log="$dir/log";
258 my $resultid="$resultdir/$id";
259 my $resultidxz="$resultid.tar.xz";
260 newdir $resultdir,"mayexist";
261 spawn "rm -rf $resultid" if -d $resultid && $force;
262 newdir $resultid;
263 unlink $resultidxz or $!{ENOENT} or die "unlink $resultidxz: $!";
264 spawn "uname -r >$dir/kernel";
265 my %dump=(
266   "path"=>$path,
267   "component"=>$component,
268   "branch"=>$branch,
269   "srcrpm"=>$srcrpm,
270   "file"=>join("\n",@file),
271   "configure"=>$configure,
272   "gdbcvspie"=>$gdbcvspie,
273   "gdbserver"=>$gdbserver,
274   "valgrind"=>$valgrind,
275   "bfd32"=>$bfd32,
276   "gdbindex"=>$gdbindex,
277   "dwarf"=>$dwarf,
278   "stabs"=>$stabs,
279   "debug_types_section"=>$debug_types_section,
280   "orphanripper"=>$orphanripper,
281   "options"=>$options,
282 );
283 while (my($name,$val)=each(%dump)) {
284   next if !$val;
285   writefile "$dir/$name","$val\n";
286   ln "$dir/$name","$resultid/$name";
287 }
288 for my $file (@file) {
289   newdir "$dir/file.d","mayexist";
290   (my $base=$file)=~s{^.*/}{};
291   my $d="$dir/file.d/$base";
292   ln $file,$d;
293   newdir "$resultid/file.d","mayexist";
294   ln $d,"$resultid/file.d/$base";
295 }
296
297 sub subst
298 {
299   my($sub,$in,$out)=@_;
300
301   $out||=$in;
302
303   local *F;
304   open F,$in or die $in;
305   local $_=do { local $/; <F>; } or die $in;
306   close F or die $in;
307
308   &{$sub}() or die $_."\nError substituting $in";
309
310   writefile $out,$_;
311 }
312
313 sub copyfiles($)
314 {
315   my($targetdir)=@_;
316
317   for my $file (@file) {
318     my $filebase=$file;
319     $filebase=~s{^.*/}{};
320     my $target="$targetdir/$filebase";
321     # Some *.patch files may be new.
322     # -f $target or die "File $file does not exist at $target";
323     spawn "rm -f $target; cp -p $file $target";
324   }
325 }
326
327 # PID->distro
328 my %child;
329 while (@distrouse || keys(%child)) {
330   while (keys(%child)<$distrojobs && @distrouse) {
331     my $distrouse=shift @distrouse;
332     my $distro=$distrouse->{"distro"};
333     my $componentdistro=$distrouse->{"componentdistro"};
334     my $target=$distrouse->{"target"};
335     my $rpmbuild="rpmbuild";
336
337     my $cvsbasedir;
338     my $gitbranch;
339     my $gitpkg;
340     my $cvsroot;
341     my $gitroot;
342     my $cvsrepo;
343     my $gitrepo;
344     if ($component=~/^fedora(.*)$/) {
345       $gitrepo=$1;
346       $gitbranch="f$1" if $componentdistro=~/^fedora-(\d+)/;
347       $gitbranch="master" if $componentdistro=~/^fedora-rawhide/;
348       die "$component vs. $componentdistro" if !$gitbranch;
349       $gitroot=$fedoragitroot;
350       $gitpkg="fedpkg";
351     }
352     if ($component=~/^rhel(.*)$/) {
353       $gitrepo=$1;
354       $gitbranch="auto";
355       $gitroot=$rhelgitroot;
356       $gitpkg="https_proxy= rhpkg";
357       # EPEL still uses Berkeley DB version 8 while F-11+ (F-10?) uses version 9.
358       # Using db_dump and db_load would no longer make it mock compatible.
359       $rpmbuild.=q{ --dbpath $PWD --nodeps};
360     }
361
362     my $distrodirbase=$distro;
363     $distrodirbase.="-$target" if $target;
364     my $distrodir="$dir/$distrodirbase";
365     newdir $distrodir;
366     $log="$distrodir/log";
367     my $out="$distrodir/out";
368     newdir $out;
369
370     if ($parallel) {
371       my $pid=fork();
372       die if !defined $pid;
373       if ($pid) {
374         $child{$pid}=$distrodirbase;
375         next;
376       }
377     }
378
379     my $builddir="$distrodir/build";
380     newdir $builddir;
381
382     # Do not use mockrun as the rpm database may be in a different version.
383     spawn "rpm -r /var/lib/mock/$distro/root -qa|sort >$out/rpm-qa";
384
385     $::distro=$distro;
386     sub mockrun($)
387     {
388       my($c)=@_;
389
390       $c="export PATH=\"$path:\$PATH\"; $c" if $path;
391       $c="export PATH=\"\$HOME/bin:\$PATH\"; $c";
392       $c="export MAKEFLAGS=\"-j\$[`getconf _NPROCESSORS_ONLN`*3/2]\"; $c";
393       $c="export http_proxy=http://127.0.0.1:3128/; $c";
394       $c="set -ex; cd $builddir; $c";
395       die "found ': $c" if $c=~/'/;
396       spawn "mockrun $::distro '$c'";
397     }
398
399     if ($gitbranch) {
400       die if !$gitroot;
401       die if !$gitrepo;
402       die if !$gitbranch;
403       die if !$gitpkg;
404       spawn "cd $distrodir; git clone ".($gitbranch eq "auto"?"":"-b $gitbranch")." $gitroot/$gitrepo $component";
405       my $componentdir="$distrodir/$component";
406       -d $componentdir or die "Failed checkout to: $componentdir";
407       if ($gitbranch eq "auto") {
408         $componentdistro=~/^(?:rhel|epel)-(\d+)$/ or die $componentdistro;
409         my $major=$1;
410         my @l=grep { m{^\s*origin/rhel-$major\.\d+\s*$} } split /\n/,readfile "cd $componentdir; git branch -r |";
411         sub minor
412         {
413           local $_=$_[0];
414           s{^\s*origin/rhel-\d+\.(\d+)\s*$}{$1} or die;
415           return $_;
416         }
417         @l=sort { minor($b) <=> minor($a); } @l;
418         $gitbranch=($l[0]=~m{^\s*origin/(.*?)\s*$})[0] or die;
419         spawn "cd $componentdir; git checkout $gitbranch";
420       }
421       copyfiles $componentdir;
422       spawn "cd $componentdir; $gitpkg verrel >$out/verrel";
423       my $glob="$componentdir/*.src.rpm";
424       @{[glob $glob]}==0 or die "Found some before test-srpm: $glob";
425       # No `spawn' as we could get:
426       # error: unpacking of archive failed on file X;4a56efef: cpio: MD5 sum mismatch
427       mockrun "cd $componentdir; $gitpkg srpm";
428       my @srcrpm=(glob $glob);
429       @srcrpm==1 or die "Did not find 1 srcrpm: @srcrpm";
430       $srcrpm=$srcrpm[0];
431     }
432
433     if ($cvsbasedir) {
434       die if !$cvsroot;
435       die if !$cvsrepo;
436       die if !$cvsbasedir;
437       spawn "cd $distrodir; cvs -q -z3 -d $cvsroot co rpms/$cvsrepo/$cvsbasedir";
438       my $componentdir="$distrodir/rpms/$cvsrepo/$cvsbasedir";
439       -d $componentdir or die "Failed checkout to: $componentdir";
440       # Required for RHEL; Fedora does so automatically.
441       spawn "cd $componentdir/..; cvs -q -z3 -d $cvsroot co common";
442       # Workaround (RHEL-5?) curl which uses `Pragma: nocache' on $http_proxy.
443       subst sub { s{echo "curl }{$&-H 'Pragma: cache' }; },"$componentdir/../common/Makefile.common";
444       copyfiles $componentdir;
445       spawn "cd $componentdir; make verrel >$out/verrel";
446       my $glob="$componentdir/*.src.rpm";
447       @{[glob $glob]}==0 or die "Found some before test-srpm: $glob";
448       # No `spawn' as we could get:
449       # error: unpacking of archive failed on file X;4a56efef: cpio: MD5 sum mismatch
450       mockrun "cd $componentdir; make test-srpm";
451       my @srcrpm=(glob $glob);
452       @srcrpm==1 or die "Did not find 1 srcrpm: @srcrpm";
453       $srcrpm=$srcrpm[0];
454     }
455
456     if ($srcrpm) {
457       my $srcrpmbasename=$srcrpm;
458       $srcrpmbasename=~s{^.*/}{};
459       spawn "cp -p $srcrpm $builddir/$srcrpmbasename";
460
461       my $rpmbuildlocal=$rpmbuild.q{ --define "_topdir $PWD" --define "_builddir $PWD" --define "_rpmdir $PWD" --define "_sourcedir $PWD" --define "_specdir $PWD" --define "_srcrpmdir $PWD" --define "_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm"};
462       $rpmbuildlocal="$orphanripper $rpmbuildlocal" if $cvsrepo && $cvsrepo eq "glibc";
463       mockrun $rpmbuildlocal." --rebuild --with testsuite $srcrpmbasename";
464     }
465
466     my $baretestsuite;
467
468     if ($component=~/^(gdb|binutils)cvs$/) {
469       my $which=$1;
470       my $cvsmaster=$which eq "gdb" ? $gdbcvsmaster : $binutilscvsmaster;
471       if (-d $cvsmaster) {
472         spawn "cp -a $cvsmaster $builddir/src; cd $builddir/src; cvs update $cvsbranch";
473       } else {
474         spawn "cd $builddir; cvs -q -z3 -d :pserver:anoncvs:\@sourceware.org:/cvs/src co $cvsbranch $which";
475       }
476       spawn "cd $builddir/src; test -z \"\$(cvs update $cvsbranch)\"";
477       $baretestsuite="$builddir/src";
478     }
479
480     if ($component=~/^archer-/) {
481       spawn "git clone ".(!-d $archermaster ? "" : "--reference $archermaster")." git://sourceware.org/git/archer.git $builddir/$component";
482       spawn "cd $builddir/$component; git fetch";
483       spawn "cd $builddir/$component; git checkout -b $component origin/$component; [ \"`git status`\" = \"# On branch $component\nnothing to commit (working directory clean)\" ]";
484       $baretestsuite="$builddir/$component";
485     }
486
487     if ($component=~m{^/home/}) {
488       spawn "cp -a $component $builddir/src; cd $builddir/src; find -name \"*.[oa]\" -o -name \"*.l[oa]\" -o -name gdb.sum -o -name gdb.log|xargs rm -f; make clean || :";
489       $baretestsuite="$builddir/src";
490     }
491
492     if ($baretestsuite) {
493       for my $file (@file) {
494         my $target="$baretestsuite/$file";
495         if ($file=~m{[.](R?)patch$}) {
496           my $R=$1;
497           my $fileabs=$file;
498           $fileabs=$ENV{"PWD"}."/$fileabs" if $fileabs!~m{^/};
499           spawn "cd $baretestsuite; patch -${R}p1 <$fileabs";
500         } else {
501           -f $target or $file=~m{/testsuite/} or die "File $file does not exist at $target";
502           spawn "rm -f $target; cp -p $file $target";
503         }
504       }
505
506       my $errs12="errs12";
507       $errs12.=" -s" if $strip;
508       $errs12.=" --target=$target" if $target;
509       $errs12.=" $options" if $options;
510       $errs12.=" $configure" if $configure;
511
512       if ($component eq "binutilscvs") {
513         mockrun "cd $baretestsuite; $errs12; ulimit -c unlimited; $orphanripper make -k check || :; ".q{for file in {gas/testsuite/gas,ld/ld,binutils/binutils}.{sum,log};do ln $file}." $out/binutils-$distro-".q{$(basename $file) || :; done;};
514       } else {
515         if ($valgrind) {
516           # FIXME
517           do { unlink $_ or warn "$_: $!"; } for "$baretestsuite/gdb/testsuite/gdb.base/break-interp.exp";
518         }
519         my @check=($distro=~/-x86_64/ ? qw(-m64 -m32) : -m32);
520         @check=map("check//unix/$_",@check);
521         @check=map({($_,"$_/-fPIE/-pie");} @check) if $gdbcvspie;
522         my %flags=("CC_FOR_TARGET"   =>"gcc",
523                    "CXX_FOR_TARGET"  =>"g++",
524                    "GO_FOR_TARGET"   =>"gccgo",
525                    "GO_LD_FOR_TARGET"=>"gccgo",
526                    );
527         # for i in ".join(" ",@check).";do $orphanripper make -k \$i || :;done
528         sub runtestcc($)
529         {
530           my($q0)=@_;
531           (my $q1=$q0)=~s/ /\\ /g;
532           (my $q2=$q1)=~s/ /\\ /g;
533           return ' RUNTESTFLAGS="'.join(' ',map($_.'='.$flags{$_}.'\ '.$q1,keys(%flags))).' GNATMAKE_FOR_TARGET=gnatmake\ --GCC=gcc\\ '.$q2.'"';
534         }
535         mockrun "cd $baretestsuite;"
536                 .(!$valgrind?"":' HAMMOCK_VALGRIND=1')
537                 ." $errs12"
538                 .(!$valgrind?"":' --without-python')    # FIXME: Fix valgrind --suppressions
539                 .(!$bfd32?"":' --disable-64-bit-bfd')
540                 ."; cd gdb; ulimit -c unlimited; "
541                 .(!$gdbserver?"":'DEJAGNU=$HOME/src/runtest-gdbserver/site.exp ')
542                 .(!$valgrind?"":'DEJAGNU=$HOME/src/runtest-valgrind/site.exp ')
543                 ."$orphanripper make -k ".join(" ",@check)
544                 # Ensure serial run if FORCE_PARALLEL=1 is not present
545                 ." RUNTESTFLAGS=DUMMY=dummy"
546                 .(!$gdbserver?"":' RUNTESTFLAGS=--target_board=native-gdbserver')
547                 .(!$valgrind?"":' RUNTESTFLAGS=--target_board=valgrind')
548                 # Missing GNATMAKE_FOR_TARGET!
549                 .(!$gdbindex?"":' RUNTESTFLAGS="'.join(' ',map($_.'=/bin/sh\ $PWD/cc-with-index.sh\ '.$flags{$_},keys(%flags))).'"')
550                 .(!$dwarf?"":runtestcc "-gdwarf-$dwarf".(!defined $debug_types_section?"":'\ '.($debug_types_section?"-fdebug-types-section":"-fno-debug-types-section"))." -g0")
551                 .(!$stabs?"":runtestcc(($stabs==1?"-gstabs":"-gstabs+")." -g0"))
552                 .(!$parallel?"":' FORCE_PARALLEL=1')
553                 ." || :; ".q{for t in sum log;do for file in testsuite*/gdb.$t;do suffix="${file#testsuite.unix.}"; suffix="${suffix%/gdb.$t}"; ln $file}." $out/gdb".q{$suffix.$t || :; done; done;};
554       }
555       # gdbunpack does:
556       #         perl -i -pe 's{\Q'"$HOME"'\E/.*?/build/[^/]*/}{}g' "$base"/*
557       my $HOME=$ENV{"HOME"};
558       for my $file (glob("$out/*.sum"),glob("$out/*.log")) {
559         subst sub { s{\Q$HOME\E/.*?/build/[^/]*/}{}g; },$file;
560       }
561     } else {
562       die "internal error" if @target;
563     }
564
565     # Call gdbunpack only if no direct $out directory will be created.
566     # It is needed only for .src.rpm-built testsuites, no matter how .src.rpm
567     # got created.
568     my @testinlog;
569     if ($component=~/^(?:fedora|rhel)glibc$/) {
570       @testinlog=(qr/={20}TESTING DETAILS={17}/,qr/={20}PLT RELOCS END={18}/);
571     }
572     if (@testinlog) {
573       subst sub { s{^.*?\n($testinlog[0]\n.*\n$testinlog[1]\n).*$}{$1}s; },$log,$out;
574     } elsif ($cvsbasedir || $srcrpm) {
575       # Applies both to gdb and binutils.
576       spawn "mv $out $out.x; gdbunpack $log; mv $out.x/* $out/; rmdir $out.x";
577     }
578
579     if ($valgrind) {
580       for my $from (glob "$out/*.log") {
581         (my $to=$from).="filt";
582         spawn q{sed -n 's/^==[0-9]*== \([A-Z]\)/\1/p' <}.$from.q{|grep -v '^\(Conditional jump \|Invalid read \|Use of uninitialised value \|Syscall param .* uninitialised byte\)' >}.$to;
583       }
584     }
585
586     my $resultout="$resultdir/$id/$distrodirbase";
587     newdir $resultout;
588     $resultout.="/out";
589     newdir $resultout;
590     for my $fname (glob "$out/*") {
591       (my $base=$fname)=~s{^.*/}{};
592       my $d="$resultout/$base";
593       warn "+ link $fname $d\n";
594       ln $fname,$d;
595     }
596
597     exit 0 if $parallel;
598   }
599
600   print STDERR "waiting for ".scalar(keys(%child))." children, ".scalar(@distrouse)." distros to go...\n";
601   my $pid=wait();
602   next if $pid==-1 && $!==10; # 10==No child processes
603   die "wait()==-1: $!" if $pid==-1;
604   die "not found pid $pid" if !$child{$pid};
605   error "weird status $? for pid $pid: ".$child{$pid} if $?;
606   print STDERR "finished: $pid ".$child{$pid}."\n";
607   delete $child{$pid};
608 }
609 die if keys(%child);
610 die if @distrouse;
611
612 spawn "(set -e -o pipefail;cd $resultdir;tar cf - $id|xz -9e >$resultidxz;rm -rf $id)&","bare";
613
614 sub timestr($)
615 {
616   my($sec)=@_;
617   my $r="";
618
619   if ($sec>=60*60) {
620     $r.=int($sec/(60*60))."h";
621     $sec%=60*60;
622   }
623   if ($r || $sec>=60) {
624     $r.=int($sec/60)."m";
625     $sec%=60;
626   }
627   $r.=$sec."s";
628
629   return $r;
630 }
631
632 print STDERR "ID = $id | dir = $dir\n";
633 my $time=timestr(time()-$start);
634 print STDERR "total time=$time\n";
635 writefile "$dir/time","$time\n";
636 die "$error errors seen, aborted" if $error;
637 print STDERR "done\n";