import re import subprocess import sys # 1. Get student answer and strip 'public' from class/interface/record student_answer = """{{ STUDENT_ANSWER | e('py') }}""" # remove 'public' to cope with multiple compilation units in one answer student_answer = re.sub( r'\bpublic\s+(abstract\s+class|class|interface|record)\b', r'\1', student_answer ) # 2. Extract classname from student code match = re.search( r'\b(?:abstract\s+)?(class|interface|record)\s+([_a-zA-Z][_a-zA-Z0-9]*)\b', student_answer ) if not match: print("The answer didn't contain a class, interface or record.", file=sys.stderr) sys.exit(1) classname = match.group(2) # 3. Write student's Java code to file # with open(f"{classname}.java", "w") as f: with open("__studentAnswer__.java", "w") as f: f.write(student_answer) # limit the amount of compilation error output def print_first_compile_error(stderr_text, stream=sys.stderr): lines = stderr_text.strip().splitlines() for i in range(min(3, len(lines))): print(lines[i], file=stream) sys.exit(1) # 5. Compile student + tester files cargs = ["javac21", "-J-Xss512k", "-J-Xmx64m", "-J-XX:CompressedClassSpaceSize=32m", "-J-Xrs", "-J-XX:ActiveProcessorCount=1"] # first try compile by itself without template # compile_cmd1 = cargs + [f"{classname}.java"] compile_cmd1 = cargs + ["__studentAnswer__.java"] comp = subprocess.run(compile_cmd1, capture_output=True, text=True) if comp.returncode != 0: # print_first_compile_error(comp.stderr) print(comp.stderr, file=sys.stderr) sys.exit(2) # 4. Only now write __tester__.java using Twig syntax with open("__tester__.java", "w") as f: f.write(""" public class __tester__ { private static final String SEPARATOR = "##"; public static void main(String[] args) { __tester__ main = new __tester__(); main.runTests(); } public void runTests() { try { {% for TEST in TESTCASES %} { {{ TEST.testcode }} {% if not loop.last %} System.out.println(SEPARATOR); {% endif %} } {% endfor %} } catch (Throwable e) { if (e instanceof java.awt.HeadlessException) { System.err.println("GUI libraries are not supported here."); } else { System.out.println("Exception: " + e); } } } } """) compile_cmd2 = cargs + ["__tester__.java"] comp = subprocess.run(compile_cmd2, capture_output=True, text=True) if comp.returncode != 0: print_first_compile_error(comp.stderr) sys.exit(2) # 6. Run compiled Java program using java21 # Here the args don't use -J rargs = ["java21", "-Xss512k", "-Xmx64m", "-XX:CompressedClassSpaceSize=32m", "-Xrs", "-XX:ActiveProcessorCount=1"] run_cmd = rargs + ["__tester__"] # no need to capture output run = subprocess.run(run_cmd, text=True)