added function isolation patch
This commit is contained in:
@@ -15,3 +15,6 @@ grdr = "auto_grader.cli:main"
|
|||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["src"]
|
where = ["src"]
|
||||||
|
|
||||||
|
[tool.setuptools.package-data]
|
||||||
|
"auto_grader" = ["data/*"]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import subprocess
|
|||||||
import shlex
|
import shlex
|
||||||
import tomllib
|
import tomllib
|
||||||
import re
|
import re
|
||||||
|
import difflib
|
||||||
from unidecode import unidecode
|
from unidecode import unidecode
|
||||||
|
|
||||||
SUCCESS_BOX = "[\033[0;92mSUCCESS\033[0m]"
|
SUCCESS_BOX = "[\033[0;92mSUCCESS\033[0m]"
|
||||||
@@ -65,6 +66,29 @@ def _weaken_file(filename, functions):
|
|||||||
f.write(new_code)
|
f.write(new_code)
|
||||||
|
|
||||||
|
|
||||||
|
def _add_static_attribute(match):
|
||||||
|
prefix = match.group(1)
|
||||||
|
func_name = match.group(2)
|
||||||
|
suffix = match.group(3)
|
||||||
|
if "static" in prefix:
|
||||||
|
return match.group(0)
|
||||||
|
return f"{prefix} static {func_name}{suffix}"
|
||||||
|
|
||||||
|
|
||||||
|
def _staticize_file(filename, functions):
|
||||||
|
pattern = re.compile(
|
||||||
|
r"^(\s*(?:(?:\w+[\s\*]+)+))"
|
||||||
|
r"(" + "|".join(functions) + r")"
|
||||||
|
r"(\s*\([^)]*\)\s*\{)",
|
||||||
|
re.MULTILINE
|
||||||
|
)
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
code = f.read()
|
||||||
|
new_code = pattern.sub(_add_static_attribute, code)
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
f.write(new_code)
|
||||||
|
|
||||||
|
|
||||||
def _remove_static_attribute(match):
|
def _remove_static_attribute(match):
|
||||||
prefix = match.group(1)
|
prefix = match.group(1)
|
||||||
func_name = match.group(2)
|
func_name = match.group(2)
|
||||||
@@ -87,17 +111,43 @@ def _unstaticize_file(filename, functions):
|
|||||||
f.write(new_code)
|
f.write(new_code)
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_prototypes_to_header(filename, functions, header_filename):
|
||||||
|
func_pattern = re.compile(
|
||||||
|
r"^(\s*(?:(?:\w+[\s\*]+)+))"
|
||||||
|
r"(" + "|".join(functions) + r")"
|
||||||
|
r"(\s*\([^)]*\)\s*\{)",
|
||||||
|
re.MULTILINE
|
||||||
|
)
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
code = f.read()
|
||||||
|
|
||||||
|
matches = func_pattern.findall(code)
|
||||||
|
if not matches:
|
||||||
|
print(f"No functions found in {filename}")
|
||||||
|
return
|
||||||
|
|
||||||
|
prototypes = []
|
||||||
|
for prefix, func_name, args in matches:
|
||||||
|
proto = f"{prefix.strip()} static {func_name}{args.strip()[:-1]};"
|
||||||
|
prototypes.append(proto)
|
||||||
|
prototypes = list(dict.fromkeys(prototypes)) # preserve order
|
||||||
|
header_exists = os.path.exists(header_filename)
|
||||||
|
with open(header_filename, "w" if header_exists else "w") as header:
|
||||||
|
header.write('#include "solver.h"'+"\n")
|
||||||
|
for proto in prototypes:
|
||||||
|
header.write(proto + "\n")
|
||||||
|
|
||||||
|
include_line = f'#include "{os.path.basename(header_filename)}"\n'
|
||||||
|
if include_line not in code:
|
||||||
|
new_code = include_line + code
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
f.write(new_code)
|
||||||
|
|
||||||
|
|
||||||
def is_submission_dir(dir_path):
|
def is_submission_dir(dir_path):
|
||||||
return os.path.isdir(dir_path) and "_assignsubmission_file" in dir_path
|
return os.path.isdir(dir_path) and "_assignsubmission_file" in dir_path
|
||||||
|
|
||||||
|
|
||||||
# def _make_names(dir_path):
|
|
||||||
# student_names = [unidecode(name[:name.find('_')].lower().replace(
|
|
||||||
# ' ', '_')) for name in os.listdir(
|
|
||||||
# dir_path) if is_submission_dir(os.path.join(dir_path, name))]
|
|
||||||
# return student_names
|
|
||||||
|
|
||||||
|
|
||||||
def _make_names_pairs(dir_path):
|
def _make_names_pairs(dir_path):
|
||||||
student_pairs = [(unidecode(name[:name.find('_')].lower().replace(
|
student_pairs = [(unidecode(name[:name.find('_')].lower().replace(
|
||||||
' ', '_')), os.path.join(dir_path, name)) for name in os.listdir(
|
' ', '_')), os.path.join(dir_path, name)) for name in os.listdir(
|
||||||
@@ -356,8 +406,6 @@ def compile(number, path, command, verbose):
|
|||||||
@click.argument('number', required=True, type=click.INT)
|
@click.argument('number', required=True, type=click.INT)
|
||||||
@click.option('-s', '--solution-path', default='./solutions', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
@click.option('-s', '--solution-path', default='./solutions', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
||||||
@click.option('-p', '--path', default='./roots', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
@click.option('-p', '--path', default='./roots', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
|
||||||
# @click.option('-c', '--command', default='make', type=click.STRING)
|
|
||||||
# @click.option('-v', '--verbose', is_flag=True, default=False)
|
|
||||||
def dissect(number, solution_path, path):
|
def dissect(number, solution_path, path):
|
||||||
sol_as_path = os.path.join(solution_path, f"assignment_{number}")
|
sol_as_path = os.path.join(solution_path, f"assignment_{number}")
|
||||||
config_path = os.path.join(sol_as_path, "test_config.toml")
|
config_path = os.path.join(sol_as_path, "test_config.toml")
|
||||||
@@ -384,6 +432,11 @@ def dissect(number, solution_path, path):
|
|||||||
f"{ERROR_BOX}: No function dissect config found for assignment_{number}.", err=True)
|
f"{ERROR_BOX}: No function dissect config found for assignment_{number}.", err=True)
|
||||||
return
|
return
|
||||||
all_functions = config['dissect']['all_functions']
|
all_functions = config['dissect']['all_functions']
|
||||||
|
if not 'build_cmd' in config["dissect"]:
|
||||||
|
click.echo(
|
||||||
|
f"{ERROR_BOX}: No build config found for assignment_{number}.", err=True)
|
||||||
|
return
|
||||||
|
static_functions = config['dissect']['static_functions']
|
||||||
if not 'build_cmd' in config["dissect"]:
|
if not 'build_cmd' in config["dissect"]:
|
||||||
click.echo(
|
click.echo(
|
||||||
f"{ERROR_BOX}: No build config found for assignment_{number}.", err=True)
|
f"{ERROR_BOX}: No build config found for assignment_{number}.", err=True)
|
||||||
@@ -428,11 +481,17 @@ def dissect(number, solution_path, path):
|
|||||||
click.echo(
|
click.echo(
|
||||||
f"\t[{os.path.basename(root)}]:{ERROR_BOX}: Missing source {os.path.basename(src)}")
|
f"\t[{os.path.basename(root)}]:{ERROR_BOX}: Missing source {os.path.basename(src)}")
|
||||||
shutil.copyfile(src, dest)
|
shutil.copyfile(src, dest)
|
||||||
|
|
||||||
|
_extract_prototypes_to_header(
|
||||||
|
dest, static_functions, os.path.join(sol_as_path, 'src', 'aid.h'))
|
||||||
|
|
||||||
_unstaticize_file(dest, all_functions)
|
_unstaticize_file(dest, all_functions)
|
||||||
_weaken_file(dest, all_functions)
|
_weaken_file(dest, all_functions)
|
||||||
# BUILD TEST
|
_staticize_file(dest, static_functions)
|
||||||
|
|
||||||
result = 0
|
result = 0
|
||||||
os.chdir(sol_as_path)
|
os.chdir(sol_as_path)
|
||||||
|
|
||||||
for function, flag in zip(test_functions, flags):
|
for function, flag in zip(test_functions, flags):
|
||||||
command = build_cmd + f" CFLAGS+={flag}"
|
command = build_cmd + f" CFLAGS+={flag}"
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
@@ -446,13 +505,9 @@ def dissect(number, solution_path, path):
|
|||||||
else:
|
else:
|
||||||
click.echo(
|
click.echo(
|
||||||
f"\t[{os.path.basename(root)}]:[BUILD]:[{function}]:{ERROR_BOX}")
|
f"\t[{os.path.basename(root)}]:[BUILD]:[{function}]:{ERROR_BOX}")
|
||||||
click.echo(f"stderr: {result.stderr[:69]:69s}...")
|
click.echo(f"\t\tstderr: {result.stderr[:61]:61s}...")
|
||||||
os.chdir(current_wd)
|
os.chdir(current_wd)
|
||||||
|
|
||||||
# for file in solution_files:
|
|
||||||
# _unstaticize_file(file, test_functions)
|
|
||||||
# _weaken_file(file, all_functions)
|
|
||||||
|
|
||||||
|
|
||||||
cli.add_command(init)
|
cli.add_command(init)
|
||||||
cli.add_command(archives)
|
cli.add_command(archives)
|
||||||
|
|||||||
Reference in New Issue
Block a user