5 Copyright (c) 2003-2010, Andrew Dunstan
7 See accompanying License file for license details
11 ####################################################
15 NAME: run_build - script to run exim buildfarm
19 run_build [option ...] [branchname]
21 AUTHOR: Andrew Dunstan
25 See http://wiki.exim.org/wiki/PostgreSQL_Buildfarm_Howto
29 https://github.com/EximBuildFarm/client-code
33 ###################################################
35 our $VERSION = 'REL_0.1';
41 use Fcntl qw(:flock :seek);
47 use FindBin qw'$RealBin';
49 use POSIX qw(:signal_h strftime);
51 use Cwd qw(abs_path getcwd);
54 # save a copy of the original enviroment for reporting
55 # save it early to reduce the risk of prior mangling
56 use vars qw($orig_env);
61 while (my ($k,$v) = each %ENV)
64 # report all the keys but only values for whitelisted settings
65 # this is to stop leaking of things like passwords
68 $k =~ /^PG(?!PASSWORD)|MAKE|CC|CPP|FLAG|LIBRAR|INCLUDE/
69 ||$k =~/^(HOME|LOGNAME|USER|PATH|SHELL)$/
79 use EximBuild::Options;
80 use EximBuild::WebTxn;
82 if ($0 =~ /(.*)\.pl$/) {
83 die "$0: Please use `@{[join ' ' => $1, @ARGV]}' instead.\n"
90 say "$0: forced umask to 022" if -t;
94 my $orig_dir = getcwd();
96 # make sure we exit nicely on any normal interrupt
97 # so the cleanup handler gets called.
98 # that lets us stop the db if it's running and
99 # remove the inst and exim directories
100 # so the next run can start clean.
102 foreach my $sig (qw(INT TERM HUP QUIT))
104 $SIG{$sig}=\&interrupt_exit;
107 # copy command line before processing - so we can later report it
110 my @invocation_args = (@ARGV);
112 # process the command line
113 EximBuild::Options::fetch_options();
115 die "only one of --from-source and --from-source-clean allowed"
116 if ($from_source && $from_source_clean);
118 die "only one of --skip-steps and --only-steps allowed"
119 if ($skip_steps && $only_steps);
121 $verbose=1 if (defined($verbose) && $verbose==0);
122 $verbose ||= 0; # stop complaints about undefined var in numeric comparison
126 $verbose=1 unless $verbose;
133 use vars qw(%skip_steps %only_steps);
135 if ($skip_steps =~ /\S/)
137 %skip_steps = map {$_ => 1} split(/\s+/,$skip_steps);
140 if ($only_steps =~ /\S/)
142 %only_steps = map {$_ => 1} split(/(\s+|[:,])/,$only_steps);
145 # Currently only specifying a branch is actually used.
146 # Specifying a different repo is just a wishlist item .
147 use vars qw($branch $repo);
148 my ($arg1,$arg2) = (shift,shift);
149 $branch = $arg2 ? $arg2 :
152 $repo = $arg2 ? $arg1 : 'exim';
153 my $explicit_branch = $branch;
155 print_help() if ($help);
158 # process config file
162 #die Dumper \%EximBuild::conf;
165 # Does not seem to be necessary
166 #die "$0: permissions on '$EximBuild::conf{build_root}' should be >= 0775\n"
167 # if -d $EximBuild::conf{build_root}
168 # and ((stat $EximBuild::conf{build_root})[2] & 0775) != 0775;
171 # get the config data into some local variables
173 $buildroot,$target,$animal, $print_success,
174 $aux_path,$trigger_exclude,$trigger_include,$secret,
175 $keep_errs,$force_every, $make, $optional_steps,
176 $use_vpath,$tar_log_cmd, $using_msvc, $extra_config,
177 $make_jobs, $core_file_glob, $global_lock_dir
180 qw(build_root target animal print_success aux_path trigger_exclude
181 trigger_include secret keep_error_builds force_every make optional_steps
182 use_vpath tar_log_cmd using_msvc extra_config make_jobs core_file_glob global_lock_dir)
185 # This should be done more generally, for all the scripts
186 # including the $buildconf. For now this is duplicated
187 # in a similiar war in run_branches.
188 $global_lock_dir //= $buildroot // die "$0: need global_lock_dir\n";
190 die "$0: need read/write permissions on '$global_lock_dir': $!\n"
191 if not -r -w $global_lock_dir;
193 #default is no parallel build
196 # default core file pattern is Linux, which used to be hardcoded
197 $core_file_glob ||= 'core*';
200 if (defined($EximBuild::conf{trigger_filter}))
202 $trigger_exclude = $EximBuild::conf{trigger_filter};
205 my $scm_timeout_secs = $EximBuild::conf{scm_timeout_secs}
206 || $EximBuild::conf{cvs_timeout_secs};
208 print scalar(localtime()),": buildfarm run for $animal:$branch starting\n"
211 # Allow commandline overrides of conf variables
212 foreach my $arg ( @{$EximBuild::Options::overrides} )
214 if (my ($key,$val) = split '=', $arg)
216 $EximBuild::conf{$key} = $val;
217 printf "Commandline override: '$key' = '%s'\n", $EximBuild::conf{$key}
222 if (ref($force_every) eq 'HASH')
224 $force_every = $force_every->{$branch} || $force_every->{default};
227 my $scm = new EximBuild::SCM \%EximBuild::conf;
231 if (exists $EximBuild::conf{base_port})
233 $buildport = $EximBuild::conf{base_port};
234 if ($branch =~ /REL(\d+)_(\d+)/)
236 $buildport += (10 * ($1 - 7)) + $2;
242 # support for legacy config style
243 $buildport = $EximBuild::conf{branch_ports}->{$branch} || 5999;
246 $ENV{EXTRA_REGRESS_OPTS} = "--port=$buildport";
248 $tar_log_cmd ||= "tar -z -cf runlogs.tgz *.log";
250 my $logdirname = "lastrun-logs";
252 if ($from_source || $from_source_clean)
254 $from_source ||= $from_source_clean;
255 die "sourceroot $from_source not absolute"
256 unless $from_source =~ m!^/!;
258 # we need to know where the lock should go, so unless the path
259 # contains HEAD we require it to be specified.
260 die "must specify branch explicitly with from_source"
261 unless ($explicit_branch || $from_source =~ m!/HEAD/!);
266 $logdirname = "fromsource-logs";
270 if ($branch eq 'HEAD' || $branch ge 'REL8_4')
273 # non-C locales are not regression-safe before 8.4
274 @locales = @{$EximBuild::conf{locales}} if exists $EximBuild::conf{locales};
276 unshift(@locales,'C') unless grep {$_ eq "C"} @locales;
279 # several people have run into these
281 if ( `uname -s 2>&1 ` =~ /CYGWIN/i )
283 my @procs = `ps -ef`;
284 die "cygserver not running" unless(grep {/cygserver/} @procs);
287 if ( $ccachedir = $EximBuild::conf{build_env}->{CCACHE_DIR} )
290 # ccache is smart enough to create what you tell it is the cache dir, but
291 # not smart enough to build the whole path. mkpath croaks on error, so
295 $ccachedir = abs_path($ccachedir);
300 die "no aux_path in config file" unless $aux_path;
303 die "cannot run as root/Administrator" unless ($using_msvc or $> > 0);
305 my $devnull = $using_msvc ? "nul" : "/dev/null";
309 $scm->check_access($using_msvc);
312 my $st_prefix = "$animal.";
314 my $exim = $from_source || $scm->get_build_path($use_vpath);
316 # set environment from config
317 while (my ($envkey,$envval) = each %{$EximBuild::conf{build_env}})
319 $ENV{$envkey}=$envval;
322 # change to buildroot for this branch or die
323 die "no buildroot" unless $buildroot;
325 unless ($buildroot =~ m!^/!
326 or($using_msvc and $buildroot =~ m![a-z]:[/\\]!i ))
328 die "buildroot $buildroot not absolute";
331 die "$buildroot does not exist or is not a directory" unless -d $buildroot;
333 chdir $buildroot || die "chdir to $buildroot: $!";
335 mkdir $branch unless -d $branch;
336 chdir $branch || die "chdir to $buildroot/$branch";
338 # rename legacy status files/directories
339 foreach my $oldfile (glob("last*"))
341 move $oldfile, "$st_prefix$oldfile";
344 my $branch_root = getcwd();
346 # Normally we would require GNU Make, but allow farm
347 # configuration to override this
348 die "$make is not GNU Make - please fix config file"
352 foreach my $module (@{$EximBuild::conf{modules}})
355 # fill in the name of the module here, so use double quotes
356 # so everything BUT the module name needs to be escaped
358 require EximBuild::Modules::$module;
359 EximBuild::Modules::${module}::setup(
376 open($lockfile, ">builder.LCK") || die "opening lockfile: $!";
378 # only one builder at a time allowed per branch
379 # having another build running is not a failure, and so we do not output
380 # a failure message under this condition.
383 die "acquiring lock in $buildroot/$branch/builder.LCK"
384 unless flock($lockfile,LOCK_EX|LOCK_NB);
386 elsif ( !flock($lockfile,LOCK_EX|LOCK_NB) )
388 print "Another process holds the lock on "
389 ."$buildroot/$branch/builder.LCK. Exiting."
394 die "$buildroot/$branch has $exim or inst directories!"
395 if ((!$from_source && -d $exim) || -d "inst");
397 # we are OK to run if we get here
400 # check if file present for forced run
401 my $forcefile = $st_prefix . "force-one-run";
408 # try to allow core files to be produced.
409 # another way would be for the calling environment
410 # to call ulimit. We do this in an eval so failure is
413 require BSD::Resource;
414 BSD::Resource->import();
416 # explicit sub calls here. using & keeps compiler happy
417 my $coreok = setrlimit(&RLIMIT_CORE,&RLIM_INFINITY,&RLIM_INFINITY);
418 die "setrlimit" unless $coreok;
420 warn "failed to unlimit core size: $@" if $@ && $verbose > 1;
422 # the time we take the snapshot
424 my $installdir = "$buildroot/$branch/inst";
429 # cleanup handler for all exits
434 unlink $ENV{TEMP_CONFIG} if $extraconf;
436 # if we have the lock we must already be in the build root, so
437 # removing things there should be safe.
438 # there should only be anything to cleanup if we didn't have
440 if ( $have_lock && -d "$exim")
445 system(qq{"bin/pg_ctl" -D data stop >$devnull 2>&1});
446 foreach my $loc (@locales)
448 next unless -d "data-$loc";
449 system(qq{"bin/pg_ctl" -D "data-$loc" stop >$devnull 2>&1});
453 if ( !$from_source && $keep_errs)
455 print "moving kept error trees\n" if $verbose;
456 my $timestr = strftime "%Y-%m-%d_%H-%M-%S", localtime($now);
457 unless (move("$exim", "eximkeep.$timestr"))
459 print "error renaming '$exim' to 'eximkeep.$timestr': $!";
463 unless(move("inst", "instkeep.$timestr"))
465 print "error renaming 'inst' to 'instkeep.$timestr': $!";
471 rmtree("inst") unless $keepall;
472 rmtree("$exim") unless ($from_source || $keepall);
475 # only keep the cache in cases of success
476 rmtree("$ccachedir") if $ccachedir;
479 # get the modules to clean up after themselves
480 process_module_hooks('cleanup');
487 # vpath builds leave some stuff lying around in the
488 # source dir, unfortunately. This should clean it up.
492 unlink("builder.LCK");
496 # Prepend the DEFAULT settings (if any) to any settings for the
497 # branch. Since we're mangling this, deep clone $extra_config
498 # so the config object is kept as given. This is done using
499 # Dumper() because the MSys DTK perl doesn't have Storable. This
500 # is less efficient but it hardly matters here for this shallow
503 $extra_config = eval Dumper($extra_config);
505 if ($extra_config && $extra_config->{DEFAULT})
507 if (!exists $extra_config->{$branch})
509 $extra_config->{$branch} = $extra_config->{DEFAULT};
513 unshift(@{$extra_config->{$branch}}, @{$extra_config->{DEFAULT}});
517 if ($extra_config && $extra_config->{$branch})
520 ($extraconf,$tmpname) =File::Temp::tempfile(
522 DIR => File::Spec->tmpdir(),
525 die 'no $tmpname!' unless $tmpname;
526 $ENV{TEMP_CONFIG} = $tmpname;
527 foreach my $line (@{$extra_config->{$branch}})
529 print $extraconf "$line\n";
531 autoflush $extraconf 1;
534 use vars qw($steps_completed);
535 $steps_completed = "";
538 my @changed_since_success;
542 my $last_success_snap;
548 if ($from_source_clean)
550 print time_str(),"cleaning source in $exim ...\n";
553 elsif (!$from_source)
556 # see if we need to run the tests (i.e. if either something has changed or
557 # we have gone over the force_every heartbeat time)
559 print time_str(),"checking out source ...\n" if $verbose;
563 $timeout_pid = spawn(\&scm_timeout,$scm_timeout_secs)
564 if $scm_timeout_secs;
566 $savescmlog = $scm->checkout($branch);
567 $steps_completed = "SCM-checkout";
569 process_module_hooks('checkout',$savescmlog);
574 # don't kill me, I finished in time
575 if (kill(SIGTERM, $timeout_pid))
579 waitpid($timeout_pid,0);
583 print time_str(),"checking if build run needed ...\n" if $verbose;
585 # transition to new time processing
586 unlink "last.success";
588 # get the timestamp data
589 $last_config = find_last('config') || 0;
590 $last_status = find_last('status') || 0;
591 $last_run_snap = find_last('run.snap');
592 $last_success_snap = find_last('success.snap');
593 $forcerun = 1 unless (defined($last_run_snap));
595 # If config file changed, force a rebuild
596 ($current_config) = (stat $orig_dir.'/'.$buildconf)[9];
597 if (defined $current_config && $current_config > $last_config)
600 set_last('config',$current_config) unless $nostatus;
603 # updated by find_changed to last mtime of any file in the repo
606 # see if we need to force a build
610 &&$last_status+($force_every*3600) < $now);
611 $last_status = 0 if $forcerun;
613 # see what's changed since the last time we did work
614 $scm->find_changed(\$current_snap,$last_run_snap, $last_success_snap,
615 \@changed_files,\@changed_since_success);
617 #ignore changes to files specified by the trigger exclude filter, if any
618 if (defined($trigger_exclude))
620 @filtered_files = grep { !m[$trigger_exclude] } @changed_files;
624 @filtered_files = @changed_files;
627 #ignore changes to files NOT specified by the trigger include filter, if any
628 if (defined($trigger_include))
630 @filtered_files = grep { m[$trigger_include] } @filtered_files;
633 my $modules_need_run;
635 process_module_hooks('need-run',\$modules_need_run);
637 # if no build required do nothing
638 if ($last_status && !@filtered_files && !$modules_need_run)
641 "No build required: last status = ",scalar(gmtime($last_status)),
642 " GMT, current snapshot = ",scalar(gmtime($current_snap))," GMT,",
643 " changed files = ",scalar(@filtered_files),"\n"
649 # get version info on both changed files sets
650 # XXX modules support?
652 $scm->get_versions(\@changed_files);
653 $scm->get_versions(\@changed_since_success);
655 } # end of unless ($from_source)
659 writelog('SCM-checkout',$savescmlog) unless $from_source;
660 $scm->log_id() unless $from_source;
662 # copy/create according to vpath/scm settings
666 print time_str(),"creating vpath build dir $exim ...\n" if $verbose;
667 mkdir $exim || die "making $exim: $!";
669 elsif (!$from_source && $scm->copy_source_required())
671 print time_str(),"copying source to $exim ...\n" if $verbose;
673 $scm->copy_source($using_msvc);
676 process_module_hooks('setup-target');
680 set_last('status',$now) unless $nostatus;
681 set_last('run.snap',$current_snap) unless $nostatus;
683 my $started_times = 0;
684 print time_str(),"running configure ...\n" if $verbose;
686 # each of these routines will call send_result, which calls exit,
687 # on any error, so each step depends on success in the previous
695 make_test() if (check_optional_step('test'));
697 make_doc() if (check_optional_step('make-doc'));
699 ##check_port_is_ok($buildport,'Post');
701 # if we get here everything went fine ...
703 my $saved_config = get_config_summary();
705 rmtree("inst"); # only keep failures
706 rmtree("$exim") unless ($from_source || $keepall);
708 print(time_str(),"OK\n") if $verbose;
714 ############## end of main program ###########################
719 usage: $0 [options] [branch]
721 where options are one or more of:
723 --nosend = don't send results
724 --nostatus = don't set status files
725 --force = force a build run (ignore status files)
726 --from-source=/path = use source in path, not from SCM
728 --from-source-clean=/path = same as --from-source, run make distclean first
729 --config=/path/to/file = alternative location for config file
730 --keepall = keep directories if an error occurs
731 --verbose[=n] = verbosity (default 1) 2 or more = huge output.
732 --quiet = suppress normal error message
733 --test = short for --nosend --nostatus --verbose --force
734 --skip-steps=list = skip certain steps
735 --only-steps=list = only do certain steps, not allowed with skip-steps
736 lists can be comma, colon, or space separated
738 Default branch is HEAD. Usually only the --config option should be necessary.
746 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
747 return sprintf("[%.2d:%.2d:%.2d] ",$hour, $min, $sec);
753 return $only_steps{$step} if $only_steps;
754 return !$skip_steps{$step} if $skip_steps;
755 return 1; # default is everything is wanted
758 sub register_module_hooks
762 while (my ($hook,$func) = each %$what)
764 $module_hooks{$hook} ||= [];
765 push(@{$module_hooks{$hook}},[$func,$who]);
769 sub process_module_hooks
773 # pass remaining args (if any) to module func
774 foreach my $module (@{$module_hooks{$hook}})
776 my ($func,$module_instance) = @$module;
777 &$func($module_instance, @_);
781 sub check_optional_step
787 return undef unless ref($oconf = $optional_steps->{$step});
788 if ($oconf->{branches})
790 return undef unless grep {$_ eq $branch} @{$oconf->{branches}};
793 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =localtime(time);
794 return undef if (exists $oconf->{min_hour} && $hour < $oconf->{min_hour});
795 return undef if (exists $oconf->{max_hour} && $hour > $oconf->{max_hour});
796 return undef if (exists $oconf->{dow}
797 &&grep {$_ eq $wday} @{$oconf->{dow}});
799 my $last_step = $last_status = find_last("$step") || 0;
800 ## If made it *to* these optional steps, we just run them and reset last time
801 #return undef unless ($forcerun ||
802 # time >$last_step + (3600 * $oconf->{min_hours_since}));
803 set_last("$step") unless $nostatus;
808 sub clean_from_source
810 if (-e "$exim/GNUmakefile")
814 my @makeout = `cd $exim && $make distclean 2>&1`;
816 writelog('distclean',\@makeout);
817 print "======== distclean log ===========\n",@makeout if ($verbose > 1);
818 send_result('distclean',$status,\@makeout) if $status;
825 print "Exiting on signal $signame\n";
831 my $lrname = $st_prefix . $logdirname;
833 mkdir "$lrname" || die "can't make $lrname dir: $!";
839 my $loglines = shift;
841 my $lrname = $st_prefix . $logdirname;
842 open($handle,">$lrname/$stage.log") || die $!;
843 print $handle @$loglines;
849 return unless step_wanted('features');
851 src/build-*/exim -C test/confs/0000 -bV `;
853 writelog('features',\@out);
854 print "======== features log ===========\n",@out if ($verbose > 1);
855 send_result('Features',$status,\@out) if $status;
856 $steps_completed .= " Features";
861 # Allow farm member to configure non-GNU make
862 my $non_gnu_make = $EximBuild::conf{non_gnu_make};
863 if (!defined $non_gnu_make ||
864 (defined $non_gnu_make && $non_gnu_make == 1)) {
867 my @out = `$make -v 2>&1`;
868 return undef unless ($? == 0 && grep {/GNU Make/} @out);
874 return unless step_wanted('make');
875 print time_str(),"running make ...\n" if $verbose;
876 my $make_args = join(' ',$EximBuild::conf{make_args});
878 my $make_cmd = $make;
879 $make_cmd = "$make -j $make_jobs"
880 if ($make_jobs > 1 && ($branch eq 'HEAD' || $branch ge 'REL9_1'));
881 @makeout = `cd $exim/src && $make_cmd $make_args 2>&1`;
883 writelog('make',\@makeout);
884 print "======== make log ===========\n",@makeout if ($verbose > 1);
885 send_result('Make',$status,\@makeout) if $status;
886 $steps_completed .= " Make";
891 return unless step_wanted('make-doc');
892 print time_str(),"running make doc ...\n" if $verbose;
895 @makeout = `cd $exim/doc/doc-docbook/ && \
896 EXIM_VER="4.82" $make everything 2>&1`;
898 writelog('make-doc',\@makeout);
899 print "======== make doc log ===========\n",@makeout if ($verbose > 1);
900 send_result('Doc',$status,\@makeout) if $status;
901 $steps_completed .= " Doc";
909 # no core = no result
910 my @cores = glob("$pgdata/$core_file_glob");
911 return () unless @cores;
914 system "gdb --version > $devnull 2>&1";
916 return () if $status;
918 my $cmdfile = "./gdbcmd";
920 open($handle, ">$cmdfile");
921 print $handle "bt\n";
926 foreach my $core (@cores)
928 my @onetrace = `gdb -x $cmdfile --batch $bindir/exim $core 2>&1`;
930 "\n\n================== stack trace: $core ==================\n",
939 sub make_install_check
942 return unless step_wanted('install-check');
943 print time_str(),"running make installcheck ($locale)...\n" if $verbose;
948 @checklog = `cd $exim/src/test/regress && $make installcheck 2>&1`;
952 chdir "$exim/src/tools/msvc";
953 @checklog = `perl vcregress.pl installcheck 2>&1`;
958 ("$exim/src/test/regress/regression.diffs","$installdir/logfile");
959 foreach my $logfile(@logfiles)
961 next unless (-e $logfile );
962 push(@checklog,"\n\n================== $logfile ==================\n");
964 open($handle,$logfile);
974 get_stack_trace("$installdir/bin","$installdir/data-$locale");
975 push(@checklog,@trace);
977 writelog("install-check-$locale",\@checklog);
978 print "======== make installcheck log ===========\n",@checklog
980 send_result("InstallCheck-$locale",$status,\@checklog) if $status;
981 $steps_completed .= " InstallCheck-$locale";
984 sub make_isolation_check
987 return unless step_wanted('isolation-check');
992 "cd $exim/src/test/isolation && $make NO_LOCALE=1 installcheck";
993 @makeout = `$cmd 2>&1`;
997 chdir "$exim/src/tools/msvc";
998 @makeout = `perl vcregress.pl isolationcheck 2>&1`;
1002 my $status = $? >>8;
1004 # get the log files and the regression diffs
1005 my @logs = glob("$exim/src/test/isolation/log/*.log");
1006 push(@logs,"$installdir/logfile");
1007 unshift(@logs,"$exim/src/test/isolation/regression.diffs")
1008 if (-e "$exim/src/test/isolation/regression.diffs");
1009 foreach my $logfile (@logs)
1011 push(@makeout,"\n\n================== $logfile ===================\n");
1013 open($handle,$logfile);
1023 get_stack_trace("$installdir/bin","$installdir/data-$locale");
1024 push(@makeout,@trace);
1026 writelog('isolation-check',\@makeout);
1027 print "======== make isolation check logs ===========\n",@makeout
1030 send_result('IsolationCheck',$status,\@makeout) if $status;
1031 $steps_completed .= " IsolationCheck";
1036 return unless step_wanted('test');
1037 print time_str(),"running make test ...\n" if $verbose;
1039 my $tests_range = $EximBuild::conf{range_num_tests} || "1 4";
1040 # backwards compat for build-farm.conf from before this support was added
1041 $EximBuild::conf{test_configure_env} = {} unless exists $EximBuild::conf{test_configure_env};
1042 $EximBuild::conf{test_configure_args} = [] unless exists $EximBuild::conf{test_configure_args};
1043 $EximBuild::conf{test_run_args} = "" unless exists $EximBuild::conf{test_run_args};
1046 my %saved_env = %ENV;
1047 foreach my $k (keys %{$EximBuild::conf{test_configure_env}}) {
1048 $ENV{$k} = $EximBuild::conf{test_configure_env}{$k};
1051 foreach (@{$EximBuild::conf{test_configure_args}}) {
1053 $conf_args .= " '${_}'";
1055 @makeout =`(cd $exim/test && ./configure $conf_args && $make )2>&1 `;
1058 my $status = $? >>8;
1061 my @tmp = `(cd $exim/test && ./runtest -CONTINUE $EximBuild::conf{test_run_args} $tests_range )2>&1`;
1063 push @makeout, @tmp;
1064 # Prepend the failed summary log outputs for ease of reading
1065 my $fail_summary = "$exim/test/failed-summary.log";
1066 if (-f $fail_summary)
1068 @tmp = `cat $fail_summary`;
1070 unshift @makeout, @tmp;
1071 unshift @makeout, "Summary of failed tests:\n";
1074 writelog('test',\@makeout);
1075 send_result('Test',$status,\@makeout) if $status;
1077 @makeout = `cat $exim/test/run-summary.log`;
1078 writelog('test-results',\@makeout);
1080 print "======== make test logs ===========\n",@makeout
1082 $steps_completed .= " Test";
1087 return unless step_wanted('ecpg-check');
1089 my $ecpg_dir = "$exim/src/interfaces/ecpg";
1092 chdir "$exim/src/tools/msvc";
1093 @makeout = `perl vcregress.pl ecpgcheck 2>&1`;
1098 @makeout = `cd $ecpg_dir && $make NO_LOCALE=1 check 2>&1`;
1100 my $status = $? >>8;
1102 # get the log files and the regression diffs
1103 my @logs = glob("$ecpg_dir/test/log/*.log");
1104 unshift(@logs,"$ecpg_dir/test/regression.diffs")
1105 if (-e "$ecpg_dir/test/regression.diffs");
1106 foreach my $logfile (@logs)
1108 push(@makeout,"\n\n================== $logfile ===================\n");
1110 open($handle,$logfile);
1119 my $base = "$ecpg_dir/test/regress/tmp_check";
1121 get_stack_trace("$base/install$installdir/bin", "$base/data");
1122 push(@makeout,@trace);
1124 writelog('ecpg-check',\@makeout);
1125 print "======== make ecpg check logs ===========\n",@makeout
1128 send_result('ECPG-Check',$status,\@makeout) if $status;
1129 $steps_completed .= " ECPG-Check";
1134 return unless step_wanted('configure');
1135 print time_str(),"creating configuration ...\n" if $verbose;
1137 my $env = $EximBuild::conf{makefile_set};
1138 my $add = $EximBuild::conf{makefile_add};
1139 my $features = $EximBuild::conf{makefile_regex};
1142 while (my ($key,$val) = each %$env)
1144 $envstr .= "$key='$val'\n";
1146 while (my ($key,$val) = each %$add)
1148 $envstr .= "$key+='$val'\n";
1151 my $conf_path = "src/src/EDITME";
1152 my $local_conf = "src/Local/Makefile";
1153 my @confout = `cd $exim; mkdir -p src/Local 2>&1`;
1154 my @tmp = `cd $exim && cp $conf_path $local_conf 2>&1`;
1155 my $status = $? >> 8;
1156 push @confout, @tmp;
1159 # First, let's display some directory permissions in case
1160 # permissions are causing problems.
1161 my @dir = split('/',`pwd`);
1163 my $count = scalar @dir;
1166 while ($loop < $count)
1169 foreach my $i (0 .. $loop)
1171 $dir .= $dir[$i].'/';
1176 @tmp = `echo "Verify Directory Permissions"
1178 push @confout, @tmp;
1179 # Build the config file from the EDITME template
1180 @tmp = `cd $exim && echo '$envstr' >> $local_conf`;
1181 push @confout, @tmp;
1182 my $exim_user = $EximBuild::conf{master_exim_user} || 'exim';
1183 @tmp = `echo "Hardcoded Exim user info:"; id $exim_user
1184 cd $exim && perl -pi -e 's/^EXIM_USER=.*/EXIM_USER=$exim_user/' $local_conf`;
1185 push @confout, @tmp;
1186 #my $me = `whoami`; chomp $me;
1187 my $me = getpwuid($>) // die "$0: getpwuid($>): $!\n";
1188 @tmp = `echo "Build Farm user info:"; id $me
1189 cd $exim && perl -pi -e 's/^# CONFIGURE_OWNER=\$/CONFIGURE_OWNER=$me/' $local_conf`;
1190 push @confout, @tmp;
1191 my $testdir = `cd $exim && /bin/pwd`; chomp $testdir; $testdir .= "/test";
1192 my $trcf = "$testdir/trusted-configs";
1193 my $tecf = "$testdir/test-config";
1194 @tmp = `cd $exim && perl -pi -e "s%^# TRUSTED_CONFIG_LIST=.*%TRUSTED_CONFIG_LIST=$trcf%" $local_conf`;
1195 push @confout, @tmp;
1196 @tmp = `cd $exim && perl -pi -e 's/^# WHITELIST_D_MACROS=.*/WHITELIST_D_MACROS=DIR:EXIM_PATH:AA:ACL:ACLRCPT:ACL_MAIL:ACL_PREDATA:ACL_RCPT:AFFIX:ALLOW:ARG1:ARG2:AUTHF:AUTHS:AUTH_ID_DOMAIN:BAD:BANNER:BB:BR:BRB:CERT:COM:COMMAND_USER:CONNECTCOND:CONTROL:CREQCIP:CREQMAC:CRL:CSS:D6:DATA:DCF:DDF:DEFAULTDWC:DELAY:DETAILS:DRATELIMIT:DYNAMIC_OPTION:ELI:ERROR_DETAILS:ERT:FAKE:FALLBACK:FILTER:FILTER_PREPEND_HOME:FORBID:FORBID_SMTP_CODE:FUSER:HAI:HAP:HARDLIMIT:HEADER_LINE_MAXSIZE:HEADER_MAXSIZE:HELO_MSG:HL:HOSTS:HOSTS_AVOID_TLS:HOSTS_MAX_TRY:HVH:IFACE:IGNORE_QUOTA:INC:INSERT:IP1:IP2:LAST:LDAPSERVERS:LENCHECK:LIMIT:LIST:LOG_SELECTOR:LS:MAXNM:MESSAGE_LOGS:MSIZE:NOTDAEMON:ONCE:ONLY:OPT:OPTION:ORDER:PAH:PEX:PORT:PTBC:QDG:QOLL:QUOTA:QUOTA_FILECOUNT:QWM:RCPT_MSG:REMEMBER:REQUIRE:RETRY:RETRY1:RETRY2:RETURN:RETURN_ERROR_DETAILS:REWRITE:ROUTE_DATA:RRATELIMIT:RT:S:SELECTOR:SELF:SERVER:SERVERS:SREQCIP:SREQMAC:SRV:STD:STRICT:SUB:SUBMISSION_OPTIONS:TIMEOUTDEFER:TIMES:TRUSTED:TRYCLEAR:UL:USE_SENDER:UTF8:VALUE:WMF:X:Y/' $local_conf`;
1197 push @confout, @tmp;
1198 @tmp = `cd $exim && perl -pi -e 's/^EXIM_MONITOR=(.*)/# EXIM_MONITOR=\$1/' $local_conf`;
1199 push @confout, @tmp;
1200 for my $regex ( @$features )
1203 perl -pi -e '$regex' $local_conf 2>&1
1204 echo "Used regex: $regex" `;
1205 push @confout, @tmp;
1207 # Add the final build file to the display output
1210 echo "Contents of Local/Makefile:"
1211 grep -E '^[^#]' $local_conf `;
1212 push @confout, @tmp;
1213 # Build the config_opts array to send to the server
1215 my @config_opts = grep s/(?:LOOKUP_|EXPERIMENTAL_|SUPPORT_|USE_)(\S+)=.*/$1/,
1217 push @config_opts, grep s/^(?:EXIM_)(PERL|PYTHON)=.*/$1/,
1219 # OpenSSL doesn't have a specific USE flag
1220 push @config_opts, grep s/^(TLS_LIBS.*-l(ssl|crypto)).*/OPENSSL/,
1222 $EximBuild::conf{config_opts} = \@config_opts;
1224 # Does not matter what the Exim version is, as long as it is valid.
1225 my $exim_ver = $EximBuild::conf{exim_test_version} || '4.82';
1227 echo 'EXIM_RELEASE_VERSION="$exim_ver"' > src/src/version.sh
1228 echo 'EXIM_VARIANT_VERSION=""' >> src/src/version.sh
1229 echo 'EXIM_COMPILE_NUMBER="0"' >> src/src/version.sh`;
1231 # Create a trusted-configs list file
1232 @tmp = `cd $exim && echo "$tecf" > "$trcf"`;
1233 push @confout, @tmp;
1236 print "======== configure output ===========\n",@confout
1239 writelog('configure',\@confout);
1243 send_result('Configure',$status,\@confout);
1246 $steps_completed .= " Configure";
1252 my $stname = $st_prefix . "last.$which";
1254 open($handle,$stname) or return undef;
1255 my $time = <$handle>;
1264 my $stname = $st_prefix . "last.$which";
1265 my $st_now = shift || time;
1267 open($handle,">$stname") or die "opening $stname: $!";
1268 print $handle "$st_now\n";
1275 # clean up temp file
1280 my $ts = $now || time;
1281 my $status=shift || 0;
1282 my $log = shift || [];
1283 print "======== log passed to send_result ===========\n",@$log
1287 "Last file mtime in snapshot: ",
1288 scalar(gmtime($current_snap)),
1289 " GMT\n","===================================================\n")
1290 unless ($from_source || !$current_snap);
1292 my $log_data = join("",@$log);
1294 my $changed_this_run = "";
1295 my $changed_since_success = "";
1296 $changed_this_run = join("!",@changed_files)
1298 $changed_since_success = join("!",@changed_since_success)
1299 if ($stage ne 'OK' && @changed_since_success);
1303 $confsum= $saved_config;
1305 elsif ($stage !~ /CVS|Git|SCM/ )
1307 $confsum = get_config_summary();
1311 $confsum = get_script_config_dump();
1314 my $savedata = Data::Dumper->Dump(
1316 $changed_this_run, $changed_since_success, $branch, $status,$stage,
1317 $animal, $ts,$log_data, $confsum, $target, $verbose, $secret
1320 qw(changed_this_run changed_since_success branch status stage
1321 animal ts log_data confsum target verbose secret)
1325 my $lrname = $st_prefix . $logdirname;
1327 # might happen if there is a CVS failure and have never got further
1328 mkdir $lrname unless -d $lrname;
1330 my $txfname = "$lrname/web-txn.data";
1332 open($txdhandle,">$txfname");
1333 print $txdhandle $savedata;
1336 if ($nosend || $stage eq 'CVS' || $stage eq 'CVS-status' )
1338 print "Branch: $branch\n";
1341 print "All stages succeeded\n";
1342 set_last('success.snap',$current_snap) unless $nostatus;
1347 print "Stage $stage failed with status $status\n";
1352 if ($stage !~ /CVS|Git|SCM|Pre-run-port-check/ )
1355 my @logfiles = glob("$lrname/*.log");
1356 my %mtimes = map { $_ => (stat $_)[9] } @logfiles;
1358 map { basename $_ }( sort { $mtimes{$a} <=> $mtimes{$b} } @logfiles );
1359 my $logfiles = join(' ',@logfiles);
1360 $tar_log_cmd =~ s/\*\.log/$logfiles/;
1362 system("$tar_log_cmd 2>&1 ");
1363 chdir($branch_root);
1369 # these would be from an earlier run, since we
1370 # do cleanlogs() after the cvs stage
1371 # so don't send them.
1372 unlink "$lrname/runlogs.tgz";
1377 # this should now only apply to older Msys installs. All others should
1378 # be running with perl >= 5.8 since that's required to build exim
1380 if (!$^V or $^V lt v5.8.0)
1383 unless (-x "$aux_path/run_web_txn.pl")
1385 print "Could not locate $aux_path/run_web_txn.pl\n";
1389 system("$aux_path/run_web_txn.pl $lrname");
1390 $txstatus = $? >> 8;
1394 $txstatus = EximBuild::WebTxn::run_web_txn($lrname) ? 0 : 1;
1400 print "Web txn failed with status: $txstatus\n";
1402 # if the web txn fails, restore the timestamps
1403 # so we try again the next time.
1404 set_last('status',$last_status) unless $nostatus;
1405 set_last('run.snap',$last_run_snap) unless $nostatus;
1409 unless ($stage eq 'OK' || $quiet)
1411 print "BuildFarm member $animal failed on $branch stage $stage\n";
1414 # print "Success!\n",$response->content
1415 # if $print_success;
1417 set_last('success.snap',$current_snap) if ($stage eq 'OK' && !$nostatus);
1422 sub get_config_summary
1426 # unless ($using_msvc)
1428 # open($handle,"$exim/config.log") || return undef;
1429 # my $start = undef;
1432 # if (!$start && /created by PostgreSQL configure/)
1435 # s/It was/This file was/;
1437 # next unless $start;
1438 # last if /Core tests/;
1440 # next if /= <?unknown>?/;
1442 # # split up long configure line
1443 # if (m!\$.*configure.*--with! && length > 70)
1445 # my $pos = index($_," ",70);
1446 # substr($_,$pos+1,0,"\\\n ") if ($pos > 0);
1447 # $pos = index($_," ",140);
1448 # substr($_,$pos+1,0,"\\\n ") if ($pos > 0);
1449 # $pos = index($_," ",210);
1450 # substr($_,$pos+1,0,"\\\n ") if ($pos > 0);
1456 # "\n========================================================\n";
1458 $config .= get_script_config_dump();
1462 sub get_script_config_dump
1465 %EximBuild::conf, # shallow copy
1466 script_version => $VERSION,
1467 invocation_args => \@invocation_args,
1468 steps_completed => $steps_completed,
1469 orig_env => $orig_env,
1471 delete $conf->{secret};
1473 if ($conf->{scm} eq 'git') {
1474 chomp($conf->{farm}{revision} = `cd $RealBin && git describe --tags --always --dirty=+`);
1475 $conf->{farm}{cwd} = getcwd();
1476 $conf->{farm}{bindir} = $RealBin;
1477 $conf->{farm}{perl} = $^V;
1480 $Data::Dumper::Sortkeys = 1;
1481 return Data::Dumper->Dump([$conf],['Script_Config']);
1486 my $wait_time = shift;
1487 my $who_to_kill = getpgrp(0);
1490 print "waiting $wait_time secs to time out process $who_to_kill\n"
1492 foreach my $sig (qw(INT TERM HUP QUIT))
1494 $SIG{$sig}='DEFAULT';
1497 $SIG{TERM} = 'IGNORE'; # so we don't kill ourself, we're exiting anyway
1498 # kill the whole process group
1499 unless (kill $sig,$who_to_kill)
1501 print "scm timeout kill failed\n";
1507 my $coderef = shift;
1509 if (defined($pid) && $pid == 0)