#!/usr/bonsaitools/bin/perl -- # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Netscape Public # License Version 1.1 (the "License"); you may not use this file # except in compliance with the License. You may obtain a copy of # the License at http://www.mozilla.org/NPL/ # # Software distributed under the License is distributed on an "AS # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or # implied. See the License for the specific language governing # rights and limitations under the License. # # The Original Code is the Tinderbox build tool. # # The Initial Developer of the Original Code is Netscape Communications # Corporation. Portions created by Netscape are # Copyright (C) 1998 Netscape Communications Corporation. All # Rights Reserved. # # Contributor(s): use lib '../bonsai'; require 'tbglobals.pl'; require 'lloydcgi.pl'; require 'imagelog.pl'; require 'header.pl'; # Hack this until I can figure out how to do get default root. -slamm $default_root = '/cvsroot'; # Show 12 hours by default # $nowdate = time; if (not defined($maxdate = $form{maxdate})) { $maxdate = $nowdate; } if ($form{showall}) { $mindate = 0; } else { $default_hours = 12; $hours = $default_hours; $hours = $form{hours} if $form{hours}; $mindate = $maxdate - ($hours*60*60); } %colormap = ( success => '11DD11', busted => 'EE0000', building => 'EEFF00', testfailed => 'FFAA00' ); %images = ( flames => '1afi003r.gif', star => 'star.gif' ); $::tree = $form{tree}; # $rel_path is the relative path to webtools/tinderbox used for links. # It changes to "../" if the page is generated statically, because then # it is placed in tinderbox/$::tree. $rel_path = ''; &show_tree_selector, exit if $::tree eq ''; &do_quickparse, exit if $form{quickparse}; &do_express, exit if $form{express}; &do_rdf, exit if $form{rdf}; &do_static, exit if $form{static}; &do_flash, exit if $form{flash}; &do_panel, exit if $form{panel}; &do_hdml, exit if $form{hdml}; &do_tinderbox, exit; # end of main #===================================================================== sub make_tree_list { my @result; while(<*>) { if( -d $_ && $_ ne 'data' && $_ ne 'CVS' && -f "$_/treedata.pl") { push @result, $_; } } return @result; } sub show_tree_selector { print "Content-type: text/html\n\n"; EmitHtmlHeader("tinderbox"); print "

"; print ""; print "
Select one of the following trees:
\n"; print "
    \n"; my @list = make_tree_list(); foreach (@list) { print "
  • $_\n"; } print "
"; print "

"; print ""; print "
"; print "Administer one of the following trees:
\n"; print "
    \n"; foreach (@list) { print "
  • $_\n"; } print "
"; } sub do_static { local *OUT; $form{nocrap}=1; my @pages = ( ['index.html', 'do_tinderbox'], ['flash.rdf', 'do_flash'], ['panel.html', 'do_panel'], ['stats.hdml', 'do_hdml'] ); $rel_path = '../'; while (($key, $value) = each %images) { $images{$key} = "$rel_path$value"; } my $oldfh = select; foreach $pair (@pages) { my ($page, $call) = @{$pair}; my $outfile = "$::tree/$page"; open(OUT,">$outfile.$$"); select OUT; eval "$call"; close(OUT); system "mv $outfile.$$ $outfile"; } select $oldfh; } sub do_tinderbox { my $tinderbox_data = &tb_load_data; &print_page_head; &print_table_header; &print_table_body($tinderbox_data); &print_table_footer; } sub print_page_head { print "Content-type: text/html\n\n\n" unless $form{static}; use POSIX qw(strftime); # Print time in format, "HH:MM timezone" my $now = strftime("%H:%M %Z", localtime); EmitHtmlTitleAndHeader("tinderbox: $::tree", "tinderbox", "tree: $::tree ($now)"); &print_javascript; # Get the message of the day only on the first pageful do "$::tree/mod.pl" if $nowdate eq $maxdate; print "$message_of_day\n"; # from $::tree/mod.pl # Quote and Lengend # unless ($form{nocrap}) { my ($imageurl,$imagewidth,$imageheight,$quote) = &get_image; print qq{


$quote

