2 # $NetBSD: post-build,v 1.70 2008/01/04 15:49:08 rillig Exp $
4 # Collect stuff after a pkg bulk build
6 # (c) 2000 Hubert Feyrer, All Rights Reserved.
10 use POSIX qw(strftime);
19 my $verbose = 1; # set to 2 to see more command execution detail
26 my ($fname, $msg) = @_;
29 $text = "[post-build] error: ";
31 if (defined($fname)) {
32 $text .= "${sep}${fname}";
35 $text .= "${sep}${msg}";
40 print STDERR '> '.join(' ', @_)."\n" if ($verbose >= 2);
44 sub readfirstline($) {
49 or pb_die($fname, "Cannot be read: $!");
51 defined($contents = <F>)
52 or pb_die($fname, "Must not be empty.");
56 or pb_die($fname, "Cannot be closed: $!");
62 # Load configuration variables from the bulk.conf file, which is a shell
66 my $BULK_BUILD_CONF = $ENV{BULK_BUILD_CONF} || (dirname($0).'/build.conf');
67 $BULK_BUILD_CONF = "./$BULK_BUILD_CONF" if ($BULK_BUILD_CONF !~ m:^/:);
69 if (!-f $BULK_BUILD_CONF) {
70 pb_die($BULK_BUILD_CONF, "Does not exist.");
73 # Dig given variable out of config file, and set it
74 sub get_build_conf_vars(@) {
78 foreach my $varname (@varnames) {
79 my $cmd = join("\n", (
80 #"set -eu", # TODO: Should be enabled soon
81 ". '${BULK_BUILD_CONF}'",
82 ". mk/bulk/post-build-conf",
84 "echo \"\${${varname}+set}\"",
85 "echo \"\${${varname}-}\""
89 or pb_die($BULK_BUILD_CONF, "Could not evaluate configuration file.");
91 chomp($is_set = <CMD>);
92 { local $/ = undef; $value = <CMD>; }
93 chomp($value); # This must be outside the above {...} block
95 close(CMD) or pb_die($BULK_BUILD_CONF, "Could not evaluate configuration file (close).");
101 if ($is_set ne "set") {
102 pb_die($BULK_BUILD_CONF, "${varname} must be set.");
104 if ($value =~ qr"^\s+$") {
105 pb_die($BULK_BUILD_CONF, "${varname} must be non-empty.");
107 if ($value =~ qr"\n") {
108 pb_die($BULK_BUILD_CONF, "${varname} must not contain newlines.");
111 $vars{$varname} = $value;
113 print STDERR "> $varname=$vars{$varname}\n";
119 'ADMINSIG', # "-Your Name"
120 'REPORTS_URL', # "ftp://ftp.example.com/pub/pkgsrc/misc/pkgstat"
121 'REPORTS_DIR', # "$HOME/bulk-logs"
122 # REPORT_BASEDIR often depends on a timestamp, which has been saved at
123 # the beginning of the bulk build in the BULK_BUILD_ID_FILE. It will be
125 'REPORT_HTML_FILE', # "report.html"
126 'REPORT_TXT_FILE', # "report.txt"
127 'USR_PKGSRC', # "/usr/pkgsrc"
128 'osrev', # `uname -r`
135 my $BMAKE = $ENV{BMAKE} || pb_die(undef, "The BMAKE environment variable must be set.");
137 sub get_mk_conf_vars (@) {
140 open(I, "set -e; . '$BULK_BUILD_CONF'; . '$vars{USR_PKGSRC}/mk/bulk/post-build-conf'; export_config_vars; cd $vars{USR_PKGSRC}/pkgtools/lintpkgsrc && $BMAKE show-vars BATCH=1 VARNAMES='".join(' ', @_)."' USE_TOOLS=\"pax\" |")
141 or pb_die(undef, "Cannot get mk.conf variables.");
143 foreach my $var (@_) {
144 chomp($vars{$var} = <I>);
146 if ($vars{$var} eq "") {
147 pb_die(undef, "\${$var} must be defined in your mk.conf");
150 print STDERR "> $var=$vars{$var}\n" if ($verbose >= 2);
153 { local $/ = undef; $rest = <I>; }
154 if (defined($rest) && $rest ne "") {
155 pb_die(undef, "Output of $BMAKE show-vars too long:\n${rest}");
158 close(I) or die pb_die(undef, "Cannot get mk.conf variables (close).");
161 # Extract the names of the files used for the build log and broken build logs.
162 # These have defaults set by bsd.bulk-pkg.mk and may be overridden in
187 my $bulk_dbfile_base = basename($vars{BULK_DBFILE});
188 my $dependstreefile_base = basename($vars{DEPENDSTREEFILE});
189 my $dependsfile_base = basename($vars{DEPENDSFILE});
190 my $supportsfile_base = basename($vars{SUPPORTSFILE});
191 my $indexfile_base = basename($vars{INDEXFILE});
192 my $orderfile_base = basename($vars{ORDERFILE});
194 my $reports_url = $vars{"REPORTS_URL"};
195 my $reports_dir = $vars{"REPORTS_DIR"};
196 my $report_basedir = readfirstline($vars{"BULK_BUILD_ID_FILE"});
197 my $report_html_file = $vars{"REPORT_HTML_FILE"};
198 my $report_txt_file = $vars{"REPORT_TXT_FILE"};
200 my $report_url = "${reports_url}/${report_basedir}";
201 my $report_dir = "${reports_dir}/${report_basedir}";
202 my $report_html_path = "${report_dir}/${report_html_file}";
203 my $report_txt_path = "${report_dir}/${report_txt_file}";
205 my $startdate = (stat($vars{STARTFILE}))[9];
207 if (!defined($startdate) || $startdate == 0) {
208 $startdate = "unknown";
210 local $ENV{TZ} = "UTC";
211 $startdate = strftime("%c %Z", gmtime($startdate));
212 $enddate = strftime("%c %Z", gmtime(time()));
215 sub print_summary_line($$) {
216 my ($name, $value) = @_;
218 printf(" %-30s %s\n", $name, $value);
221 sub print_report_line($$$) {
222 my ($pkgpath, $breaks, $maintainer) = @_;
224 printf("%-26s %-7s %s\n", $pkgpath, $breaks, $maintainer);
227 sub print_report_header() {
228 print_report_line("Package", "Breaks", "Maintainer");
229 print("--------------------------------------------------------------\n");
232 my_system("mkdir", "-p", "--", $report_dir);
234 # Copy over the output from the build process
235 chdir($vars{"BULKFILESDIR"}) or pb_die($vars{"BULKFILESDIR"}, "Cannot change directory.");
236 my_system("find . -name $vars{BROKENFILE} -print -o -name $vars{BROKENWRKLOG} -print | $vars{PAX} -r -w -X ${report_dir}");
238 # Copy over the cache files used during the build
239 foreach my $f qw(BULK_DBFILE DEPENDSTREEFILE DEPENDSFILE SUPPORTSFILE INDEXFILE ORDERFILE) {
241 my_system("cp", "--", $vars{$f}, $report_dir);
245 chdir($report_dir) or pb_die($report_dir, "Cannot change directory.");
249 # Adjust "last" symlink
251 unlink("${reports_dir}/last");
252 symlink($report_basedir, "${reports_dir}/last");
255 # Generate leftovers-$vars{MACHINE_ARCH}.html: files not deleted
256 # Leftover files are copied to leftovers-$vars{MACHINE_ARCH} dir,
257 # and linked from leftovers-$vars{MACHINE_ARCH}.html
260 chdir($report_dir) or pb_die($report_dir, "Cannot change directory.");
261 my_system("mkdir", "-p", "leftovers-$vars{MACHINE_ARCH}");
263 # Find files since last build:
264 my $leftovers_txt = "leftovers-$vars{MACHINE_ARCH}.txt";
265 my $leftovers_html = "leftovers-$vars{MACHINE_ARCH}.html";
267 my_system("$vars{FIND} $vars{LOCALBASE}/ -newer $vars{STARTFILE} -type f -print >>$leftovers_txt");
268 my_system("$vars{FIND} $vars{X11BASE}/ -newer $vars{STARTFILE} -type f -print >>$leftovers_txt");
275 $perlfiles = `$vars{PKG_TOOLS_BIN}/pkg_info -qL perl*`;
278 my $perlfiles_pattern = $perlfiles;
279 $perlfiles_pattern =~ s/\n/|/g;
280 $perlfiles_pattern =~ s/|$//;
282 open (LEFT, $leftovers_txt) or die "can't read $leftovers_txt: $!";
285 my @leftovers = grep(!/^(?:${perlfiles_pattern})$/, @left);
287 if (index($vars{PKG_DBDIR}, $vars{LOCALBASE}) == 0) {
288 # If PKG_DBDIR is inside LOCALBASE, exclude it from the leftovers.
289 @leftovers = grep { index($_, $vars{PKG_DBDIR}) != 0 } @leftovers;
292 open (LEFT, ">$leftovers_txt") or die "can't write $leftovers_txt: $!";
293 print LEFT @leftovers;
296 if (scalar(@leftovers)) {
297 # Store leftovers, for easier identification:
298 my_system("$vars{PAX} -r -w -X leftovers-$vars{MACHINE_ARCH} < $leftovers_txt");
301 # Add links to leftover list:
302 open (OUT, "> $leftovers_html")
303 or die "can't write $leftovers_html";
309 foreach (@leftovers) {
311 print OUT "<a href=\"${report_url}/leftovers-$vars{MACHINE_ARCH}$_\">$_</a>\n";
321 # print the result of a single broken package
323 my ($pinfo, $state) = @_;
324 my $pkg = $pinfo->{pkg};
325 my $nbrokenby = $pinfo->{nbrokenby};
326 my $nerrors = $pinfo->{nerrors};
327 my $pkgdirmissing = 0;
330 if (not opendir($DIR, "$vars{USR_PKGSRC}/$pkg")) {
338 if (not $pkgdirmissing) {
339 @idents = `$vars{FIND} $vars{USR_PKGSRC}/$pkg -type f -print | xargs grep \\\$NetBSD`;
344 foreach my $ident (@idents) {
345 $ident =~ /\$[N]etBSD: ([^ ]*),v [^ ]* ([^ ]*) ([^ ]*) [^ ]* Exp \$/;
346 if (defined($2) && defined($3) && ("$2 $3" gt $datetime)) {
354 if (not $pkgdirmissing) {
355 $maintainer = `grep ^MAINTAINER $vars{USR_PKGSRC}/$pkg/Makefile`;
357 $maintainer = "directory_does_not_exist";
359 $maintainer =~ s/MAINTAINER=[ \t]*//;
361 $maintainer = `cd $vars{USR_PKGSRC}/$pkg ; $BMAKE show-var VARNAME=MAINTAINER`;
363 $maintainer =~ s/</</g;
364 $maintainer =~ s/>/>/g;
367 (my $state_style = $state) =~ s/ //g;
369 my $nbrokenby_html = '<td> </td>';
371 '<td align="right" class="pkg-'.$state_style.'">'.$nbrokenby.'</td>'
374 if ($pinfo->{nerrors} != 0 && $verbose && ($state eq "broken" || $state eq "topten")) {
375 print_report_line($pkg, $nbrokenby > 0 ? $nbrokenby : "", $maintainer);
380 <td><a class="pkg-$state_style" href="$pinfo->{bf}" title="build log for $pkg">$pkg</a></td>
389 # write the build report
391 my $broken = getBroken();
392 my $nbroken = scalar(@{$broken->{"broken"}});
393 my $nbrokendep = scalar(@{$broken->{"broken depends"}});
394 my $nunpackaged = scalar(@{$broken->{"not packaged"}});
395 my $nnot_available = scalar(@{$broken->{"not available"}});
396 my $nbrokentot = $nbroken + $nbrokendep;
397 my $ntotal = $nunpackaged + $nbroken + $nbrokendep;
399 # determine the number of packages attempted, and then successful
400 open(ORDER, $vars{ORDERFILE}) || die "can't open $vars{ORDERFILE}: $!";
403 my $nattempted = scalar(@order);
404 my $nsuccessful = $nattempted - $ntotal;
408 pkgsrc bulk build results
409 $os $vars{osrev}/$vars{MACHINE_ARCH}
410 Compiler: $vars{PKGSRC_COMPILER}
415 print_summary_line("Build started:", $startdate);
416 print_summary_line("Build ended:", $enddate);
418 print_summary_line("Successfully packaged:", $nsuccessful);
419 print_summary_line("Packages really broken:", $nbroken);
420 print_summary_line("Pkgs broken due to them:", $nbrokendep);
421 print_summary_line("Total broken:", $nbrokentot);
422 print_summary_line("Not packaged:", $nunpackaged);
423 print_summary_line("Not available:", $nnot_available);
424 print_summary_line("Total:", $ntotal);
427 Packages not listed here resulted in a binary package. The build
428 report, including logs of failed/not-packaged is available from:
430 ${report_url}/${report_html_file}
434 open(HTML, ">", $report_html_path) or die "Can't write ${report_html_path}: $!";
438 <title>$os $vars{osrev}/$vars{MACHINE_ARCH} bulk package build</title>
439 <style type="text/css">
443 Font-Family: Tahoma, Verdana, sans-serif;
445 Text-Decoration: None;
447 Background-Color: white;
456 Font-Family: Tahoma, Verdana, sans-serif;
470 Text-Decoration: underline;
509 Font-Family: Arial, Helvetica, Courier, fixed;
511 Text-Decoration: none;
530 <body bgcolor="white" text="black" link="#3535c5" vlink="#700080" alink="#3535c5">
533 <h1>pkgsrc bulk build results</h1>
534 <h2>$os $vars{osrev}/$vars{MACHINE_ARCH}</h2>
540 <td>Build started: <td align="right">$startdate</td>
543 <td>Build ended: <td align="right">$enddate</td>
546 <td> </td> <td> </td>
549 <td>Successfully packaged:</td> <td align="right">$nsuccessful</td>
551 <tr class="pkg-broken">
552 <td>Packages really broken:</td> <td align="right">$nbroken</td>
554 <tr class="pkg-brokendepends">
555 <td>Packages broken due to them:</td> <td align="right">$nbrokendep</td>
558 <td>Total broken:</td> <td align="right">$nbrokentot</td>
560 <tr class="pkg-notpackaged">
561 <td>Not packaged:</td> <td align="right">$nunpackaged</td>
563 <tr class="pkg-notavailable">
564 <td>Not available:</td> <td align="right">$nnot_available</td>
567 <td>Total:</td> <td align="right">$ntotal</td>
572 Packages not listed here resulted in a <a
573 href="../../packages/" title="binary packages for $os $vars{osrev}/$vars{MACHINE_ARCH}">binary
574 package</a>. Results of failed packages are available below.
578 Files leftover from the build (because of broken PLISTs, etc.) can be
579 found in <a href="leftovers-$vars{MACHINE_ARCH}.html" title="leftover files">this
586 <li><a href="#topten">Top Ten Offenders</a></li>
587 <li><a href="#broken">Broken packages</a></li>
588 <li><a href="#broken depends">Broken dependencies</a></li>
589 <li><a href="#not packaged">Not packaged</a></li>
590 <li><a href="#not available">Not available</a></li>
597 "topten" => "Top Ten Offenders",
598 "broken" => "Broken packages",
599 "broken depends" => "Broken dependencies",
600 "not packaged" => "Not packaged",
601 "not available" => "Not available",
604 foreach my $state ("topten", "broken", "broken depends", "not packaged", "not available") {
605 next unless scalar(@{$broken->{$state}});
607 if ($verbose && ($state eq "topten" || $state eq "broken")) {
608 print "\n\n$state_head{$state}\n\n";
609 print_report_header();
615 <a name="$state"></a>
616 <h2>$state_head{$state}</h2>
619 <th width="30%">Package</th>
621 <th>File touched last</th>
626 foreach my $pinfo (@{$broken->{$state}}) {
627 print HTML pkgResult($pinfo, $state);
633 <a href="#top">Up to top</a><br/>
641 The following cache files were used during the build:
644 <li>The <a href="$bulk_dbfile_base">SPECIFIC_PKGS bulk database file</a>.</li>
645 <li>The <a href="$dependstreefile_base">depends tree file</a>.</li>
646 <li>The <a href="$dependsfile_base">depends file</a>.</li>
647 <li>The <a href="$supportsfile_base">supports file</a>.</li>
648 <li>The <a href="$indexfile_base">index file</a>.</li>
649 <li>The <a href="$orderfile_base">build order file</a>.</li>
655 <!-- <li>See the list of <a href="../index.html">all log files</a>. -->
656 <li>Visit the <a href="http://www.NetBSD.org">NetBSD web site</a>.
658 <a href="http://www.NetBSD.org/docs/software/packages.html">
659 The NetBSD Packages Collection</a>.
668 print "\n\n$vars{ADMINSIG}\n\n";
669 print "[* This message was created by the Packages Collection bulk build software *]\n";
673 # get and sort the broken packages
677 'broken depends' => [],
678 'not packaged' => [],
680 "not available" => [],
683 open (BF, $vars{BROKENFILE}) || return $res;
689 my ($nerrors, $bf, $nbrokenby) = split;
691 $pkg =~ s,/$vars{BROKENFILE},,;
695 nbrokenby => $nbrokenby,
699 if (-f "$vars{BULKFILESDIR}/$pkg/$vars{NOT_AVAILABLE_FILE}") {
700 push(@{$res->{"not available"}}, $tmp);
701 } elsif ($nerrors > 0) {
702 push(@{$res->{"broken"}}, $tmp);
703 } elsif ($nerrors == -1) {
704 push(@{$res->{"broken depends"}}, $tmp);
706 push(@{$res->{"not packaged"}}, $tmp);
710 # sort pkgs in each state
711 foreach my $state ("broken", "broken depends", "not packaged", "not available") {
712 $res->{$state} = [ sort { $a->{pkg} cmp $b->{pkg} } @{$res->{$state}} ];
715 $res->{"topten"} = [ sort { $b->{nbrokenby} <=> $a->{nbrokenby} } @{$res->{"broken"}} ];
717 for (my $count = $#{$res->{"topten"}}; $count >= 10; $count--) {
718 pop(@{$res->{"topten"}});