Skip to content

Commit c2d1b35

Browse files
committed
[FIX/DOC] Documented some code and added a regex to get the binary name directly from the makefile. Fixes #68
1 parent 9b69df0 commit c2d1b35

File tree

7 files changed

+149
-20
lines changed

7 files changed

+149
-20
lines changed

42PyChecker.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
import argparse
77
import platform
88
from PyChecker.projects import libft, ft_commandements, other
9-
# @todo: Add verbose output
109

1110

1211
def print_header():
12+
"""
13+
This function simply prints the warranty and condition statement.
14+
Might be removed in the future
15+
"""
1316
print("\t42PyChecker Copyright (C) 2018-present Jules Lasne "
1417
"<jules.lasne@gmail.com>\n\tThis program comes with"
1518
" ABSOLUTELY NO WARRANTY; for details run with `--show-w'.\n\tThis is free"
@@ -18,8 +21,16 @@ def print_header():
1821

1922

2023
def main():
24+
"""
25+
Main function where arguments are parse and where the GUI is created.
26+
"""
27+
28+
# Initialize the parser and get the path of the script to use as a reference.
2129
root_path = os.path.dirname(os.path.realpath(__file__))
2230
parser = argparse.ArgumentParser()
31+
32+
# @todo: Add verbose output
33+
# Adds all the arguments one by one.
2334
parser.add_argument("-v", "--verbose", help="Increases output verbosity",
2435
action="store_true")
2536
parser.add_argument("--no-gui", help="disables the Graphical User Interface",
@@ -44,9 +55,12 @@ def main():
4455
# @todo: Fix --do-benchmark option for libft-unit-test
4556
parser.add_argument("--do-benchmark", help="Disables libft-unit-test benchmarking", action="store_false")
4657

58+
# Calls the parser for the arguments we asked to implement.
4759
args = parser.parse_args()
60+
61+
# If the argument `--show-w` is passed, the program will display the warranty
62+
# statement and exit.
4863
if args.show_w:
49-
print_header()
5064
print("THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY"
5165
" APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING\n"
5266
" THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM"
@@ -57,20 +71,32 @@ def main():
5771
" WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME\n"
5872
" THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.")
5973
return
74+
75+
# If the `--show-c` argument is passed, the program will display the License
76+
# and exit.
6077
if args.show_c:
61-
print_header()
6278
with open(root_path + '/.github/LICENSE.lesser', 'r') as file:
6379
print(file.read())
6480
return
81+
82+
# Here, if the `--no-tests` option is set, all the testing suites will be
83+
# disabled, no matter the project.
6584
if args.no_tests:
6685
args.no_libftest = True
6786
args.no_maintest = True
6887
args.no_moulitest = True
88+
args.no_libft_unit_test = True
89+
90+
# If no project is given the parser sends an error.
6991
if args.project is None:
7092
parser.error("You need to specify a project.")
93+
# If the path of the selected project is empty, the parser prints an error.
7194
if args.path == "":
7295
parser.error("`--path' needs to be specified in order for 42PyChecker"
7396
" to know where your project is.")
97+
98+
# If a test is disabled and the libft project is selected, the parser will
99+
# return an error.
74100
if args.no_libftest and args.project != "libft":
75101
parser.error("`--no-libftest' can only be applied if libft is selected "
76102
"with `--project'")
@@ -80,7 +106,8 @@ def main():
80106
if args.no_moulitest and args.project != "libft":
81107
parser.error("`--no-moulitest' can only be applied if libft is selected"
82108
" with `--project'")
83-
print_header()
109+
110+
# Here we select the project and start the check based on the argument `--project`
84111
if args.project == "libft":
85112
libft.check(root_path, args)
86113
# @todo: Handle options for 42commandements: No option can be passed (like --no-norm)
@@ -93,6 +120,7 @@ def main():
93120

94121
if __name__ == '__main__':
95122
if not platform.system() == "Windows":
123+
print_header()
96124
main()
97125
else:
98126
raise OSError("Sorry, this script can't be run on windows !")

PyChecker/projects/libft.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ def check(root_path: str, args):
7878
if not args.no_static:
7979
static_results = static.check(root_path, args)
8080
if not args.no_makefile:
81-
makefile_results = makefile.check(args.path, "libft.a", root_path)
81+
makefile_results = makefile.check(args.path, root_path)
8282
if not args.no_forbidden_functions:
83-
forbidden_functions_results = forbidden_functions.check(args.path, "libft.a", authorized_functions, root_path)
83+
forbidden_functions_results = forbidden_functions.check(args.path, authorized_functions, root_path)
8484
if not args.no_moulitest:
8585
moulitest_results = moulitest.run(args.path, has_libft_bonuses, "libft", root_path)
8686
if not args.no_libftest:

PyChecker/projects/other.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
Copyright (C) 2018 Jules Lasne <jules.lasne@gmail.com>
33
See full notice in `LICENSE'
44
"""
5-
from PyChecker.utils import author, norme
5+
from PyChecker.utils import author, norme, makefile
66

77

88
def check(root_path: str, args):
99
if not args.no_author:
1010
author.check(args.path)
1111
if not args.no_norm:
1212
norme.check(args.path, root_path)
13-
# @todo: Add the makefile check and find the binary name itself
13+
if not args.no_makefile:
14+
makefile.check(args.path, root_path)
1415
# @todo: Add forbidden function check based on assumption of what the project is.
1516
return 0

PyChecker/utils/forbidden_functions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
import re
99

1010

11-
def check(project_path: str, binary: str, authorized_func, root_path: str):
11+
def check(project_path: str, authorized_func, root_path: str):
1212
"""
1313
This function will check the functions used by the given binary in the given project.
1414
1515
:param project_path: The path of the project you want to test.
1616
:param binary_name: The binary that you want to analyze
1717
:param authorized_func: The functions authorized by the project.
1818
"""
19+
with open(project_path + '/Makefile', 'r') as file:
20+
data = file.read()
21+
binary = re.findall("NAME[\s]*=[\s]*(.*)", data)[0]
1922
print("*---------------------------------------------------------------*")
2023
print("*----------------------Forbidden functions:---------------------*")
2124
print("*---------------------------------------------------------------*")

PyChecker/utils/makefile.py

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,30 @@
1010

1111

1212
def check_makefile_clean_dir(project_path: str, binary_name: str, root_path: str):
13+
"""
14+
This function will clean the project's directory and test if the binary
15+
and the .o were removed.
16+
17+
:param project_path: The project path where what needs to be tested is.
18+
:param binary_name: The name of the binary the makefile will compile
19+
:param root_path: The absolute path leading to the script's main file.
20+
21+
:return: Returns the number of errors encountered during that function.
22+
"""
1323
error_count = 0
1424
with open(root_path + "/.mymakefile", 'w+') as file:
1525
file.write("Cleaning Directory\n")
1626
print("Cleaning Directory")
1727
file.write("*------------------------------------------------------*\n")
1828
file.write("")
29+
# Run make fclean in the project's directory
1930
result = subprocess.run('make ' + '-C ' + project_path + ' fclean',
2031
shell=True, stdout=subprocess.PIPE,
2132
stderr=subprocess.STDOUT).stdout.decode('utf-8')
2233
file.write(result + '\n')
2334
print(result)
35+
# If the binary or the object file still exists after the fclean,
36+
# print an error and increment the error counter.
2437
if os.path.exists(project_path + '/' + binary_name):
2538
file.write("-> Error when processing rule `fclean':"
2639
" It should have removed {}\n".format(binary_name))
@@ -37,6 +50,15 @@ def check_makefile_clean_dir(project_path: str, binary_name: str, root_path: str
3750

3851

3952
def check_makefile_all(project_path: str, binary_name: str, root_path: str):
53+
"""
54+
This function will check the rule `all` for the makefile of the given project.
55+
56+
:param project_path: The project path where what needs to be tested is.
57+
:param binary_name: The name of the binary the makefile will compile
58+
:param root_path: The absolute path leading to the script's main file.
59+
60+
:return: Returns the number of errors encountered during that function.
61+
"""
4062
error_count = 0
4163
makefile_path = project_path + '/Makefile'
4264
match = False
@@ -45,6 +67,8 @@ def check_makefile_all(project_path: str, binary_name: str, root_path: str):
4567
file.write("*------------------------------------------------------*\n")
4668
file.write("Checking rule: `all'\n")
4769
print("Checking rule: `all'")
70+
# Searches every line of the makefile for the string `all`
71+
# followed by spaces and/or tabs and then a `:`
4872
for line in makefile:
4973
if re.match('^all[\t ]*:', line):
5074
match = True
@@ -53,6 +77,9 @@ def check_makefile_all(project_path: str, binary_name: str, root_path: str):
5377
print("-> Error: rule `all' not found in the Makefile.")
5478
error_count += 1
5579
return error_count
80+
# Searches every line of the makefile for the string `all` followed
81+
# by tabs and/or spaces and then `:` then tabs and spaces again and
82+
# then for the string `$(NAME)`
5683
for line in makefile:
5784
if re.match('^all[\t ]*:[\t ]*\$\(NAME\)', line):
5885
match = True
@@ -61,11 +88,13 @@ def check_makefile_all(project_path: str, binary_name: str, root_path: str):
6188
print("-> Error: rule `all' should call the rule `$(NAME)'")
6289
error_count += 1
6390
return error_count
91+
# Runs make all in the project's directory
6492
result = subprocess.run('make ' + '-C ' + project_path + ' all',
6593
shell=True, stdout=subprocess.PIPE,
6694
stderr=subprocess.STDOUT).stdout.decode('utf-8')
6795
file.write(result + '\n')
6896
print(result)
97+
# Checks if binary and object file were created.
6998
if not os.path.exists(project_path + '/' + binary_name):
7099
file.write("-> Error when processing rule `all':"
71100
" It should have created {}\n".format(binary_name))
@@ -83,6 +112,15 @@ def check_makefile_all(project_path: str, binary_name: str, root_path: str):
83112

84113

85114
def check_makefile_clean(project_path: str, binary_name: str, root_path: str):
115+
"""
116+
This function will check the rule `clean` for the makefile of the given project.
117+
118+
:param project_path: The project path where what needs to be tested is.
119+
:param binary_name: The name of the binary the makefile will compile
120+
:param root_path: The absolute path leading to the script's main file.
121+
122+
:return: Returns the number of errors encountered during that function.
123+
"""
86124
error_count = 0
87125
makefile_path = project_path + '/Makefile'
88126
match = False
@@ -91,6 +129,8 @@ def check_makefile_clean(project_path: str, binary_name: str, root_path: str):
91129
file.write("*------------------------------------------------------*\n")
92130
file.write("Checking rule: `clean'\n")
93131
print("Checking rule: `clean'")
132+
# Searches every line of the makefile for the string `clean`
133+
# followed by `:`
94134
for line in makefile:
95135
if re.match('^clean[\t ]*:', line):
96136
match = True
@@ -99,18 +139,23 @@ def check_makefile_clean(project_path: str, binary_name: str, root_path: str):
99139
print("-> Error: rule `clean' not found in the Makefile.")
100140
error_count += 1
101141
return error_count
142+
# If previous rule failed, print an error message, increment error
143+
# counter and return number of errors.
102144
if not os.path.exists(project_path + '/' + binary_name):
103145
file.write("-> Error: Cannot test rule `clean' because rule"
104146
" `all' failed\n")
105147
print("-> Error: Cannot test rule `clean' because rule"
106148
" `all' failed")
107149
error_count += 1
108150
return error_count
151+
# Run `make clean` in the project's directory
109152
result = subprocess.run('make ' + '-C ' + project_path + ' clean',
110153
shell=True, stdout=subprocess.PIPE,
111154
stderr=subprocess.STDOUT).stdout.decode('utf-8')
112155
file.write(result + '\n')
113156
print(result)
157+
# If the binary name is not here and/or the object files are here,
158+
# the rule failed.
114159
if not os.path.exists(project_path + '/' + binary_name):
115160
file.write("-> Error: Failing Rule: It should not have "
116161
"cleaned the binary named {}.\n".format(binary_name))
@@ -125,6 +170,15 @@ def check_makefile_clean(project_path: str, binary_name: str, root_path: str):
125170

126171

127172
def check_makefile_re(project_path: str, binary_name: str, root_path: str):
173+
"""
174+
This function will check the rule `re` for the makefile of the given project.
175+
176+
:param project_path: The project path where what needs to be tested is.
177+
:param binary_name: The name of the binary the makefile will compile
178+
:param root_path: The absolute path leading to the script's main file.
179+
180+
:return: Returns the number of errors encountered during that function.
181+
"""
128182
error_count = 0
129183
makefile_path = project_path + '/Makefile'
130184
match = False
@@ -181,6 +235,15 @@ def check_makefile_re(project_path: str, binary_name: str, root_path: str):
181235

182236

183237
def check_makefile_fclean(project_path: str, binary_name: str, root_path: str):
238+
"""
239+
This function will check the rule `fclean` for the makefile of the given project.
240+
241+
:param project_path: The project path where what needs to be tested is.
242+
:param binary_name: The name of the binary the makefile will compile
243+
:param root_path: The absolute path leading to the script's main file.
244+
245+
:return: Returns the number of errors encountered during that function.
246+
"""
184247
error_count = 0
185248
makefile_path = project_path + '/Makefile'
186249
match = False
@@ -220,6 +283,15 @@ def check_makefile_fclean(project_path: str, binary_name: str, root_path: str):
220283

221284

222285
def check_makefile_name(project_path: str, binary_name: str, root_path: str):
286+
"""
287+
This function will check the rule `$(NAME)` for the makefile of the given project.
288+
289+
:param project_path: The project path where what needs to be tested is.
290+
:param binary_name: The name of the binary the makefile will compile
291+
:param root_path: The absolute path leading to the script's main file.
292+
293+
:return: Returns the number of errors encountered during that function.
294+
"""
223295
error_count = 0
224296
makefile_path = project_path + '/Makefile'
225297
match = False
@@ -258,6 +330,15 @@ def check_makefile_name(project_path: str, binary_name: str, root_path: str):
258330

259331

260332
def check_makefile_phony(project_path: str, binary_name: str, root_path: str):
333+
"""
334+
This function will check the rule `.PHONY` for the makefile of the given project.
335+
336+
:param project_path: The project path where what needs to be tested is.
337+
:param binary_name: The name of the binary the makefile will compile
338+
:param root_path: The absolute path leading to the script's main file.
339+
340+
:return: Returns the number of errors encountered during that function.
341+
"""
261342
error_count = 0
262343
makefile_path = project_path + '/Makefile'
263344
match = False
@@ -299,14 +380,15 @@ def check_makefile_phony(project_path: str, binary_name: str, root_path: str):
299380
return error_count
300381

301382

302-
def check(project_path: str, binary_name: str, root_path: str):
383+
def check(project_path: str, root_path: str):
303384
"""
304-
This function is the one you call to test the makefile given with the project.
385+
This function will run all the makefile checks.
305386
306387
:param project_path: The path of the project you want to test.
307388
:param binary_name: The binary that the makefile compiles
389+
:param root_path: The absolute path leading to the script's main file.
308390
309-
:return This functions returns 1 If an error occured, 0 otherwise.
391+
:return This functions returns the number of errors found on the makefile
310392
"""
311393
print("*---------------------------------------------------------------*")
312394
print("*----------------------------Makefile---------------------------*")
@@ -315,6 +397,9 @@ def check(project_path: str, binary_name: str, root_path: str):
315397
if not os.path.exists(makefile_path):
316398
print("--> Error: Makefile not found.")
317399
return "--> Error: Makefile not found."
400+
with open(makefile_path, 'r') as file:
401+
data = file.read()
402+
binary_name = re.findall("NAME[\s]*=[\s]*(.*)", data)[0]
318403
error_count = 0
319404
error_count += check_makefile_clean_dir(project_path, binary_name, root_path)
320405
error_count += check_makefile_all(project_path, binary_name, root_path)

0 commit comments

Comments
 (0)