#!/usr/bin/perl -w # # Program: userloc.pl # # Synopsis: This program uses "cvs annotate" to get a summary of how many lines # of code the various developres are responsible for. It takes one # argument, the directory to process. If the argument is not specified # then the cwd is used. The directory must be an LLVM tree checked out # from cvs. # # Syntax: userloc.pl [-details|-recurse|-tag=tag|-html... ... # # Options: # -details # Print detailed per-directory information. # -recurse # Recurse through sub directories. Without this, only the # specified directory is examined # -tag=tag # Use "tag" to select the revision (as per cvs -r option) # -html # Generate HTML output instead of text output die "Usage userloc.pl [-details|-recurse|-tag=tag|-html] ..." if ($#ARGV < 0); my $tag = ""; my $details = 0; my $recurse = 0; my $html = 0; while ( substr($ARGV[0],0,1) eq '-' ) { if ($ARGV[0] eq "-details") { $details = 1 ; } elsif ($ARGV[0] eq "-recurse") { $recurse = 1; } elsif ($ARGV[0] =~ /-tag=.*/) { $tag = $ARGV[0]; $tag =~ s#-tag=(.*)#$1#; } elsif ($ARGV[0] eq "-html") { $html = 1; } else { die "Invalid option: $ARGV[0]"; } shift; } die "Usage userloc.pl [-details|-recurse|-tag=tag|-html] ..." if ($#ARGV < 0); my %Stats; my %StatsDetails; sub ValidateFile { my $f = $_[0]; my $d = $_[1]; return 0 if ( "$f" eq "configure"); if ( $d =~ ".*autoconf.*") { return 1 if ($f eq "configure.ac"); return 1 if ($f eq "AutoRegen.sh"); return 0; } return 1; } sub GetCVSFiles { my $d = $_[0]; my $files =""; open STATUS, "cvs -nfz6 status $d -l 2>/dev/null |" || die "Can't 'cvs status'"; while ( defined($line = ) ) { if ( $line =~ /^File:.*/ ) { chomp($line); $line =~ s#^File: ([A-Za-z0-9._-]*)[ \t]*Status:.*#$1#; $files = "$files $d/$line" if (ValidateFile($line,$d)); } } return $files; } my $annotate = "cvs annotate -lf "; if (length($tag) > 0) { $annotate = $annotate . " -r " . $tag; } sub ScanDir { my $Dir = $_[0]; my $files = GetCVSFiles($Dir); open (DATA,"$annotate $files 2>/dev/null |") || die "Can't read cvs annotation data"; my %st; while ( defined($line = ) ) { if ($line =~ /^[0-9.]*[ \t]*\(/) { $line =~ s#^[0-9.]*[ \t]*\(([a-zA-Z0-9_.-]*).*#$1#; chomp($line); $st{$line}++; $Stats{$line}++; } } $StatsDetails{$Dir} = { %st }; close DATA; } sub ValidateDirectory { my $d = $_[0]; return 0 if ($d =~ /.*CVS.*/); return 0 if ($d =~ /.*Debug.*/); return 0 if ($d =~ /.*Release.*/); return 0 if ($d =~ /.*Profile.*/); return 0 if ($d =~ /.*docs\/CommandGuide\/html.*/); return 0 if ($d =~ /.*docs\/CommandGuide\/man.*/); return 0 if ($d =~ /.*docs\/CommandGuide\/ps.*/); return 0 if ($d =~ /.*docs\/CommandGuide\/man.*/); return 0 if ($d =~ /.*docs\/HistoricalNotes.*/); return 0 if ($d =~ /.*docs\/img.*/); return 0 if ($d =~ /.*bzip2.*/); return 1 if ($d =~ /.*projects\/Stacker.*/); return 1 if ($d =~ /.*projects\/sample.*/); return 0 if ($d =~ /.*projects\/llvm-.*/); return 0 if ($d =~ /.*win32.*/); return 1; } my $RowCount = 0; sub printStats { my $dir = $_[0]; my $hash = $_[1]; my $user; my $total = 0; if ($RowCount % 10 == 0) { print " Directory\n"; foreach $user (sort keys %Stats) { print "",$user,"\n"; } print "\n"; } $RowCount++; if ($html) { print "",$dir,""; } else { print $dir,"\n"; } foreach $user (keys %{$hash}) { $total += $hash->{$user}; } foreach $user ( sort keys %Stats ) { my $v = $hash->{$user}; if (defined($v)) { if ($html) { printf "%d
(%2.1f%%)", $v, (100.0/$total)*$v; } else { printf "%8d (%4.1f%%): %s\n", $v, (100.0/$total)*$v, $user; } } elsif ($html) { print "- "; } } print "\n" if ($html); } my @ALLDIRS = @ARGV; if ($recurse) { $Dirs = join(" ", @ARGV); $Dirs = `find $Dirs -type d \! -name CVS -print`; @ALLDIRS = split(' ',$Dirs); } if ($html) { print "\n"; print "\n\n"; print " LLVM LOC Based On CVS Annotation\n"; print " \n"; print "\n"; print "
LLVM LOC Based On CVS Annotation
\n"; print "

This document shows the total lines of code per user in each\n"; print "LLVM directory. Lines of code are attributed by the user that last\n"; print "committed the line. This does not necessarily reflect authorship.

\n"; print "

The following directories were skipped:

\n"; print "
    \n"; } for $Dir (@ALLDIRS) { if ( -d "$Dir" && -d "$Dir/CVS" && ValidateDirectory($Dir) ) { ScanDir($Dir); } elsif ($html) { print "
  1. $Dir
  2. \n"; } } if ($html) { print "
\n"; print "\n"; } if ($details) { foreach $dir (sort keys %StatsDetails) { printStats($dir,$StatsDetails{$dir}); } } printStats("Total",\%Stats); if ($html) { print "
\n"; }