Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-10-25 10:05:53

0001 #!/usr/bin/env perl
0002 #____________________________________________________________________ 
0003 # File: LogFileParser.pl
0004 #____________________________________________________________________ 
0005 #  
0006 # Author: Shaun ASHBY <Shaun.Ashby@cern.ch>
0007 # Update: 2005-11-16 11:45:09+0100
0008 #
0009 #  Shaun ASHBY
0010 #
0011 #--------------------------------------------------------------------
0012 
0013 ######## Needed to find Template Toolkit. Can be removed if PERL5LIB defined instead #######
0014 BEGIN {
0015     if (! exists($ENV{PERL5LIB})) {
0016     # Elementary check of architecture:
0017     use Config; $PERLVERSION=$Config{version};
0018     # Default lib path for slc4:
0019     $LIBPATH = "/afs/cern.ch/cms/sw/slc4_ia32_gcc345/external/p5-template-toolkit/2.14/lib/site_perl/5.8.5/i386-linux-thread-multi";
0020     if ($PERLVERSION eq '5.8.0') { # Dirty: could be different version of Perl
0021         $LIBPATH = "/afs/cern.ch/cms/sw/slc3_ia32_gcc323/external/p5-template-toolkit/2.14/lib/site_perl/5.8.0/i386-linux-thread-multi";
0022     }
0023     # Check for 64-bit arch:
0024     if ($Config{archname} =~ /64/) {
0025         $LIBPATH="/afs/cern.ch/cms/sw/slc4_amd64_gcc345/external/p5-template-toolkit/2.14/lib/site_perl/5.8.5/x86_64-linux-thread-multi";
0026     }
0027     }
0028 }
0029 
0030 use Cwd;
0031 use File::Find;
0032 
0033 # To find Template Toolkit:
0034 use lib $LIBPATH;
0035 
0036 use Getopt::Long ();
0037 use Storable;
0038 use Time::localtime; # To get the day index;
0039 
0040 my $buildsummary={};
0041 my ($mainlogfile,$logfile,$workdir,$cmsrelease,$projectversion,$templatedir,$outputdir);
0042 my %opts;
0043 my %options =
0044     ("mainlog=s"     => sub { $mainlogfile = $_[1] }, # The logfile where SCRAM errors (missing packages etc.) can be found
0045      "workdir=s"     => sub { $workdir = $_[1] },     # The working dir from where log files can be found
0046      "release=s"     => sub { $projectversion = $_[1] },
0047      "templatedir=s" => sub { $templatedir = $_[1] }, # Where to find the templates
0048      "outputdir=s"   => sub { $outputdir = $_[1] },   # Where to write the HTML logs/summary
0049      "debug"         => sub { $debug = 1 },           # Show debugging info
0050      "help"          => sub { &usage(); exit(0) } );  # Show help and exit
0051 
0052 my $data={};
0053 my $dataobjs={};
0054 
0055 my $pkglist=[];
0056 my $currentpackage;
0057 my $lastpkglog;
0058 my $packagedata={};
0059 
0060 my $tmstruct = localtime;
0061 my $daynumber = $tmstruct->wday; # Get day index;
0062 
0063 my $mailalertdir;
0064 my $missingpackage="";
0065 my $problem_bfs={};
0066 my $scram_warnings=0;
0067 
0068 my $package_version;
0069 
0070 # Handle argument opts:
0071 Getopt::Long::config qw(default no_ignore_case require_order);
0072 
0073 if (! Getopt::Long::GetOptions(\%opts, %options)) {
0074     &usage(); exit(1);
0075 } else {
0076     # We must have a project release version:
0077     die "ERROR: You must give a project release version (e.g. --release=CMSSW_xyz).\n", unless ($projectversion);
0078     # We must have a working dir, an output dir and a template location:
0079     die "ERROR: You must give a working directory which points to the project log files (--workdir=/a/dir/path/tmp/<ARCH>)\n", unless ($workdir && -d $workdir);
0080     die "ERROR: You must give an output directory where package information can be written (--outputdir=/a/dir/path/XXXX).\n", unless ($outputdir);
0081     system("mkdir","-p",$outputdir), unless (-d $outputdir);
0082     $templatedir||=$ENV{LOGFILEPARSER_TEMPLATE_DIR};
0083     die "ERROR: Unable to find the templates for the log results (use --templatedir=/path/2/templates).\n", unless ($templatedir && -d $templatedir);
0084     
0085     # Debugging off by default:
0086     $debug||=0;
0087     $mainlogfile||="";
0088     
0089     # For mail alerts:
0090     $mailalertdir=$ENV{MAILALERT_DIR}||$outputdir."/nightly-alerts";
0091     system("mkdir","-p",$mailalertdir), unless (-d $mailalertdir);
0092     
0093     # Isolate the numerical day:
0094     $buildsummary->{CMSSW_RELEASE}="CMSSW_".$daynumber;
0095     $buildsummary->{CMSSW_VERSION}=$projectversion;
0096     $buildsummary->{CMSSW_BUILD_ARCH}=$ENV{SCRAM_ARCH} || 'slc3_ia32_gcc323';
0097     $buildsummary->{CMSSW_BUILD_HOST}=`hostname`;
0098     $buildsummary->{DAY_INDEX}=$daynumber;
0099 
0100     # Some counters to keep track of the number of each kind of errors. These statistics are
0101     # for the whole build:
0102     $buildsummary->{N_COMPILATION_ERRORS} = 0;
0103     $buildsummary->{N_LINK_ERRORS} = 0;
0104     $buildsummary->{N_OTHER_ERRORS} = 0;
0105     $buildsummary->{TOTAL_FAILED_PACKAGES} = 0;
0106 
0107     # Create a date string:
0108     my $datestring;
0109     chomp($datestring=`date +'%F'`);
0110     $buildsummary->{CMSSW_DATESTRING}=$datestring;
0111     # Get the list of packages from the TC:
0112     my $packagelistfromTC = &getpklistfromtc();
0113     
0114     # Traverse the source code tree looking for developer files. Use these files to 
0115     # generate a list of responsibles for each package. First, check for cached info.
0116     # This saves a long wait.
0117     # Global store for package Administrators, Developers and everyone ('all'):
0118     $responsibles={};
0119     my $cachefilename = "./peoplecache.db";
0120     
0121     if (-f $cachefilename) {
0122     # Retrieve the info from cache:
0123     print "logfile_parser: Retrieving info on developers/admins cached in $cachefilename.\n";
0124     $responsibles = retrieve($cachefilename);
0125     } else {
0126     # Populate a new cache file:
0127     use vars qw/*name *dir *prune/;
0128     
0129     *name   = *File::Find::name;
0130     *dir    = *File::Find::dir;
0131     *prune  = *File::Find::prune;
0132     
0133     $|=1;
0134     print "logfile_parser: Traversing source tree under ".$workdir."/../../src\n";    
0135     # Read the list of files found under directory:
0136     File::Find::find({wanted => \&collect}, $workdir."/../../src");
0137     # Cache the contents of $responsibles:
0138     store($responsibles,$cachefilename);
0139     print "logfile_parser: Info on developers/admins cached in $cachefilename.\n";
0140     print "logfile_parser: Collected package admin/developer info.","\n";
0141     }
0142     
0143     # Loop over packages:
0144     foreach my $p (sort keys %$packagelistfromTC) {
0145     chomp($p);  
0146     $package_version = $packagelistfromTC->{$p};
0147     # Check to see if there's a logfile and if
0148     # so, process it. Also make sure file is not
0149     # zero size:
0150     $logfile=$workdir."/cache/log/src/$p/build.log";
0151     if (-f $logfile && -s $logfile) {
0152         open(PLOG,"< $logfile") || die "$0: Unable to read package logfile $logfile!: $!","\n";
0153         while(<PLOG>) {
0154         chomp;
0155         push(@$pkglist,$p);
0156         $currentpackage = $p;
0157         my $concatpackname = $p; $concatpackname =~ s/\///g;
0158         # A note on the status numbers:
0159         # 1 [Compilation Error], 2 [Link Error], 3 [Dictionary Error]
0160         # The last error wins except if status!=0, then don't set to 2
0161         # => status=1,3 wins over 2, which is what we want
0162         #
0163         # Delegate handling of per-package info to a dedicated object:
0164         if (! exists($dataobjs->{$currentpackage})) {
0165             $dataobjs->{$currentpackage} = new PackageResults($currentpackage,$package_version,$responsibles->{$currentpackage});
0166         }
0167         
0168         # Look for error messages in the log file:
0169         # Linker errors. Report the missing library:
0170         if ($_ =~ m|/usr/bin/ld: cannot find -l(.*?)$|) {
0171             $buildsummary->{N_LINK_ERRORS}++;
0172             $dataobjs->{$currentpackage}->log($_);
0173             $dataobjs->{$currentpackage}->status(2);
0174             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS (missing library \"".$1."\")");                  
0175             # Look for matches containing errors from gmake. Determine whether it was from
0176             # a failed link command, or from a compilation error:
0177         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/src/$concatpackname/.*?\.so|) {
0178             $buildsummary->{N_LINK_ERRORS}++;
0179             $dataobjs->{$currentpackage}->log($_);
0180             $dataobjs->{$currentpackage}->status(2);
0181             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for package library");
0182         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/src/$concatpackname/.*?\.o|) {
0183             $buildsummary->{N_COMPILATION_ERRORS}++;
0184             $dataobjs->{$currentpackage}->log($_);
0185             $dataobjs->{$currentpackage}->status(1);
0186             $dataobjs->{$currentpackage}->error(1,"COMPILATION ERRORS for package");            
0187         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/bin/(.*?)/.*?\.o|) {
0188             $buildsummary->{N_COMPILATION_ERRORS}++;
0189             $dataobjs->{$currentpackage}->log($_);
0190             $dataobjs->{$currentpackage}->status(1);
0191             $dataobjs->{$currentpackage}->error(1,"COMPILATION ERRORS for executable $1 in bin");
0192         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/bin/(.*?)/\1|) {
0193             $buildsummary->{N_LINK_ERRORS}++;
0194             $dataobjs->{$currentpackage}->log($_);
0195             $dataobjs->{$currentpackage}->status(2);
0196             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for executable $1 in bin");
0197         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/bin/(.*?)/lib\1\.so|) {
0198             $buildsummary->{N_LINK_ERRORS}++;
0199             $dataobjs->{$currentpackage}->log($_);
0200             $dataobjs->{$currentpackage}->status(2);
0201             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for shared library $1 in bin");
0202         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/stubs/lib(.*?)\.so|) {
0203             $buildsummary->{N_LINK_ERRORS}++;
0204             $dataobjs->{$currentpackage}->log($_);
0205             $dataobjs->{$currentpackage}->status(2);
0206             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for shared library $1 in test/stubs");
0207         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/(.*?)/.*?\.so|) {
0208             $buildsummary->{N_LINK_ERRORS}++;
0209             $dataobjs->{$currentpackage}->log($_);
0210             $dataobjs->{$currentpackage}->status(2);
0211             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for shared library $1 in test");
0212         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/stubs/.*?\.o|) {
0213             $buildsummary->{N_COMPILATION_ERRORS}++;
0214             $dataobjs->{$currentpackage}->log($_);
0215             $dataobjs->{$currentpackage}->status(1);
0216             $dataobjs->{$currentpackage}->error(1,"COMPILATION ERRORS for library in test/stubs");      
0217         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/(.*?)/.*?\.o|) {
0218             $buildsummary->{N_COMPILATION_ERRORS}++;
0219             $dataobjs->{$currentpackage}->log($_);
0220             $dataobjs->{$currentpackage}->status(1);
0221             $dataobjs->{$currentpackage}->error(1,"COMPILATION ERRORS for executable $1 in test");
0222         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/(.*?)\.so|) {
0223             $buildsummary->{N_LINK_ERRORS}++; 
0224             $dataobjs->{$currentpackage}->log($_);
0225             $dataobjs->{$currentpackage}->status(2);
0226             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for shared library $1 in test");
0227         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/(.*?)\.o|) {
0228             $buildsummary->{N_COMPILATION_ERRORS}++;
0229             $dataobjs->{$currentpackage}->log($_);
0230             $dataobjs->{$currentpackage}->status(1);
0231             $dataobjs->{$currentpackage}->error(1,"COMPILATION ERRORS for executable $1 in test");          
0232         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/(.*?)/\1|) {
0233             $buildsummary->{N_LINK_ERRORS}++;
0234             $dataobjs->{$currentpackage}->log($_);
0235             $dataobjs->{$currentpackage}->status(2);
0236             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for executable $1 in test");
0237         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/src/$concatpackname/classes_rflx\.cpp|) {
0238             $buildsummary->{N_OTHER_ERRORS}++;
0239             $dataobjs->{$currentpackage}->log($_);
0240             $dataobjs->{$currentpackage}->status(3);
0241             $dataobjs->{$currentpackage}->error(3,"DICTIONARY GEN ERRORS in package");
0242         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/plugins/(.*?)/.*?\.o|) {
0243             $buildsummary->{N_COMPILATION_ERRORS}++;
0244             $dataobjs->{$currentpackage}->log($_);
0245             $dataobjs->{$currentpackage}->status(1);
0246             $dataobjs->{$currentpackage}->error(1,"COMPILATION ERRORS for SEAL PLUGIN $1 in plugins");
0247         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/plugins/(.*?)/lib\1\.so|) {
0248             $buildsummary->{N_LINK_ERRORS}++;
0249             $dataobjs->{$currentpackage}->log($_);
0250             $dataobjs->{$currentpackage}->status(2);
0251             $dataobjs->{$currentpackage}->error(2,"LINK ERRORS for SEAL PLUGIN library $1 in plugins");
0252         } elsif ($_ =~ m|^gmake: \*\*\* .*?/src/$currentpackage/test/data/download\.url|) {
0253             $buildsummary->{N_OTHER_ERRORS}++;
0254             $dataobjs->{$currentpackage}->log($_);
0255             $dataobjs->{$currentpackage}->status(3);
0256             $dataobjs->{$currentpackage}->error(3,"DATA FILE COPY ERROR for file in data/download.url in test");
0257         } elsif ($_ =~ m|^gmake: \*\*\* .*$|) {
0258             # For a misc error line which failed to match any of the other rules, dump
0259             # some output for the line that a new regexp is needed for:
0260             print STDERR "logfile_parser.pl: No regexps matched the line \"".$_."\"\n";
0261         } else {
0262             # Just keep the logged info:
0263             $dataobjs->{$currentpackage}->log($_);
0264         }       
0265         }
0266         # Keep track of the number of packages with errors:
0267         $buildsummary->{TOTAL_FAILED_PACKAGES}++, if ($dataobjs->{$currentpackage}->status() != 0);
0268         close(PLOG);
0269     } else {
0270         print STDERR "WARNING: Missing log file (or file is zero-length) for $p\n";
0271     }
0272     }
0273 
0274     # See if user supplied location of main build log:
0275     if (-f $mainlogfile) {
0276     # Open the main logfile to look for SCRAM warnings and cyclic deps:
0277     open(BUILDLOG,"< $mainlogfile") || die "$0: Unable to read main logfile $mainlogfile!: $!","\n";
0278     # Create temp store for main package info (cycles..):
0279     my $mainobj=new PackageResults('MAIN',$projectversion);
0280     my $cycles=0;
0281 
0282     while(<BUILDLOG>) {
0283         chomp;
0284         # Collect cyclic deps messages. These messages will result in a complete
0285         # build failure:
0286         if ($_ =~ m|^SCRAM buildsystem ERROR:   Cyclic dependency (.*?) <--------> (.*?)$|) {
0287         $cycles=1;
0288         $mainobj->status(4);
0289         $mainobj->error(4,"$1 <--> $2");
0290         } else {
0291         # Also scan for WARNING: messages from SCRAM concerning packages without BuildFiles
0292         # (or non-existent packages):
0293         if ($_ =~ m|^WARNING: Unable to find package/tool called (.*?)$|) {
0294             $scram_warnings=1;
0295             $missingpackage=$1;
0296         }
0297         
0298         if ($_ =~ m|.*?in current project area \(declared at (.*?)\)$| && $missingpackage ne "") {
0299             if (exists($problem_bfs->{$1})) {
0300             push(@{$problem_bfs->{$1}},$missingpackage);
0301             }
0302             else {
0303             $problem_bfs->{$1} = [ $missingpackage ];
0304             }         
0305             $missingpackage="";
0306         }
0307         }       
0308     }
0309     
0310     close(BUILDLOG);
0311     # If there were cycles, keep the data object for MAIN:
0312     if ($cycles) {
0313         $dataobjs->{'MAIN'} = $mainobj;
0314     }
0315     
0316     # Dump missing packages/problem BuildFile report:
0317     my $scramlog=$outputdir."/scram_warnings.log";
0318     open(SCRAM_WARNINGS,"> $scramlog") || die "Unable to open $scramlog for writing:$!\n";;
0319     
0320     if ($scram_warnings) {
0321         # Write to a file somewhere:
0322         my ($pk, $dep); 
0323         print SCRAM_WARNINGS "\n\n";
0324         while (($pk, $dep)= each %{$problem_bfs}) {
0325         print SCRAM_WARNINGS "\n-> Location $pk has incorrect dependencies (to be removed from $pk/BuildFile): \n\t",join(" ",@$dep),"\n";
0326         print SCRAM_WARNINGS "\n";
0327         }
0328     }
0329     else {
0330         print SCRAM_WARNINGS "\n No SCRAM BuildFile warnings for this build: congratulations!","\n";
0331     }
0332     
0333     close(SCRAM_WARNINGS);
0334     } else {
0335     print "logfile_parser: No main log given. Skipping scanning for SCRAM messages.\n";
0336     }
0337      
0338     # Write the logs for each package:
0339     foreach my $p (sort keys %$dataobjs) {
0340     # Get the package data object:
0341     my $pdata = $dataobjs->{$p};
0342     # Check to see if there was a log for MAIN. If so
0343     # it means a big failure:
0344     if ($p eq 'MAIN') {
0345         # Check status, just to make sure:
0346         if ($pdata->status() == 4) {
0347         # Jump out of the loop:
0348         last;
0349         }
0350     } else {
0351         my $packagehtmllogfile = $pdata->subsystem()."_".$pdata->packname().".log.html";
0352         # Prepare the data for the summary page:
0353         if ($pdata->status()) {
0354         print "Package $p had errors.","\n", if ($debug);
0355         # Call the subroutine to prepare an email to be sent to the admins:
0356         &write_alert_mail($pdata, $templatedir);
0357         }
0358         
0359         # Create an HTML log page for this package:
0360         &log2html($pdata, $packagehtmllogfile, $templatedir, $outputdir);
0361     }
0362     }
0363     
0364     # Dump the main summary page:
0365     $buildsummary->{summarydata} = $dataobjs;
0366     
0367     &dumpmainpage($buildsummary, $templatedir, $outputdir);    
0368     # Dump out a final goodbye and stats on N packages processed:
0369     my $npackages=scalar(keys %$responsibles);
0370     print "logfile_parser: Looked at ",$npackages," packages.","\n";
0371     print "\n";
0372 }
0373 
0374 #### Subroutines ####
0375 sub dumpmainpage() {
0376     my ($builddata,$templatedir,$outputdir)=@_;
0377     my $summaryfiletmpl="buildsummary.html.tmpl";
0378     
0379     use Template;
0380     
0381     # Template toolkit parameters:
0382     my $template_config = {
0383     INCLUDE_PATH => $templatedir,
0384     EVAL_PERL    => 1 
0385     };
0386     
0387     # Prepare the data for the bootstrap file and requirements:
0388     my $template_engine = Template->new($template_config) || die $Template::ERROR, "\n";   
0389     $template_engine->process($summaryfiletmpl, $builddata, $outputdir."/index.html")
0390     || die "Template error: ".$template_engine->error;   
0391 }
0392 
0393 sub log2html() {
0394     my ($pdata,$packagehtmllogfile,$templatedir,$outputdir)=@_;
0395     my $packagehtmllogtmpl="package.log.html.tmpl";
0396     my $tdata = { package_data => $pdata };
0397     
0398     use Template;
0399     
0400     # Template toolkit parameters:
0401     my $template_config = {
0402     INCLUDE_PATH => $templatedir,
0403     EVAL_PERL    => 1 
0404     };
0405 
0406     # Prepare the data for the bootstrap file and requirements:
0407     my $template_engine = Template->new($template_config) || die $Template::ERROR, "\n";   
0408     $template_engine->process($packagehtmllogtmpl, $tdata, $outputdir."/".$packagehtmllogfile)
0409     || die "Template error: ".$template_engine->error;
0410 }
0411 
0412 sub write_alert_mail() {
0413     my ($pdata, $templatedir)=@_;
0414     # Prepare the data for the template. Use the project version (e.g. CMSSW_xxxx-xx-xx)
0415     # as the release, rather than CMSSW_x: 
0416     my $tdata = {
0417     CMSSW_RELEASE => $projectversion,
0418     PACKAGE_OBJ => $pdata
0419     };
0420     
0421     use Template;
0422     
0423     # Template toolkit parameters:
0424     my $template_config = {
0425     INCLUDE_PATH => $templatedir,
0426     EVAL_PERL    => 1 
0427     };
0428     
0429     # Loop over all admins for the package:   
0430     foreach my $admin (split(" ",$pdata->responsibles("administrators"))) {
0431     # Set the name of the admin as SENDTO in the mail stub (ready to be interpreted by 
0432     # the mailing script):
0433     $tdata->{THIS_ADMIN} = $admin;
0434     # Generate a unique file name for the current package/administrator combination. For 
0435     # the mailing step, the first line of the mail stub contains the "to:" address.   
0436     srand();
0437     $fileid=int(rand 99999999)+1;
0438     print "logfile_parser: Preparing mail alert (fileid=",$fileid,") for package ".$pdata->fullname(),"\n";
0439     my $mailfile = $mailalertdir."/".$fileid.".mail";   
0440     my $template_engine = Template->new($template_config) || die $Template::ERROR, "\n";      
0441     $template_engine->process("alert_mail_stub.tmpl", $tdata, $mailfile)
0442         || die "Template error: ".$template_engine->error;      
0443     }
0444 }
0445 
0446 sub collect() {
0447     my $persontype;
0448     my $persondata={ 'administrators' => [], 'developers' => []};
0449     
0450     if (my ($packagename) = ($name =~ m|.*?/src/(.*?)/.admin/developers|)) {
0451     open(DEVELOPERS, "$name") || die "$name: $!","\n";
0452     while(<DEVELOPERS>) {
0453         chomp;
0454         # Ignore comment lines:
0455         next if ($_ =~ /^\#/);
0456         # Look for the type of person tag (Administrators or Developers):
0457         if ($_ =~ m|>(.*?)$|) {     
0458         $persontype=lc($1);
0459         }
0460 
0461         if ($_ =~ m|.*:.(.*?)@(.*)|) {
0462         my $address="$1\@$2";      
0463         # Check to avoid duplication of admins (where a developer file contains the
0464         # same email address more than once):
0465         if (! grep($address eq $_, @{$persondata->{$persontype}})) {
0466             push(@{$persondata->{$persontype}},$address);
0467         }
0468         }
0469     }
0470       
0471     # Close the file:
0472     close(DEVELOPERS);
0473     
0474     # Set the value of the administrators and developers entries for this package:
0475     $responsibles->{$packagename}->{'all'}="";
0476     foreach my $pertype ('administrators', 'developers') {
0477         # Also store all persons as a single string:
0478         $responsibles->{$packagename}->{'all'}.=" ".join(" ",@{$persondata->{$pertype}});
0479         $responsibles->{$packagename}->{$pertype}=join(" ",@{$persondata->{$pertype}});
0480     }
0481     }   
0482 }
0483 
0484 sub getpklistfromtc() {
0485     # Based on script by D.Lange.
0486     #
0487     # Subroutine to get a list of packages/tags for a given release:
0488     # Check the version of wget.
0489     # --no-check-certificate needed for 1.10 and above:
0490     my $wgetver = (`wget --version` =~ /^GNU Wget 1\.1.*?/);
0491     my $options = ""; $options = "--no-check-certificate", if ($wgetver == 1);
0492     my $gotpacks=0;
0493     
0494     open(CMSTCQUERY,"wget $options  -nv -o /dev/null -O- 'https://cmstags.cern.ch/tc/public/CreateTagList?release=$projectversion' |");
0495     
0496     my %tags;
0497     while ( <CMSTCQUERY> ) {
0498     if ( $_ =~ /td/) {
0499         my @sp1=split(' ',$_,99);
0500         my $pack=$sp1[2];
0501         my $tag=$sp1[5];
0502         $tags{$pack}=$tag;
0503         $gotpacks++;
0504     }
0505     }
0506     
0507     close CMSTCQUERY;
0508     # Die if no tags found (i.e. release doesn't exist):
0509     die "$0: No packages found in release $projectversion. Perhaps $projectversion doesn't exist?\n" if ($gotpacks == 0);
0510     return \%tags;
0511 }
0512 
0513 sub usage() {
0514     my $hstring="Usage:\n\n$0 [-h] [-d] --release=<version>  --workdir=<workingdir>  --outputdir=<dir>  [-t <DIR>]\n";
0515     $hstring.="\n";
0516     $hstring.="--mainlog | -m              Location of main logfile, if you want to see SCRAM messages. Optional.\n";
0517     $hstring.="--workdir | -w              The project working directory where the log files can be found.\n";
0518     $hstring.="--release | -r              The project release version (e.g. CMSSW_xxxx-xx-xx).\n";
0519     $hstring.="--outputdir | -o DIR        Where to write the HTML log files and summary page.\n";
0520     $hstring.="--templatedir | -t DIR      Set location where templates reside if LOGFILEPARSER_TEMPLATE_DIR not set.\n";
0521     $hstring.="\n";
0522     $hstring.="--debug | -d                Debug mode ON (off by default).\n";
0523     $hstring.="--help | -h                 Show usage information.\n";
0524     $hstring.="\n";
0525     print $hstring,"\n";
0526 }
0527 
0528 package PackageResults;
0529 
0530 sub new() {
0531     my $proto=shift;
0532     my $class=ref($proto) || $proto;
0533     my $self={};
0534     bless($self,$class);
0535     my ($fullname, $version,$responsibles)=@_;
0536     $self->{FULLNAME} = $fullname;
0537     $self->{VERSION} = $version;
0538     $self->{RESPONSIBLES} = $responsibles || {};
0539     $self->{LOG} = [];
0540     $self->{STATUS} = 0;
0541     # Get the subsystem and package names from the full name:
0542     my ($subsystem,$packname)=split("/",$fullname);
0543     $self->{SUBSYSTEM} = $subsystem;
0544     $self->{PACKNAME} = $packname;
0545     
0546     return $self;
0547 }
0548 
0549 sub fullname() {
0550     my $self=shift;
0551     @_ ? $self->{FULLNAME} = shift
0552     : $self->{FULLNAME};
0553 }
0554 
0555 sub packname() {
0556     my $self=shift;
0557     @_ ? $self->{PACKNAME} = shift
0558     : $self->{PACKNAME};
0559 }
0560 
0561 sub subsystem() {
0562     my $self=shift;
0563     @_ ? $self->{SUBSYSTEM} = shift
0564     : $self->{SUBSYSTEM};
0565 }
0566 
0567 sub version() {
0568     my $self=shift;
0569     @_ ? $self->{VERSION} = shift
0570     : $self->{VERSION};
0571 }
0572 
0573 sub status() {
0574     my $self=shift;
0575     my ($status)=shift;
0576     # Only change status to 2 (link errors) if
0577     # current status is 0 (otherwise, impossible
0578     # to distinguish between link errors only and
0579     # compilation errors + link errors in same pkg.).
0580     if ($status) { # status is >= 1
0581     if ($self->{STATUS} == 0 && $status >= 2) {
0582         $self->{STATUS} = $status;
0583     } elsif ($self->{STATUS} > 0) {
0584     }  else {
0585         $self->{STATUS} = 1;
0586     }
0587     } else {
0588     return $self->{STATUS};
0589     }
0590 }
0591 
0592 sub log() {
0593     my $self=shift;
0594     if (@_) {
0595     push(@{$self->{LOG}},$_[0]);
0596     } else {
0597     return join("\n",@{$self->{LOG}});
0598     }
0599 }
0600 
0601 sub responsibles() {
0602     my $self=shift;
0603     my ($type)=@_;
0604     if (exists($self->{RESPONSIBLES}->{$type})) {
0605     return $self->{RESPONSIBLES}->{$type};
0606     } else {
0607     return "";
0608     }
0609 }
0610 
0611 sub error() {
0612     my $self=shift;
0613     my ($type,$msg)=@_;
0614 
0615     if (exists($self->{ERRORS}->{$type})) {
0616     if (exists($self->{ERRORS}->{$type}->{$msg})) {
0617         $self->{ERRORS}->{$type}->{$msg}++;
0618     } else {
0619         $self->{ERRORS}->{$type}->{$msg} = 1;
0620     }
0621     } else {
0622     $self->{ERRORS}->{$type}->{$msg} = 1;
0623     }
0624 
0625 }
0626 
0627 sub nErrorsByType() {
0628     my $self=shift;
0629     my ($type)=@_;
0630     my $n_errors=0;
0631 
0632     if (exists($self->{ERRORS}->{$type})) {
0633     foreach my $err (keys %{$self->{ERRORS}->{$type}}) {
0634         $n_errors += $self->{ERRORS}->{$type}->{$err};
0635     }
0636     }
0637     return $n_errors;
0638 }
0639 
0640 sub package_errors() {
0641     my $self=shift;
0642     my ($stat)=@_;
0643 
0644     if ($stat) {
0645     if (exists($self->{ERRORS}->{$stat})) {
0646         return $self->{ERRORS}->{$stat};
0647     }
0648     } else {
0649     return $self->{ERRORS};
0650     }
0651 }