2019-02-27 18:30:39 +01:00
#!/usr/bin/python3
2019-02-27 14:14:19 +01:00
"""
2019-02-28 11:09:10 +01:00
Best - practices tracker for Tor source code .
2019-02-27 14:14:19 +01:00
Go through the various . c files and collect metrics about them . If the metrics
violate some of our best practices and they are not found in the optional
2019-02-28 11:09:10 +01:00
exceptions file , then log a problem about them .
2019-02-27 14:14:19 +01:00
We currently do metrics about file size , function size and number of includes .
2019-02-28 11:09:10 +01:00
practracker . py should be run with its second argument pointing to the Tor
top - level source directory like this :
$ python3 . / scripts / maint / practracker / practracker . py .
The exceptions file is meant to be initialized once with the current state of
the source code and then get saved in the repository for ever after :
$ python3 . / scripts / maint / practracker / practracker . py . > . / scripts / maint / practracker / exceptions . txt
2019-02-27 14:14:19 +01:00
"""
import os , sys
import metrics
import util
2019-02-27 17:24:10 +01:00
import problem
2019-02-27 14:14:19 +01:00
2019-02-28 11:09:10 +01:00
# The filename of the exceptions file (it should be placed in the practracker directory)
EXCEPTIONS_FNAME = " ./exceptions.txt "
2019-02-27 14:14:19 +01:00
# Recommended file size
MAX_FILE_SIZE = 3000 # lines
# Recommended function size
MAX_FUNCTION_SIZE = 100 # lines
# Recommended number of #includes
MAX_INCLUDE_COUNT = 50
#######################################################
2019-02-28 11:09:10 +01:00
# ProblemVault singleton
2019-02-27 17:24:10 +01:00
ProblemVault = None
2019-02-27 14:14:19 +01:00
2019-02-28 11:09:10 +01:00
# The Tor source code topdir
TOR_TOPDIR = None
2019-02-27 14:14:19 +01:00
#######################################################
2019-02-27 17:24:10 +01:00
def consider_file_size ( fname , f ) :
2019-02-27 18:30:39 +01:00
""" Consider file size issues for ' f ' and return True if a new issue was found """
file_size = metrics . get_file_len ( f )
2019-02-27 14:14:19 +01:00
if file_size > MAX_FILE_SIZE :
2019-02-27 18:30:39 +01:00
p = problem . FileSizeProblem ( fname , file_size )
return ProblemVault . register_problem ( p )
return False
2019-02-27 14:14:19 +01:00
2019-02-27 17:24:10 +01:00
def consider_includes ( fname , f ) :
2019-02-27 18:30:39 +01:00
""" Consider #include issues for ' f ' and return True if a new issue was found """
2019-02-27 16:05:00 +01:00
include_count = metrics . get_include_count ( f )
2019-02-27 14:14:19 +01:00
if include_count > MAX_INCLUDE_COUNT :
2019-02-27 18:30:39 +01:00
p = problem . IncludeCountProblem ( fname , include_count )
return ProblemVault . register_problem ( p )
return False
2019-02-27 14:14:19 +01:00
2019-02-27 17:24:10 +01:00
def consider_function_size ( fname , f ) :
2019-02-27 18:30:39 +01:00
""" Consider the function sizes for ' f ' and return True if a new issue was found """
found_new_issues = False
for name , lines in metrics . get_function_lines ( f ) :
2019-02-27 14:14:19 +01:00
# Don't worry about functions within our limits
if lines < = MAX_FUNCTION_SIZE :
continue
2019-02-27 17:24:10 +01:00
# That's a big function! Issue a problem!
2019-02-27 18:30:39 +01:00
canonical_function_name = " %s : %s () " % ( fname , name )
p = problem . FunctionSizeProblem ( canonical_function_name , lines )
found_new_issues | = ProblemVault . register_problem ( p )
return found_new_issues
2019-02-27 14:14:19 +01:00
#######################################################
2019-02-27 17:24:10 +01:00
def consider_all_metrics ( files_list ) :
2019-02-27 18:30:39 +01:00
""" Consider metrics for all files, and return True if new issues were found """
found_new_issues = False
2019-02-27 14:14:19 +01:00
for fname in files_list :
with open ( fname , ' r ' ) as f :
2019-02-27 18:30:39 +01:00
found_new_issues | = consider_metrics_for_file ( fname , f )
return found_new_issues
2019-02-27 14:14:19 +01:00
2019-02-27 17:24:10 +01:00
def consider_metrics_for_file ( fname , f ) :
2019-02-27 14:14:19 +01:00
"""
2019-02-27 18:30:39 +01:00
Consider the various metrics for file with filename ' fname ' and file descriptor ' f ' .
Return True if we found new issues .
2019-02-27 14:14:19 +01:00
"""
2019-02-27 18:30:39 +01:00
# Strip the useless part of the path
if fname . startswith ( TOR_TOPDIR ) :
fname = fname [ len ( TOR_TOPDIR ) : ]
found_new_issues = False
2019-02-27 14:14:19 +01:00
# Get file length
2019-02-27 18:30:39 +01:00
found_new_issues | = consider_file_size ( fname , f )
2019-02-27 14:14:19 +01:00
# Consider number of #includes
f . seek ( 0 )
2019-02-27 18:30:39 +01:00
found_new_issues | = consider_includes ( fname , f )
2019-02-27 14:14:19 +01:00
# Get function length
f . seek ( 0 )
2019-02-27 18:30:39 +01:00
found_new_issues | = consider_function_size ( fname , f )
2019-02-27 14:14:19 +01:00
2019-02-27 18:30:39 +01:00
return found_new_issues
2019-02-27 17:24:10 +01:00
2019-02-27 18:30:39 +01:00
def main ( ) :
2019-02-28 11:09:10 +01:00
if ( len ( sys . argv ) != 2 ) :
print ( " Usage: \n \t $ practracker.py <tor topdir> \n \t (e.g. $ practracker.py ~/tor/) " )
return
global TOR_TOPDIR
TOR_TOPDIR = sys . argv [ 1 ]
exceptions_file = os . path . join ( TOR_TOPDIR , " scripts/maint/practracker " , EXCEPTIONS_FNAME )
2019-02-27 14:14:19 +01:00
# 1) Get all the .c files we care about
2019-02-28 11:09:10 +01:00
files_list = util . get_tor_c_files ( TOR_TOPDIR )
2019-02-27 14:14:19 +01:00
2019-02-27 17:24:10 +01:00
# 2) Initialize problem vault and load an optional exceptions file so that
# we don't warn about the past
2019-02-27 18:30:39 +01:00
global ProblemVault
2019-02-28 11:09:10 +01:00
ProblemVault = problem . ProblemVault ( exceptions_file )
2019-02-27 14:14:19 +01:00
2019-02-27 17:24:10 +01:00
# 3) Go through all the files and report problems if they are not exceptions
2019-02-27 18:30:39 +01:00
found_new_issues = consider_all_metrics ( files_list )
2019-02-28 11:09:10 +01:00
# If new issues were found, try to give out some advice to the developer on how to resolve it.
if ( found_new_issues ) :
new_issues_str = " practracker FAILED as indicated by the problem lines above. Please use the exceptions file ( {} ) to find any previous state of these problems. If you are unable to fix the underlying best-practices issue right now then you need to either update the relevant exception line or add a new one. " . format ( exceptions_file )
print ( new_issues_str )
sys . exit ( found_new_issues )
2019-02-27 14:14:19 +01:00
if __name__ == ' __main__ ' :
main ( )