2020-01-21 14:27:39 +01:00
#!/usr/bin/env python3
2019-10-23 09:12:15 +02:00
# Run this script from your branch with proposed Cppcheck patch to verify your
2020-06-15 20:04:43 +02:00
# patch against current main. It will compare output of testing a bunch of
2019-10-23 09:12:15 +02:00
# opensource packages
2022-04-10 22:47:27 +02:00
# If running on Windows, make sure that git.exe, wget.exe, and MSBuild.exe are available in PATH
2019-10-23 09:12:15 +02:00
import donate_cpu_lib as lib
import argparse
2023-05-23 09:51:51 +02:00
import glob
2019-10-23 09:12:15 +02:00
import os
import sys
import random
import subprocess
2020-05-14 19:45:53 +02:00
def format_float ( a , b = 1 ) :
if a > 0 and b > 0 :
return ' {:.2f} ' . format ( a / b )
return ' N/A '
2019-10-23 09:12:15 +02:00
if __name__ == " __main__ " :
2022-10-06 20:49:47 +02:00
__my_script_name = os . path . splitext ( os . path . basename ( sys . argv [ 0 ] ) ) [ 0 ]
__work_path = os . path . expanduser ( os . path . join ( ' ~ ' , ' cppcheck- ' + __my_script_name + ' -workfolder ' ) )
2020-06-15 20:04:43 +02:00
parser = argparse . ArgumentParser ( description = ' Run this script from your branch with proposed Cppcheck patch to verify your patch against current main. It will compare output of testing bunch of opensource packages ' )
2019-10-23 09:12:15 +02:00
parser . add_argument ( ' -j ' , default = 1 , type = int , help = ' Concurency execution threads ' )
2022-12-11 19:41:21 +01:00
package_group = parser . add_mutually_exclusive_group ( )
package_group . add_argument ( ' -p ' , default = 256 , type = int , help = ' Count of packages to check ' )
package_group . add_argument ( ' --packages ' , nargs = ' + ' , help = ' Check specific packages and then stop. ' )
2023-05-23 09:51:51 +02:00
package_group . add_argument ( ' --packages-path ' , default = None , type = str , help = ' Check packages in path. ' )
2019-10-23 09:12:15 +02:00
parser . add_argument ( ' -o ' , default = ' my_check_diff.log ' , help = ' Filename of result inside a working path dir ' )
2022-12-11 19:41:21 +01:00
language_group = parser . add_mutually_exclusive_group ( )
language_group . add_argument ( ' --c-only ' , dest = ' c_only ' , help = ' Only process c packages ' , action = ' store_true ' )
language_group . add_argument ( ' --cpp-only ' , dest = ' cpp_only ' , help = ' Only process c++ packages ' , action = ' store_true ' )
2022-10-06 20:49:47 +02:00
parser . add_argument ( ' --work-path ' , ' --work-path= ' , default = __work_path , type = str , help = ' Working directory for reference repo ' )
2019-10-23 09:12:15 +02:00
args = parser . parse_args ( )
print ( args )
2022-09-02 09:30:26 +02:00
if not lib . check_requirements ( ) :
print ( " Error: Check requirements " )
sys . exit ( 1 )
2019-12-12 16:43:40 +01:00
work_path = os . path . abspath ( args . work_path )
2019-10-23 09:12:15 +02:00
if not os . path . exists ( work_path ) :
os . makedirs ( work_path )
2021-12-19 18:52:36 +01:00
repo_dir = os . path . join ( work_path , ' repo ' )
old_repo_dir = os . path . join ( work_path , ' cppcheck ' )
main_dir = os . path . join ( work_path , ' tree-main ' )
2019-10-23 09:12:15 +02:00
2022-10-06 20:49:47 +02:00
lib . set_jobs ( ' -j ' + str ( args . j ) )
2019-10-23 09:12:15 +02:00
result_file = os . path . join ( work_path , args . o )
2020-05-14 19:45:53 +02:00
( f , ext ) = os . path . splitext ( result_file )
timing_file = f + ' _timing ' + ext
2019-10-23 09:12:15 +02:00
your_repo_dir = os . path . dirname ( os . path . dirname ( os . path . abspath ( sys . argv [ 0 ] ) ) )
if os . path . exists ( result_file ) :
os . remove ( result_file )
2020-05-14 19:45:53 +02:00
if os . path . exists ( timing_file ) :
os . remove ( timing_file )
2019-10-23 09:12:15 +02:00
2021-12-19 18:52:36 +01:00
try :
lib . clone_cppcheck ( repo_dir , old_repo_dir )
pass
2022-10-06 20:49:47 +02:00
except Exception as e :
print ( ' Failed to clone Cppcheck repository ( {} ), retry later ' . format ( e ) )
2021-12-19 18:52:36 +01:00
sys . exit ( 1 )
try :
lib . checkout_cppcheck_version ( repo_dir , ' main ' , main_dir )
pass
2022-10-06 20:49:47 +02:00
except Exception as e :
print ( ' Failed to checkout main ( {} ), retry later ' . format ( e ) )
2019-10-23 09:12:15 +02:00
sys . exit ( 1 )
try :
os . chdir ( your_repo_dir )
2020-06-15 20:04:43 +02:00
commit_id = ( subprocess . check_output ( [ ' git ' , ' merge-base ' , ' origin/main ' , ' HEAD ' ] ) ) . strip ( ) . decode ( ' ascii ' )
2019-10-23 09:12:15 +02:00
with open ( result_file , ' a ' ) as myfile :
myfile . write ( ' Common ancestor: ' + commit_id + ' \n \n ' )
2020-05-14 19:45:53 +02:00
package_width = ' 140 '
timing_width = ' >7 '
with open ( timing_file , ' a ' ) as myfile :
myfile . write ( ' { : {package_width} } { : {timing_width} } { : {timing_width} } { : {timing_width} } \n ' . format (
2020-06-15 20:04:43 +02:00
' Package ' , ' main ' , ' your ' , ' Factor ' , package_width = package_width , timing_width = timing_width ) )
2019-10-23 09:12:15 +02:00
2020-06-15 20:04:43 +02:00
os . chdir ( main_dir )
2021-12-19 18:52:36 +01:00
subprocess . check_call ( [ ' git ' , ' fetch ' , ' --depth=1 ' , ' origin ' , commit_id ] )
2019-10-23 09:12:15 +02:00
subprocess . check_call ( [ ' git ' , ' checkout ' , ' -f ' , commit_id ] )
2021-12-19 18:52:36 +01:00
except BaseException as e :
print ( ' Error: {} ' . format ( e ) )
2020-06-15 20:04:43 +02:00
print ( ' Failed to switch to common ancestor of your branch and main ' )
2019-10-23 09:12:15 +02:00
sys . exit ( 1 )
2022-10-06 20:49:47 +02:00
if not lib . compile_cppcheck ( main_dir ) :
2020-06-15 20:04:43 +02:00
print ( ' Failed to compile main of Cppcheck ' )
2019-10-23 09:12:15 +02:00
sys . exit ( 1 )
print ( ' Testing your PR from directory: ' + your_repo_dir )
2022-10-06 20:49:47 +02:00
if not lib . compile_cppcheck ( your_repo_dir ) :
2019-10-23 09:12:15 +02:00
print ( ' Failed to compile your version of Cppcheck ' )
sys . exit ( 1 )
2023-05-23 09:51:51 +02:00
if args . packages_path :
# You can download packages using daca2-download.py
args . packages = glob . glob ( os . path . join ( args . packages_path , ' *.tar.xz ' ) )
args . p = len ( args . packages )
packages_idxs = list ( range ( args . p ) )
random . shuffle ( packages_idxs )
elif args . packages :
2021-12-05 20:25:38 +01:00
args . p = len ( args . packages )
packages_idxs = [ ]
else :
2022-10-06 20:49:47 +02:00
packages_count = lib . get_packages_count ( )
2021-12-05 20:25:38 +01:00
if not packages_count :
print ( " network or server might be temporarily down.. " )
sys . exit ( 1 )
2019-10-23 09:12:15 +02:00
2021-12-05 20:25:38 +01:00
packages_idxs = list ( range ( packages_count ) )
random . shuffle ( packages_idxs )
2019-10-23 09:12:15 +02:00
packages_processed = 0
crashes = [ ]
2020-01-24 12:33:51 +01:00
timeouts = [ ]
2019-10-23 09:12:15 +02:00
2021-12-05 20:25:38 +01:00
while ( packages_processed < args . p and len ( packages_idxs ) > 0 ) or args . packages :
if args . packages :
package = args . packages . pop ( )
else :
2022-10-06 20:49:47 +02:00
package = lib . get_package ( packages_idxs . pop ( ) )
2019-10-23 09:12:15 +02:00
2023-05-23 09:51:51 +02:00
if package . startswith ( ' ftp:// ' ) or package . startswith ( ' http:// ' ) :
tgz = lib . download_package ( work_path , package , None )
if tgz is None :
print ( " No package downloaded " )
continue
else :
tgz = package
2019-10-23 09:12:15 +02:00
2022-09-05 11:32:17 +02:00
source_path , source_found = lib . unpack_package ( work_path , tgz , c_only = args . c_only , cpp_only = args . cpp_only )
2022-07-12 19:39:07 +02:00
if not source_found :
2019-10-23 09:12:15 +02:00
print ( " No files to process " )
continue
2022-09-05 16:27:07 +02:00
results_to_diff = list ( )
2019-10-23 09:12:15 +02:00
2020-06-15 20:04:43 +02:00
main_crashed = False
2019-10-23 09:12:15 +02:00
your_crashed = False
2020-06-15 20:04:43 +02:00
main_timeout = False
2020-01-24 12:33:51 +01:00
your_timeout = False
2022-07-13 21:09:29 +02:00
libraries = lib . library_includes . get_libraries ( source_path )
2022-10-06 20:49:47 +02:00
c , errout , info , time_main , cppcheck_options , timing_info = lib . scan_package ( main_dir , source_path , libraries )
2019-10-23 09:12:15 +02:00
if c < 0 :
if c == - 101 and ' error: could not find or open any of the paths given. ' in errout :
# No sourcefile found (for example only headers present)
print ( ' Error: 101 ' )
2020-01-24 12:33:51 +01:00
elif c == lib . RETURN_CODE_TIMEOUT :
2020-06-15 20:04:43 +02:00
print ( ' Main timed out! ' )
main_timeout = True
2019-10-23 09:12:15 +02:00
else :
2020-06-15 20:04:43 +02:00
print ( ' Main crashed! ' )
main_crashed = True
2019-10-23 09:12:15 +02:00
results_to_diff . append ( errout )
2022-10-06 20:49:47 +02:00
c , errout , info , time_your , cppcheck_options , timing_info = lib . scan_package ( your_repo_dir , source_path , libraries )
2019-10-23 09:12:15 +02:00
if c < 0 :
if c == - 101 and ' error: could not find or open any of the paths given. ' in errout :
# No sourcefile found (for example only headers present)
print ( ' Error: 101 ' )
2020-01-24 12:33:51 +01:00
elif c == lib . RETURN_CODE_TIMEOUT :
print ( ' Your code timed out! ' )
your_timeout = True
2019-10-23 09:12:15 +02:00
else :
print ( ' Your code crashed! ' )
your_crashed = True
results_to_diff . append ( errout )
2020-06-15 20:04:43 +02:00
if main_crashed or your_crashed :
2019-10-23 09:12:15 +02:00
who = None
2020-06-15 20:04:43 +02:00
if main_crashed and your_crashed :
2019-10-23 09:12:15 +02:00
who = ' Both '
2020-06-15 20:04:43 +02:00
elif main_crashed :
who = ' Main '
2019-10-23 09:12:15 +02:00
else :
who = ' Your '
2020-01-21 14:27:39 +01:00
crashes . append ( package + ' ' + who )
2019-11-05 21:05:43 +01:00
2020-06-15 20:04:43 +02:00
if main_timeout or your_timeout :
2020-01-24 12:33:51 +01:00
who = None
2020-06-15 20:04:43 +02:00
if main_timeout and your_timeout :
2020-01-24 12:33:51 +01:00
who = ' Both '
2020-06-15 20:04:43 +02:00
elif main_timeout :
who = ' Main '
2020-01-24 12:33:51 +01:00
else :
who = ' Your '
timeouts . append ( package + ' ' + who )
2019-10-23 09:12:15 +02:00
with open ( result_file , ' a ' ) as myfile :
myfile . write ( package + ' \n ' )
2021-03-06 12:34:12 +01:00
diff = lib . diff_results ( ' main ' , results_to_diff [ 0 ] , ' your ' , results_to_diff [ 1 ] )
2022-09-05 20:17:38 +02:00
if not main_crashed and not your_crashed and diff != ' ' :
2022-09-05 20:55:55 +02:00
myfile . write ( ' libraries: ' + ' , ' . join ( libraries ) + ' \n ' )
2019-10-24 21:54:22 +02:00
myfile . write ( ' diff: \n ' + diff + ' \n ' )
2019-10-23 09:12:15 +02:00
2022-09-05 20:17:38 +02:00
if not main_crashed and not your_crashed :
with open ( timing_file , ' a ' ) as myfile :
myfile . write ( ' { : {package_width} } { : {timing_width} } { : {timing_width} } { : {timing_width} } \n ' . format (
package , format_float ( time_main ) ,
format_float ( time_your ) , format_float ( time_your , time_main ) ,
package_width = package_width , timing_width = timing_width ) )
2020-05-14 19:45:53 +02:00
2019-10-23 09:12:15 +02:00
packages_processed + = 1
print ( str ( packages_processed ) + ' of ' + str ( args . p ) + ' packages processed \n ' )
with open ( result_file , ' a ' ) as myfile :
myfile . write ( ' \n \n crashes \n ' )
myfile . write ( ' \n ' . join ( crashes ) )
2020-01-24 12:33:51 +01:00
with open ( result_file , ' a ' ) as myfile :
myfile . write ( ' \n \n timeouts \n ' )
2020-05-14 19:45:53 +02:00
myfile . write ( ' \n ' . join ( timeouts ) + ' \n ' )
2020-01-24 12:33:51 +01:00
2019-10-23 09:12:15 +02:00
print ( ' Result saved to: ' + result_file )