L = Show Build Log
C = Show Checkins
D = Download Build
= Show Log comments
Successful Build, optional bloaty stats:
Lk:XXX (bytes leaked)
Bl:YYYY (bytes allocated, bloat)
Build in Progress
Successful Build, but Tests Failed
Build Failed
}; } if (is_tree_state_available()) { print ""; print "The tree is "; print (is_tree_open() ? 'open' : 'closed'); print "\n"; } } sub print_table_body { my $tinderbox_data = $_[0]; for (my $tt=0; $tt < $time_count; $tt++) { last if $build_time_times->[$tt] < $mindate; print_table_row($tinderbox_data, $tt); } } sub print_bloat_delta { my ($value, $compare) = @_; my $units = ' '; $value = $value || 0; $compare = $compare || 0; if ($value >= 1000000) { $value = int($value / 1000000); $min = int($min / 1000000); $units = 'M'; } elsif ($value >= 1000) { $value = int($value / 1000); $min = int($min / 1000); $units = 'K'; } # Took out colors because the numbers jump around too much. -slamm return "$value$units"; } BEGIN { # Make $lasthour persistent private variable for print_table_row(). my $lasthour = ''; sub print_table_row { my ($td, $tt) = @_; # Time column # my $query_link = ''; my $end_query = ''; my $pretty_time = print_time($build_time_times->[$tt]); ($hour) = $pretty_time =~ /(\d\d):/; if ($lasthour ne $hour or has_who_list($tt)) { $query_link = query_ref($td, $build_time_times->[$tt]); $end_query = ''; } if ($lasthour eq $hour) { $pretty_time =~ s/^.* //; } else { $lasthour = $hour; } my $hour_color = ''; $hour_color = ' bgcolor=#e7e7e7' if ($build_time_times->[$tt] + 1) % 7200 <= 3600; print "", "$query_link\n$pretty_time$end_query\n"; # Guilty # print ''; for $who (sort keys %{$who_list->[$tt]} ){ my $qr = &who_menu($td, $build_time_times->[$tt], $build_time_times->[$tt-1],$who); $who =~ s/%.*$//; print " $qr$who\n"; } print ''; # Build Status # for (my $build_index=0; $build_index < $name_count; $build_index++) { if (not defined($br = $build_table->[$tt][$build_index])) { # No build data for this time (e.g. no build after this time). print "\n"; next; } next if $br == -1; # Covered by rowspan my $rowspan = $br->{rowspan}; $rowspan = $mindate_time_count - $tt + 1 if $tt + $rowspan - 1 > $mindate_time_count; print "{buildstatus}}>\n"; my $logfile = $br->{logfile}; my $buildtree = $br->{td}->{name}; print "\n"; # Build Note # my $logurl = "${rel_path}showlog.cgi?log=$buildtree/$logfile"; if ($br->{hasnote}) { print "{noteid},'$logfile');\">", "\n"; } # Build Log # # Uncomment this line to print logfile names in build rectangle. # print "$logfile
"; print "" ."L"; # What Changed # # Only add the "C" link if there have been changes since the last build. if ($br->{previousbuildtime}) { my $previous_br = $build_table->[$tt+$rowspan][$build_index]; my $previous_rowspan = $previous_br->{rowspan}; if (&has_who_list($tt+$rowspan, $tt+$rowspan+$previous_rowspan-1)) { print "\n", &query_ref($br->{td}, $br->{previousbuildtime}, $br->{starttime} - 1); print "C"; } } # Binary URL # # Only add the "D" link if there is a url to a downloadable binary if( $br->{binaryurl} ){ $binaryurl = $br->{binaryurl}; print" D"; } # Leak/Bloat if (defined $td->{bloaty}{$logfile}) { my ($leaks, $bloat, $leaks_cmp, $bloat_cmp) = @{ $td->{bloaty}{$logfile} }; print "
Lk:", print_bloat_delta($leaks, $leaks_cmp), "
Bl:", print_bloat_delta($bloat, $bloat_cmp); } # Warnings if (defined $td->{warnings}{$logfile}) { my ($warning_count) = $td->{warnings}{$logfile}; my $warn_file = "$::tree/warn$logfile"; $warn_file =~ s/\.gz$/.html/; print "

Warn:$warning_count"; } print "
\n"; } print "\n"; } } sub print_table_header { print "\n"; print "\n"; print "\n"; print "\n"; for (my $ii=0; $ii < $name_count; $ii++) { my $bn = $build_names->[$ii]; $bn =~ s/Clobber/Clbr/g; $bn =~ s/Depend/Dep/g; $bn = "$bn"; my $last_status = tb_last_status($ii); if ($last_status eq 'busted') { if ($form{nocrap}) { print ""; } else { print ""; } } else { print ""; } } print "\n"; print ""; print ""; print "\n"; } sub print_table_footer { print "
Build TimeGuilty$bn"; print "$bn$bn
Click time to
see changes
", "since then
", "Click name to see what they did
\n"; my $nextdate = $maxdate - $hours*60*60; print &open_showbuilds_href(maxdate=>"$nextdate", nocrap=>'1') ."Show previous $hours hours
"; if ($hours != 24) { my $save_hours = $hours; $hours = 24; print &open_showbuilds_href(maxdate=>"$nextdate", nocrap=>'1') ."Show previous 24 hours"; $hours = $save_hours; } print "

", "Administrate Tinderbox Trees
\n"; } sub open_showbuilds_url { my %args = ( nocrap => "$form{nocrap}", @_ ); my $url = "${rel_path}showbuilds.cgi?tree=$::tree"; $url .= "&hours=$hours" if $hours ne $default_hours; while (my ($key, $value) = each %args) { $url .= "&$key=$value" if $value ne ''; } return $url; } sub open_showbuilds_href { return ""; } sub query_ref { my ($td, $mindate, $maxdate, $who) = @_; my $output = ''; $output = "{cvs_branch} ne 'HEAD'; $output .= "&cvsroot=$td->{cvs_root}" if $td->{cvs_root} ne $default_root; $output .= "&date=explicit&mindate=$mindate"; $output .= "&maxdate=$maxdate" if $maxdate and $maxdate ne ''; $output .= "&who=$who" if $who and $who ne ''; $output .= ">"; } sub who_menu { my ($td, $mindate, $maxdate, $who) = @_; my $treeflag; my $qr = "${rel_path}../registry/who.cgi?email=". url_encode($who) . "&d=$td->{cvs_module}|$td->{cvs_branch}|$td->{cvs_root}|$mindate|$maxdate"; return ""; } # Check to see if anyone checked in during time slot. # ex. has_who_list(1); # Check for checkins in most recent time slot. # ex. has_who_list(1,5); # Check range of times. sub has_who_list { my ($time1, $time2) = @_; if (not defined(@who_check_list)) { # Build a static array of true/false values for each time slot. $who_check_list[$time_count - 1] = 0; for (my $tt = 0; $tt < $time_count; $tt++) { $who_check_list[$tt] = 1 if each %{$who_list->[$tt]}; } } if ($time2) { for (my $ii=$time1; $ii<=$time2; $ii++) { return 1 if $who_check_list[$ii] } return 0 } else { return $who_check_list[$time1]; } } BEGIN { # Check bonsai tree for open/close state my $treestate = undef; my $checked_state = 0; sub _check_tree_state { my $tree = shift; $checked_state = 1; tb_load_treedata($tree); # Loading for the global, $bonsai_tree return unless defined $bonsai_tree and $bonsai_tree ne ''; local $_; $::BatchID=''; eval qq(require "../bonsai/data/$bonsai_tree/batchid.pl"); if ($::BatchID eq '') { warn "No BatchID in ../bonsai/data/$bonsai_tree/batchid.pl\n"; return; } open(BATCH, "<../bonsai/data/$bonsai_tree/batch-$::BatchID.pl") or print "can't open batch-$::BatchID.pl
"; while () { if (/^\$::TreeOpen = '(\d+)';/) { $treestate = $1; last; } } return; } sub is_tree_state_available { my $tree = shift; $tree = $::tree unless defined $tree; return 1 if defined $treestate; return 0 if $checked_state; _check_tree_state($tree); return is_tree_state_available(); } sub is_tree_open { my $tree = shift; $tree = $::tree unless defined $tree; _check_tree_state($tree) unless $checked_state; return $treestate; } } sub print_javascript { my $script; ($script = <<"__ENDJS") =~ s/^ //gm; __ENDJS print $script; } sub do_express { print "Content-type: text/html\nRefresh: 900\n\n\n"; my (%build, %times); tb_loadquickparseinfo($::tree, \%build, \%times); my @keys = sort keys %build; my $keycount = @keys; my $tm = &print_time(time); print ""; print "\n"; foreach my $buildname (@keys) { print ""; } print "
"; print &open_showbuilds_href."$::tree"; if (is_tree_state_available()) { print (is_tree_open() ? ' is open' : ' is closed'); } print ", $tm
$buildname
\n"; } # This is essentially do_express but it outputs a different format sub do_panel { print "Content-type: text/html\n\n\n" unless $form{static}; my (%build, %times); tb_loadquickparseinfo($::tree, \%build, \%times); print q( ); # Hack the panel link for now. print "
$::tree"; if (is_tree_state_available()) { print " is ", is_tree_open() ? 'open' : 'closed'; } # Add the current time my ($minute,$hour,$mday,$mon) = (localtime)[1..4]; my $tm = sprintf("%d/%d %d:%02d",$mon+1,$mday,$hour,$minute); print ", $tm
"; print ""; while (my ($name, $status) = each %build) { print ""; } print "
$name
"; } sub do_flash { print "Content-type: text/rdf\n\n" unless $form{static}; my (%build, %times); tb_loadquickparseinfo($::tree, \%build, \%times); my ($mac,$unix,$win) = (0,0,0); while (my ($name, $status) = each %build) { next if $status eq 'success'; $mac = 1, next if $name =~ /Mac/; $win = 1, next if $name =~ /Win/; $unix = 1; } print q{ }; my $busted = $mac + $unix + $win; if ($busted) { # Construct a legible sentence; e.g., "Mac, Unix, and Windows # are busted", "Windows is busted", etc. This is hideous. If # you can think of something better, please fix it. my $text; if ($mac) { $text .= 'Mac' . ($busted > 2 ? ', ' : ($busted > 1 ? ' and ' : '')); } if ($unix) { $text .= 'Unix' . ($busted > 2 ? ', and ' : ($win ? ' and ' : '')); } if ($win) { $text .= 'Windows'; } $text .= ($busted > 1 ? ' are ' : ' is ') . 'busted'; # The Flash spec says we need to give ctime. use POSIX; my $tm = POSIX::ctime(time()); $tm =~ s/^...\s//; # Strip day of week $tm =~ s/:\d\d\s/ /; # Strip seconds chop $tm; print qq{ $::tree $text $tm }; } print q{ }; } sub do_quickparse { print "Content-type: text/plain\n\n"; my @treelist = split /,/, $::tree; foreach my $tt (@treelist) { if (is_tree_state_available($tt)) { my $state = is_tree_open($tt) ? 'open' : 'closed'; print "State|$tt|$bonsai_tree|$state\n"; } my (%build, %times); tb_loadquickparseinfo($tt, \%build, \%times); foreach my $buildname (sort keys %build) { print "Build|$tt|$buildname|$build{$buildname}\n"; } } } sub do_rdf { print "Content-type: text/plain\n\n"; my $mainurl = "http://$ENV{SERVER_NAME}$ENV{SCRIPT_NAME}?tree=$::tree"; my $dirurl = $mainurl; $dirurl =~ s@/[^/]*$@@; my (%build, %times); tb_loadquickparseinfo($::tree, \%build, \%times); my $image = "channelok.gif"; my $imagetitle = "OK"; foreach my $buildname (sort keys %build) { if ($build{$buildname} eq 'busted') { $image = "channelflames.gif"; $imagetitle = "Bad"; last; } } print qq{ Tinderbox - $::tree Build bustages for $::tree $mainurl $imagetitle $dirurl/$image $mainurl }; if (is_tree_state_available()) { my $state = is_tree_open() ? 'open' : 'closed'; print "The tree is currently $state", "$mainurl\n"; } foreach my $buildname (sort keys %build) { if ($build{$buildname} eq 'busted') { print "$buildname is in flames", "$mainurl\n"; } } print "\n"; } # This is for Sprint phones sub do_hdml { print "Content-type: text/hdml\n\n" unless $form{static}; print q{ }; %state_symbols = (success=>'+',busted=>'!',testfailed=>'~'); if (is_tree_state_available()) { print "$::tree is " . (is_tree_open() ? 'open' : 'closed'); } my (%build, %times); tb_loadquickparseinfo($::tree, \%build, \%times); foreach my $buildname (sort keys %build) { print "$state_symbols{$build{$buildname}} $buildname\n"; } print q{ Legend:
+ : Good Build
! : Broken Build
~ : Tests Failed
}; }