#! /usr/bin/env python
# $Id: //guest/thomas_quinot/perforce/utils/metrics/filemetrics.py#2 $
##
## Written by Scott Pasnikowski around the time of 4/22/99
## @symantec corp. Home of the Norton utilities
##
## Under duress of pesky QA people...
##
## This comes with no guarantee whatsoever... on any level.
##
## Consider this to be under the linux type liscense thingy
## ( Don't recall what its called GNU or copyleft or whatever )
## and if anyone complains my manager du jour said I could give it away.
## ( he really did )
##
import sys, os, string, re
# This func corresponds to p4 diff2 with the -dn switch
def extract_metrics_counts_rcs( depot_root, label_one, label_two ):
# Does a p4 changes depot_root@label_one depot_root@label_two
# depot_root is used to limit the diff range to a single "project"
# or something even smaller.. like a single file
# we then parse the output and add up the totals
# quote the 2 params for the cases where the depot path contains a space
command = 'p4 diff2 -dn \"' + depot_root + label_one + '\" \"' + depot_root + label_two + '\"'
lines_added = 0
lines_changed = 0
lines_deleted = 0
#sys.stdout.write('Extraction function using ' + command +' \n' )
# break the line up according to...
# sample line: a85 2
# operation (line)location (Number of lines affected)count
p = re.compile( r'^([ad])([0-9]+) (\d+)' )
for line in os.popen( command,'r').readlines():
tmp = line[0:1]
if tmp == 'a' or tmp == 'd':
# only do this if we have a match otherwise groups() will blow chunks diffing an rtf
m = p.match( line )
if m:
(operation, location, count) = re.match( r'^([ad])([0-9]+) (\d+)', line).groups()
#sys.stdout.write( '>' + operation + '< >' + location + '< >' + count + '<\n' )
if tmp == 'a':
lines_added = lines_added + int( count )
elif tmp == 'd':
lines_deleted = lines_deleted + int( count )
return lines_added, lines_deleted
# This func corresponds to p4 diff2 with the -dc switch
def extract_metrics_counts_context( depot_root, label_one, label_two ):
# Does a p4 changes depot_root@label_one depot_root@label_two
# depot_root is used to limit the diff range to a single "project"
# or something even smaller.. like a single file
# we then parse the output and add up the totals
# quote the 2 params for the cases where the depot path contains a space
command = 'p4 diff2 -dc \"' + depot_root + label_one + '\" \"' + depot_root + label_two + '\"'
lines_added = 0
lines_changed = 0
lines_deleted = 0
#sys.stdout.write('Extraction function using ' + command +' \n' )
for line in os.popen( command,'r').readlines():
# sample line: Change number on date by name@machine 'comment'
# dummy ch#
#sys.stdout.write( line + '\n' )
tmp = line[0:1]
if tmp == '+':
lines_added = lines_added + 1
elif tmp == '-':
lines_deleted = lines_deleted + 1
elif tmp == '!':
lines_changed = lines_changed +1
return lines_added,lines_changed,lines_deleted
def extract_metrics_counts_summary( depot_root, label_one, label_two ):
# Does a p4 changes depot_root@label_one depot_root@label_two
# depot_root is used to limit the diff range to a single "project"
# or something even smaller.. like a single file
# we then parse the output and add up the totals
# quote the 2 params for the cases where the depot path contains a space
command = 'p4 diff2 -ds \"' + depot_root + label_one + '\" \"' + depot_root + label_two + '\"'
lines_added = 0
add_chunks = 0
lines_deleted = 0
delete_chunks = 0
lines_changed = 0
change_chunks = 0
extra_data = 0
#sys.stdout.write('Extraction function using ' + command +' \n' )
# This will match lines starting with
exp1 = re.compile( r'^["add""deleted""changed]"' )
#
# The next 3 eat the add deleted and changed lines
# it could be one expression I suppose
#
exp2 = re.compile( r'^(add) (\d+) (chunks) (\d+)' )
exp3 = re.compile( r'^(deleted) (\d+) (chunks) (\d+)' )
exp4 = re.compile( r'^(changed) (\d+) (chunks) (\d+) / (\d+)' )
for line in os.popen( command,'r').readlines():
# only do this if we have a match
m2 = exp2.search( line )
m3 = exp3.search( line )
m4 = exp4.search( line )
if m2:
( operation, count1, chunks, count2 ) = m2.groups()
add_chunks = add_chunks + int( count1 )
lines_added = lines_added + int( count2 )
#sys.stdout.write( count2 + '\n' )
elif m3:
( operation, count1, chunks, count2 ) = m3.groups()
delete_chunks = delete_chunks + int( count1 )
lines_deleted = lines_deleted + int( count2 )
#sys.stdout.write( count2 + '\n' )
elif m4:
( operation, count1, chunks, count2, count3 ) = m4.groups()
change_chunks = change_chunks + int( count1 )
extra_data = extra_data + int( count2 )
lines_changed = lines_changed + int( count3 )
#sys.stdout.write( count3 + '\n' )
return lines_added,add_chunks,lines_deleted,delete_chunks,lines_changed,change_chunks, extra_data
# This func corresponds to p4 diff2 with the -du switch
def extract_metrics_counts_unified( depot_root, label_one, label_two ):
# Does a p4 changes depot_root@label_one depot_root@label_two
# depot_root is used to limit the diff range to a single "project"
# or something even smaller.. like a single file
# we then parse the output and add up the totals
# quote the 2 params for the cases where the depot path contains a space
command = 'p4 diff2 -du \"' + depot_root + label_one + '\" \"' + depot_root + label_two + '\"'
lines_added = 0
lines_deleted = 0
#sys.stdout.write('Extraction function using ' + command +' \n' )
for line in os.popen( command,'r').readlines():
# sample line: Change number on date by name@machine 'comment'
# dummy ch#
#sys.stdout.write( line + '\n' )
tmp = line[0:1]
if tmp == '+':
lines_added = lines_added + 1
elif tmp == '-':
lines_deleted = lines_deleted + 1
return lines_added, lines_deleted
# This func corresponds to p4 diff2 with no switchs
def extract_metrics_counts_flat( depot_root, label_one, label_two ):
# Does a p4 changes depot_root@label_one depot_root@label_two
# depot_root is used to limit the diff range to a single "project"
# or something even smaller.. like a single file
# we then parse the output and add up the totals
# quote the 2 params for the cases where the depot path contains a space
command = 'p4 diff2 \"' + depot_root + label_one + '\" \"' + depot_root + label_two + '\"'
lines_added = 0
lines_deleted = 0
lines_changed = 0
#sys.stdout.write('Extraction function using ' + command +' \n' )
expFile = re.compile( r'^====' )
# The next 4 eat the add deleted and changed lines
# it could be one expression I suppose
#
exp1 = re.compile( r'^(\d+)([acd])(\d+)' )
exp2 = re.compile( r'^(\d+),(\d+)([acd])(\d+)' )
exp3 = re.compile( r'^(\d+)([acd])(\d+),(\d+)' )
exp4 = re.compile( r'^(\d+),(\d+)([acd])(\d+),(\d+)' )
for line in os.popen( command,'r').readlines():
# only do this if we have a match
m1 = exp1.search( line )
m2 = exp2.search( line )
m3 = exp3.search( line )
m4 = exp4.search( line )
if m4:
( count1, count2, operation, count3, count4 ) = m4.groups()
mLine = expFile.match( prev_line )
if mLine:
sys.stdout.write( prev_line + '\n' )
sys.stdout.write( count1 + ' ' + count2 + ' ' + operation + ' ' + count3 + ' ' + count4 + '\n' )
elif m3:
( count1, operation, count2, count3 )= m3.groups()
mLine = expFile.match( prev_line )
if mLine:
sys.stdout.write( prev_line + '\n' )
sys.stdout.write( count1 + ' ' + operation + ' ' + count2 + ' ' + count3 + '\n' )
elif m2:
( count1, count2, operation, count3 ) = m2.groups()
mLine = expFile.match( prev_line )
if mLine:
sys.stdout.write( prev_line + '\n' )
sys.stdout.write( count1 + ' ' + count2 + ' ' + operation + ' ' + count3 + '\n' )
elif m1:
( count1, operation, count2 ) = m1.groups()
mLine = expFile.match( prev_line )
if mLine:
sys.stdout.write( prev_line + '\n' )
sys.stdout.write( count1 + ' ' + operation + ' ' + count2 + '\n' )
prev_line = line
return lines_added, lines_deleted, lines_changed
#
# This script takes a depot path and two labels (like @jakcalope.20 )
# OR two date-time combos (like @1999/02/02 )
#
# The output then gives you the counts of lines changed deleted or added
# by each of the various "p4 diff2 -d?" formats. (metrics)
#
#
# Main body of program
#
#
depot_location = sys.argv[1]
diff_label1 = sys.argv[2]
diff_label2 = sys.argv[3]
sys.stdout.write( 'Starting processing....\n\n' )
# fyi
sys.stdout.write('Start point ' + diff_label1 + ' End Point ' + diff_label2 + '\n' )
sys.stdout.write('Depot Range ' + depot_location + '\n\n' )
#call the diff with the RCS flags
#
#
adds = changes = deletes = 0
( adds, deletes) = extract_metrics_counts_rcs( depot_location, diff_label1, diff_label2 )
sys.stdout.write( 'Metrics via RCS diff method Lines Added --> ' + `adds` + '\n' )
sys.stdout.write( ' Lines Deleted--> ' + `deletes` + '\n\n' )
#call the diff with the context flags
#
#
adds = changes = deletes = 0
( adds, changes, deletes) = extract_metrics_counts_context( depot_location, diff_label1, diff_label2 )
sys.stdout.write( 'Metrics via Context diff method Lines Added --> ' + `adds` + '\n' )
sys.stdout.write( ' Lines Changed--> ' + `changes` + '\n' )
sys.stdout.write( ' Lines Deleted--> ' + `deletes` + '\n\n' )
#call the diff with the summary flags
#
#
adds = changes = deletes = 0
( adds, addchunks, deletes, deletechunks, changes, changechunks, extra ) = extract_metrics_counts_summary( depot_location, diff_label1, diff_label2 )
sys.stdout.write( 'Metrics via Summary diff method Add --> ' + `addchunks` + ' Chunks\n' )
sys.stdout.write( ' ' + `adds` + ' Lines\n' )
sys.stdout.write( ' Deleted--> ' + `deletechunks` + ' Chunks\n' )
sys.stdout.write( ' ' + `deletes` + ' Lines\n' )
sys.stdout.write( ' Changed--> ' + `changechunks` + ' Chunks\n' )
sys.stdout.write( ' ' + `changes` + ' / ' + `extra` + ' Lines\n\n' )
#call the diff with the unified flags
#
#
adds = changes = deletes = 0
( adds, deletes) = extract_metrics_counts_unified( depot_location, diff_label1, diff_label2 )
sys.stdout.write( 'Metrics via Unified diff method Lines Added --> ' + `adds` + '\n' )
sys.stdout.write( ' Lines Deleted--> ' + `deletes` + '\n\n' )