프로그램 언어 별 Cyclomatic complexity, LOC, Parameter Number를 계산해주는 Python 모듈인 lizard를 소개한다.
C/C++ 언어의 경우에도 헤더 파싱을 안하더라도 간결하게 계산할 수 있어 도움이 많이 될듯 하다.
1. 설치
pip install lizard
2. 사용법
lizard [options] [PATH or FILE] [PATH] ...
- 옵션 없이 실행하면, 해당 폴더를 포함하여 하부 폴더에 있는 소스코드에 대한 분석을 수행한다.
- -m : Modified CC를 계산함(switch / case 문은 1로 계산함)
- -X, --xml : 결과를 xml로 저장함.
- --csv : 결과를 csv로 저장함.
- -H, --html : 결과를 html로 저장함.
3. 결과 화면
================================================
NLOC CCN token PARAM length location
------------------------------------------------
22 1 143 1 103 set_prefs@5-107@.\.vscode\.ropeproject\config.py
1 1 6 1 2 project_opened@110-111@.\.vscode\.ropeproject\config.py
125 1 1207 2 126 setupUi@16-141@.\fir\qt4\MainDlg.py
20 1 423 2 20 retranslateUi@143-162@.\fir\qt4\MainDlg.py
2 1 12 2 2 __init__@21-22@.\fir\ClocXmlParser.py
9 2 51 2 17 openXml@24-40@.\fir\ClocXmlParser.py
5 2 47 3 5 executeCloc@42-46@.\fir\ClocXmlParser.py
19 4 120 2 29 parseXml@48-76@.\fir\ClocXmlParser.py
12 1 80 2 12 __init__@36-47@.\fir\FileInfo.py
6 2 47 1 12 check_file_type@49-60@.\fir\FileInfo.py
2 1 10 1 7 get_file_ext@62-68@.\fir\FileInfo.py
2 1 10 1 7 get_file_type@70-76@.\fir\FileInfo.py
12 3 71 2 21 read_info@78-98@.\fir\FileInfo.py
2 1 13 1 6 get_checksum@100-105@.\fir\FileInfo.py
2 1 13 1 6 get_mtime@107-112@.\fir\FileInfo.py
2 1 13 1 6 get_size@114-119@.\fir\FileInfo.py
2 1 13 1 7 get_line_count@121-127@.\fir\FileInfo.py
6 1 46 1 15 read_crc32@129-143@.\fir\FileInfo.py
7 1 55 1 16 read_sha1@145-160@.\fir\FileInfo.py
7 1 55 1 16 read_md5@162-177@.\fir\FileInfo.py
5 1 44 1 13 read_mtime@179-191@.\fir\FileInfo.py
3 1 26 1 11 read_size@193-203@.\fir\FileInfo.py
7 2 47 1 17 read_line_count@205-221@.\fir\FileInfo.py
3 1 18 2 3 __init__@27-29@.\fir\FilesInfoDB.py
2 1 9 1 2 get_db@31-32@.\fir\FilesInfoDB.py
2 1 22 3 9 insert@34-42@.\fir\FilesInfoDB.py
19 4 152 2 34 to_csv@44-77@.\fir\FilesInfoDB.py
12 4 119 2 22 to_stdout@79-100@.\fir\FilesInfoDB.py
19 4 122 3 23 __init__@40-62@.\fir\FilesInfoReader.py
2 1 9 1 2 get_file_info_db@64-65@.\fir\FilesInfoReader.py
2 1 12 2 2 set_ignore_pattern@67-68@.\fir\FilesInfoReader.py
2 1 9 1 2 get_path_ignore_pattern@70-71@.\fir\FilesInfoReader.py
2 1 13 2 8 set_root_path@73-80@.\fir\FilesInfoReader.py
7 1 51 2 15 start_cloc@82-96@.\fir\FilesInfoReader.py
4 3 44 2 4 _walk_dir@98-101@.\fir\FilesInfoReader.py
5 1 53 4 13 iterate@103-115@.\fir\FilesInfoReader.py
60 12 443 6 71 _iterate_all_files@117-187@.\fir\FilesInfoReader.py
9 3 62 1 10 _get_total_size@189-198@.\fir\FilesInfoReader.py
7 3 52 2 16 save@200-215@.\fir\FilesInfoReader.py
3 1 17 1 3 setUp@28-30@.\test\test_ClocXmlParser.py
2 1 6 1 2 tearDown@32-33@.\test\test_ClocXmlParser.py
3 1 21 1 3 test_parseXml_with_data1@35-37@.\test\test_ClocXmlParser.py
3 1 21 1 3 test_parseXml_with_nofile@39-41@.\test\test_ClocXmlParser.py
6 1 62 1 6 test_parseXml_normal_data@43-48@.\test\test_ClocXmlParser.py
5 1 50 1 5 test_parseXml_normal_data_with_korean_path@50-54@.\test\test_ClocXmlParser.py
2 1 6 1 2 setUp@26-27@.\test\test_FileInfo.py
2 1 6 1 2 tearDown@29-30@.\test\test_FileInfo.py
5 1 41 1 5 test_init@32-36@.\test\test_FileInfo.py
4 1 24 1 4 test_check_file_type_source_code@38-41@.\test\test_FileInfo.py
4 1 24 1 4 test_check_file_type_no_source_code@43-46@.\test\test_FileInfo.py
4 1 24 1 4 test_get_file_ext_1@48-51@.\test\test_FileInfo.py
4 1 24 1 4 test_get_file_ext_2@53-56@.\test\test_FileInfo.py
6 1 46 1 6 test_read_info_1@58-63@.\test\test_FileInfo.py
6 1 46 1 6 test_read_info_2@65-70@.\test\test_FileInfo.py
2 1 6 1 2 setUp@22-23@.\test\test_FilesInfoReader.py
2 1 6 1 2 tearDown@25-26@.\test\test_FilesInfoReader.py
9 1 57 1 9 test_iterate@28-36@.\test\test_FilesInfoReader.py
14 1 59 0 18 print_usage@24-41@.\FilesInfoReaderMain.py
4 1 31 2 4 __init__@38-41@.\FilesInfoReaderMainGUI.py
5 1 35 5 5 set_data@43-47@.\FilesInfoReaderMainGUI.py
6 2 53 1 6 run@49-54@.\FilesInfoReaderMainGUI.py
47 1 530 2 65 __init__@60-124@.\FilesInfoReaderMainGUI.py
16 2 171 1 19 close_app@126-144@.\FilesInfoReaderMainGUI.py
3 1 22 1 3 _load_config@146-148@.\FilesInfoReaderMainGUI.py
9 2 63 2 14 change_ignore@151-164@.\FilesInfoReaderMainGUI.py
9 2 63 2 16 change_cloc_use@166-181@.\FilesInfoReaderMainGUI.py
9 2 63 2 14 change_ext_only@183-196@.\FilesInfoReaderMainGUI.py
3 1 26 1 7 show_target_folder_dlg@198-204@.\FilesInfoReaderMainGUI.py
5 2 42 1 9 show_output_file_dlg@206-214@.\FilesInfoReaderMainGUI.py
3 1 28 1 7 show_cloc_path_dlg@216-222@.\FilesInfoReaderMainGUI.py
41 10 257 1 58 read_info@224-281@.\FilesInfoReaderMainGUI.py
4 3 27 1 4 _all_disable@283-286@.\FilesInfoReaderMainGUI.py
4 3 27 1 4 _all_enable@288-291@.\FilesInfoReaderMainGUI.py
11 3 67 2 11 _check_worker@293-303@.\FilesInfoReaderMainGUI.py
2 1 26 2 7 _warning@305-311@.\FilesInfoReaderMainGUI.py
2 1 26 2 7 _info@313-319@.\FilesInfoReaderMainGUI.py
13 file analyzed.
==============================================================
NLOC Avg.NLOC AvgCCN Avg.token function_cnt file
--------------------------------------------------------------
23 11.5 1.0 74.5 2 .\.vscode\.ropeproject\config.py
150 72.5 1.0 815.0 2 .\fir\qt4\MainDlg.py
53 8.8 2.2 57.5 4 .\fir\ClocXmlParser.py
107 5.1 1.3 36.2 15 .\fir\FileInfo.py
61 7.6 2.2 64.0 5 .\fir\FilesInfoDB.py
152 10.8 2.8 79.1 11 .\fir\FilesInfoReader.py
5 0.0 0.0 0.0 0 .\fir\__init__.py
46 3.7 1.0 29.5 6 .\test\test_ClocXmlParser.py
60 4.1 1.0 26.8 9 .\test\test_FileInfo.py
33 4.3 1.0 23.0 3 .\test\test_FilesInfoReader.py
0 0.0 0.0 0.0 0 .\test\__init__.py
83 14.0 1.0 59.0 1 .\FilesInfoReaderMain.py
222 10.2 2.2 86.5 18 .\FilesInfoReaderMainGUI.py
=============================================================================================
No thresholds exceeded (cyclomatic_complexity > 15 or length > 1000 or parameter_count > 100)
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
995 9.3 1.7 76.9 76 0 0.00 0.00
NLOC CCN token PARAM length location
------------------------------------------------
22 1 143 1 103 set_prefs@5-107@.\.vscode\.ropeproject\config.py
1 1 6 1 2 project_opened@110-111@.\.vscode\.ropeproject\config.py
125 1 1207 2 126 setupUi@16-141@.\fir\qt4\MainDlg.py
20 1 423 2 20 retranslateUi@143-162@.\fir\qt4\MainDlg.py
2 1 12 2 2 __init__@21-22@.\fir\ClocXmlParser.py
9 2 51 2 17 openXml@24-40@.\fir\ClocXmlParser.py
5 2 47 3 5 executeCloc@42-46@.\fir\ClocXmlParser.py
19 4 120 2 29 parseXml@48-76@.\fir\ClocXmlParser.py
12 1 80 2 12 __init__@36-47@.\fir\FileInfo.py
6 2 47 1 12 check_file_type@49-60@.\fir\FileInfo.py
2 1 10 1 7 get_file_ext@62-68@.\fir\FileInfo.py
2 1 10 1 7 get_file_type@70-76@.\fir\FileInfo.py
12 3 71 2 21 read_info@78-98@.\fir\FileInfo.py
2 1 13 1 6 get_checksum@100-105@.\fir\FileInfo.py
2 1 13 1 6 get_mtime@107-112@.\fir\FileInfo.py
2 1 13 1 6 get_size@114-119@.\fir\FileInfo.py
2 1 13 1 7 get_line_count@121-127@.\fir\FileInfo.py
6 1 46 1 15 read_crc32@129-143@.\fir\FileInfo.py
7 1 55 1 16 read_sha1@145-160@.\fir\FileInfo.py
7 1 55 1 16 read_md5@162-177@.\fir\FileInfo.py
5 1 44 1 13 read_mtime@179-191@.\fir\FileInfo.py
3 1 26 1 11 read_size@193-203@.\fir\FileInfo.py
7 2 47 1 17 read_line_count@205-221@.\fir\FileInfo.py
3 1 18 2 3 __init__@27-29@.\fir\FilesInfoDB.py
2 1 9 1 2 get_db@31-32@.\fir\FilesInfoDB.py
2 1 22 3 9 insert@34-42@.\fir\FilesInfoDB.py
19 4 152 2 34 to_csv@44-77@.\fir\FilesInfoDB.py
12 4 119 2 22 to_stdout@79-100@.\fir\FilesInfoDB.py
19 4 122 3 23 __init__@40-62@.\fir\FilesInfoReader.py
2 1 9 1 2 get_file_info_db@64-65@.\fir\FilesInfoReader.py
2 1 12 2 2 set_ignore_pattern@67-68@.\fir\FilesInfoReader.py
2 1 9 1 2 get_path_ignore_pattern@70-71@.\fir\FilesInfoReader.py
2 1 13 2 8 set_root_path@73-80@.\fir\FilesInfoReader.py
7 1 51 2 15 start_cloc@82-96@.\fir\FilesInfoReader.py
4 3 44 2 4 _walk_dir@98-101@.\fir\FilesInfoReader.py
5 1 53 4 13 iterate@103-115@.\fir\FilesInfoReader.py
60 12 443 6 71 _iterate_all_files@117-187@.\fir\FilesInfoReader.py
9 3 62 1 10 _get_total_size@189-198@.\fir\FilesInfoReader.py
7 3 52 2 16 save@200-215@.\fir\FilesInfoReader.py
3 1 17 1 3 setUp@28-30@.\test\test_ClocXmlParser.py
2 1 6 1 2 tearDown@32-33@.\test\test_ClocXmlParser.py
3 1 21 1 3 test_parseXml_with_data1@35-37@.\test\test_ClocXmlParser.py
3 1 21 1 3 test_parseXml_with_nofile@39-41@.\test\test_ClocXmlParser.py
6 1 62 1 6 test_parseXml_normal_data@43-48@.\test\test_ClocXmlParser.py
5 1 50 1 5 test_parseXml_normal_data_with_korean_path@50-54@.\test\test_ClocXmlParser.py
2 1 6 1 2 setUp@26-27@.\test\test_FileInfo.py
2 1 6 1 2 tearDown@29-30@.\test\test_FileInfo.py
5 1 41 1 5 test_init@32-36@.\test\test_FileInfo.py
4 1 24 1 4 test_check_file_type_source_code@38-41@.\test\test_FileInfo.py
4 1 24 1 4 test_check_file_type_no_source_code@43-46@.\test\test_FileInfo.py
4 1 24 1 4 test_get_file_ext_1@48-51@.\test\test_FileInfo.py
4 1 24 1 4 test_get_file_ext_2@53-56@.\test\test_FileInfo.py
6 1 46 1 6 test_read_info_1@58-63@.\test\test_FileInfo.py
6 1 46 1 6 test_read_info_2@65-70@.\test\test_FileInfo.py
2 1 6 1 2 setUp@22-23@.\test\test_FilesInfoReader.py
2 1 6 1 2 tearDown@25-26@.\test\test_FilesInfoReader.py
9 1 57 1 9 test_iterate@28-36@.\test\test_FilesInfoReader.py
14 1 59 0 18 print_usage@24-41@.\FilesInfoReaderMain.py
4 1 31 2 4 __init__@38-41@.\FilesInfoReaderMainGUI.py
5 1 35 5 5 set_data@43-47@.\FilesInfoReaderMainGUI.py
6 2 53 1 6 run@49-54@.\FilesInfoReaderMainGUI.py
47 1 530 2 65 __init__@60-124@.\FilesInfoReaderMainGUI.py
16 2 171 1 19 close_app@126-144@.\FilesInfoReaderMainGUI.py
3 1 22 1 3 _load_config@146-148@.\FilesInfoReaderMainGUI.py
9 2 63 2 14 change_ignore@151-164@.\FilesInfoReaderMainGUI.py
9 2 63 2 16 change_cloc_use@166-181@.\FilesInfoReaderMainGUI.py
9 2 63 2 14 change_ext_only@183-196@.\FilesInfoReaderMainGUI.py
3 1 26 1 7 show_target_folder_dlg@198-204@.\FilesInfoReaderMainGUI.py
5 2 42 1 9 show_output_file_dlg@206-214@.\FilesInfoReaderMainGUI.py
3 1 28 1 7 show_cloc_path_dlg@216-222@.\FilesInfoReaderMainGUI.py
41 10 257 1 58 read_info@224-281@.\FilesInfoReaderMainGUI.py
4 3 27 1 4 _all_disable@283-286@.\FilesInfoReaderMainGUI.py
4 3 27 1 4 _all_enable@288-291@.\FilesInfoReaderMainGUI.py
11 3 67 2 11 _check_worker@293-303@.\FilesInfoReaderMainGUI.py
2 1 26 2 7 _warning@305-311@.\FilesInfoReaderMainGUI.py
2 1 26 2 7 _info@313-319@.\FilesInfoReaderMainGUI.py
13 file analyzed.
==============================================================
NLOC Avg.NLOC AvgCCN Avg.token function_cnt file
--------------------------------------------------------------
23 11.5 1.0 74.5 2 .\.vscode\.ropeproject\config.py
150 72.5 1.0 815.0 2 .\fir\qt4\MainDlg.py
53 8.8 2.2 57.5 4 .\fir\ClocXmlParser.py
107 5.1 1.3 36.2 15 .\fir\FileInfo.py
61 7.6 2.2 64.0 5 .\fir\FilesInfoDB.py
152 10.8 2.8 79.1 11 .\fir\FilesInfoReader.py
5 0.0 0.0 0.0 0 .\fir\__init__.py
46 3.7 1.0 29.5 6 .\test\test_ClocXmlParser.py
60 4.1 1.0 26.8 9 .\test\test_FileInfo.py
33 4.3 1.0 23.0 3 .\test\test_FilesInfoReader.py
0 0.0 0.0 0.0 0 .\test\__init__.py
83 14.0 1.0 59.0 1 .\FilesInfoReaderMain.py
222 10.2 2.2 86.5 18 .\FilesInfoReaderMainGUI.py
=============================================================================================
No thresholds exceeded (cyclomatic_complexity > 15 or length > 1000 or parameter_count > 100)
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
995 9.3 1.7 76.9 76 0 0.00 0.00
4. 기타
파이썬 모듈로써 활용할 수 있다.
>>> import lizard
>>> i = lizard.analyze_file("../cpputest/tests/AllTests.cpp")
>>> print i.__dict__
{'nloc': 9, 'function_list': [<lizard.FunctionInfo object at 0x10bf7af10>], 'filename': '../cpputest/tests/AllTests.cpp'} >>> print i.function_list[0].__dict__ {'cyclomatic_complexity': 1, 'token_count': 22, 'name': 'main', 'parameter_count': 2, 'nloc': 3, 'long_name': 'main( int ac , const char ** av )', 'start_line': 30}