#!perl.exe ############################################################################### # File: count_changes # # $Id: count_changes,v 1.7 2000/05/11 22:30:11 mberney Exp $ # # Description: # This is a perl program used to extract source code changes from a CVS # codebase. It uses the 'cvs history' and 'cvs annotate' commands to collect # the total number of files (and lines changed within those files) since the # specified date. # # The output report is generated in HTML format. Below is an example of # the report. # # Source File Total Lines Daily (%) Weekly (%) # # Aal5Stat.idl 117 0 (0%) 3 (2%) # Atm622.idl 66 0 (0%) 2 (3%) # # Usage: # count_changes [-d ] [-o ] [-w ] # # Where: # is the time from which the data are collected in the form: # YYYY-MM-DD. An error is generated if the date is greater than # the current date. If not specified, then 'yesterday' is used. # is the name of the file to store the results. If not # specified, output goes to YYYY-MM-DD.html. # is the top level directory to start counting # # (c) Copyright, Oresis Communications, Inc. All Rights Reserved ############################################################################### # Standard Perl Packages use Time::Local; # Effeciently convert time from local and GMT time use Getopt::Std; # Process single-character switches with switch clustering ########## # Process Command Line getopts("d:o:w:"); ########## # Initialization unless ($opt_w) { die "ERROR: No work area specified\n"; } # Check for CVSROOT environment variable if ($ENV{CVSROOT} eq "") { die "ERROR: \"\$CVSROOT\" Environment variable does not exist Unable to continue processing!\n\n"; } $seven_days = 604800; # Number of seconds in 7 days (60s * 60m * 24h * 7d) $one_day = 86400; # Number seconds in 1 day # Lists to associate numeric days with text names @daylist = (Sun,Mon,Tue,Wed,Thu,Fri,Sat); @monlist = ("Jan","Feb","Mar","Apr","May","Jun","Jul", "Aug","Sep","Oct","Nov","Dec"); $root = $opt_w; # Work area directory for source code $total_file=0; # Number of source lines within a single file $total_lines=0; # Number of source lines processed in all files $num_files=0; # Number of files processed $daily_total=0; # Number of source lines changed daily $weekly_total=0; # Number of source lines changed weekly $last_sourcepath=""; # Current directory of sources being processed ######################################## # BEGIN PROGRAM # ######################################## ########## # Process Date String; Subtract 7-days to collect rate of # change statistics for 1 week if date not specified. unless ($opt_d) { # Get the current time ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $sec=0;$min=0;$hour=0; # convert to number of seconds $time = timelocal($sec,$min,$hour,$mday,$mon,$year); # subtract 7 days and convert back to constituent parts $time -= $seven_days; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time); $year+=1900; $mon++; $opt_d = "$year-$mon-$mday"; } # Get the current time ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $year +=1900; $date_string = "$monlist[$mon] $mday, $year"; $mon++; unless ($opt_o) { $opt_o = "$year-$mon-$mday.html"; # Default to YYYY-MM-DD.html } ########## # Open the output file for storing the results. print "Opening: \"$opt_o\" to store the report\n"; open (OUTFILE, ">$opt_o") || die "Unable to open \"$opt_d\" for output: $!"; ############################## # FORMAT HTML OUTPUT # # (DON'T CHANGE) # ############################## print OUTFILE <

Total Weekly Changes to Oresis Codebase: $date_string
$hour:$min:$sec

Total Weekly Changes to Oresis Codebase
ENDHEADER ########## # Generate CVS History file # # CVS History command: # -c = files that were committed # -a = all users # -l = just the last revision by that user # -D ; only files changed within one week # # sort command: # -b = ignore leading blank characters # -k = start_field,end_field (sort on directory name) # -u = unique; remove duplicates print "Extracting CVS History from \"$opt_d\" to \"$year-$mon-$mday\"\n"; print "This may take a while...\n"; open (HISTORY,"cvs -d $ENV{CVSROOT} history -calD $opt_d | sort -b -k 8,8 -k 7,7 -u|") || die "Unable to execute cvs history: $!\n"; ### DEBUGGING: MANUALLY GENERATED HISTORY FILE ### #$historyfile = "history.txt"; #open (HISTORY, "<$historyfile") || die "Unable to open '$historyfile': $!"; while () { @splitline = split /\s+/; # Split path by white space $sourcever = $splitline[5]; # Source File Version $sourcefile = $splitline[6]; # Source File Name $sourcepath = $splitline[7]; # Source path (not including root) # Skip if can't find the file next if (! -f "$root/$sourcepath/$sourcefile"); ########## # Skip these file extensions: ".plo", ".ubf", ".rbf", # ".ibf", ".a". These files are either binary format # or generated files. next if ($sourcefile =~ /\.plo/); next if ($sourcefile =~ /\.ubf/); next if ($sourcefile =~ /\.rbf/); next if ($sourcefile =~ /\.ibf/); next if ($sourcefile =~ /\.a/); next if ($sourcepath =~ /plo/); # Keep track of the number of sources processed $num_files++; print "Source = $root/$sourcepath/$sourcefile\n"; ########## # Collect file change statistics and accumulate results ($daily,$weekly,$total_file) = &changed_lines($sourcever,"$root/$sourcepath/$sourcefile"); ########## # Some problem with the CVS Annotate command. It doesn't record # lines that are deleted. As a result, the CVS History command may # indicate that a file is modified, but the CVS Annotate command # declares that NO lines have changed in the last week. These # files are skipped here to prevent the data from being skewed. next if ($daily == 0 && $weekly == 0); $total_lines += $total_file; $daily_total += $daily; $weekly_total += $weekly; ########## # Avoid divide-by-zero errors if ($total_file != 0) { $percent_daily = (($daily / $total_file) * 100); $percent_weekly = (($weekly / $total_file) * 100); } else { $percent_daily = 0; $percent_weekly = 0; } ########## # Print new source path when path changes. if ( $sourcepath ne $last_sourcepath ) { # print "Old Source Path = \"$last_sourcepath\"\n"; print OUTFILE "\n"; print OUTFILE "\n"; $last_sourcepath = $sourcepath; } ########## # Annotate output 'RED' when daily or weekly thresholds exceeded: # Daily changes >= 20% ; Weekly changes >= 50% if (($percent_daily >= 20) || ($percent_weekly >= 50)) { $font = "RED"; } else { $font = "BLACK"; } ########## # Print out daily and weekly source code changes (with color annotation) print OUTFILE "\n"; print OUTFILE ""; print OUTFILE ""; printf OUTFILE "",$daily,$percent_daily; printf OUTFILE "\n",$weekly,$percent_weekly; print OUTFILE "\n"; ### DEBUGGING ### #print "Source Lines = $total_file\n"; #printf ("Lines modified today = %d (%d%%)\n",$daily,$percent_daily); #printf ("Lines modified within 1 week = %d (%d%%)\n", # $weekly,$percent_weekly); } # End of History File ########## # Collect totals; avoid divide-by zero errors if ( $total_lines != 0 ) { $percent_daily_total = ($daily_total / $total_lines) * 100; $percent_weekly_total = ($weekly_total / $total_lines) * 100; } else { $percent_daily_total = 0; $percent_weekly_total = 0; } ########## # Print totals to output file. ### HEADING ### print OUTFILE "\n"; print OUTFILE ""; print OUTFILE ""; print OUTFILE ""; print OUTFILE "\n"; print OUTFILE "\n"; ### TOTALS ### print OUTFILE "\n"; print OUTFILE ""; print OUTFILE ""; printf OUTFILE "",$daily_total,$percent_daily_total; printf OUTFILE "\n",$weekly_total,$percent_weekly_total; print OUTFILE "\n"; ### DEBUGGING ### print "\n\nTotal Source Lines = $total_lines\n"; print "Total Lines Changed Today = $daily_total\n"; print "Total Lines Changed This Week = $weekly_total\n"; ########## # FORMAT HTML OUTPUT print OUTFILE < ENDHEADER close OUTFILE; print "Generated output file: \"$opt_o\" ...\n\n"; exit 0; ######################################## # END PROGRAM # ######################################## ############################################################################### # Subroutine: changed_lines - Record daily and weekly changes # # Arguments: # $sourcefile = source file from which to collect changes # # Description: # Use the CVS Annotate command to determine the number of changes that # have occurred with the last day and the last week. This is done by # converting the timestamp in the annotated file into a . Where # the is the number of seconds elapsed since 1 Jan 1970. Then # compare the to today's date. # # Returns list of: # a) Number of lines changed within the last day # b) Number of lines changed within the last week # c) Total number of lines in the file ############################################################################### sub changed_lines { my ($sourcever,$sourcefile) = @_; # my $sourcefile = $_[0]; # Source file from which to collect data print ">>changed_lines\n"; print " $sourcefile, $sourcever\n"; my $total_lines = 0; # Total number of lines in the source file my $daily_lines = 0; # Total number of lines changed within the last day my $weekly_lines = 0; # Total number of lines changed within the last week ########## # Hours, Minutes and Seconds are "zero-ed" out to account for # time differences during the day. Only the "date" is used. $sec=0;$min=0;$hour=0; ########## # Convert time into constituent parts ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $sec=0;$min=0;$hour=0; ########## # Convert localtime into num seconds from (1 Jan 1970) $time_today = timelocal($sec,$min,$hour,$mday,$mon,$year); ########## # Generate annotated file open (ANNOTATE, "cvs -d $ENV{CVSROOT} annotate -r $sourcever $sourcefile|") || die "Can't execute cvs annotate: $!"; ########## # PARSE ANNOTATE FILE while () { ########## # Extract Date String (only $date is used) ($f_revision, $user, $date) = /(\S+)\s+\((\S+)\s+(\S+)\)/; ########## # Extract Date String ($mday,$mon,$year) = ($date =~ /(\d+)-(\S+)-(\d+)/); if ($year == 00) { $year = 100; } # y2k offset $mon = &lindex($mon,@monlist); # convert to numeric ########## # Convert localtime into num seconds from (1 Jan 1970) $time = timelocal($sec,$min,$hour,$mday,$mon,$year); ########## # Increment counters if (($time_today - $time) <= $one_day) { $daily_lines++; } if (($time_today - $time) <= $seven_days) { $weekly_lines++; } $total_lines++; } ### DEBUGGING ### print "Total Lines = $total_lines\n"; printf ("Lines modified today = %d (%d%%)\n",$daily_lines,$percent_daily); printf ("Lines modified within 1 week = %d (%d%%)\n", $weekly_lines,$percent_weekly); ########## # Return the list of: # a) Number of lines changed within the last day # b) Number of lines changed within the last week # c) Total number of lines in the file return ($daily_lines,$weekly_lines,$total_lines); } ############################################################################### # Subroutine: lindex - return numeric index of value within a list # # Arguments: # $value = value to search for in the list # @list = list to search for value # # Description: # Scan through the list and return the index corresponding to # the specified value. If no value matching in list, then -1 # is returned. I'm sure there is a built-in way to do this. But # I couldn't figure it out. # # Returns: # 0-based index into list corresponding to $value or -1 if no match ############################################################################### sub lindex { my ($value, @list) = @_; my $cur = 0; for ($cur=0; $cur<=$#list; $cur++) { if ($value eq $list[$cur]) { return $cur } } return -1; }
Source FileTotal LinesDaily (%)Weekly (%)
$sourcepath
$sourcefile$total_file%s (%d%%)%s (%d%%)
Total FilesTotal LinesDaily (%)Weekly (%)
$num_files$total_lines%d (%d%%)%d (%d%%)