diff --git a/src/sinol_make/__init__.py b/src/sinol_make/__init__.py index ffa53349..5a936997 100644 --- a/src/sinol_make/__init__.py +++ b/src/sinol_make/__init__.py @@ -12,7 +12,7 @@ from sinol_make.task_type.interactive import InteractiveTaskType # noqa -__version__ = "1.9.7" +__version__ = "1.9.8" def configure_parsers(): diff --git a/src/sinol_make/commands/chkwer/__init__.py b/src/sinol_make/commands/chkwer/__init__.py index 55d3e1ca..b5787096 100644 --- a/src/sinol_make/commands/chkwer/__init__.py +++ b/src/sinol_make/commands/chkwer/__init__.py @@ -42,7 +42,7 @@ def compile(self, file_path, exe_path, args, name, compilation_flags): print(f'Compiling {name}... ', end='') compilers = compiler.verify_compilers(args, [file_path]) exe, compile_log_path = compile.compile_file(file_path, exe_path, compilers, compilation_flags, - use_fsanitize=False, use_extras=False) + use_sanitizers=self.args.sanitize, use_extras=False) if exe is None: print(util.error('ERROR')) compile.print_compile_log(compile_log_path) @@ -57,11 +57,11 @@ def run_test(self, execution: ChkwerExecution) -> RunResult: """ output_file = paths.get_chkwer_path(os.path.basename(execution.out_test_path)) with open(execution.in_test_path, 'r') as inf, open(output_file, 'w') as outf: - process = subprocess.Popen([execution.model_exe], stdin=inf, stdout=outf) - process.wait() + process = subprocess.Popen([execution.model_exe], stdin=inf, stdout=outf, stderr=subprocess.PIPE) + _, stderr = process.communicate() ok, points, comment = self.task_type.check_output(execution.in_test_path, output_file, execution.out_test_path) - return RunResult(execution.in_test_path, ok, int(points), comment) + return RunResult(execution.in_test_path, ok, int(points), comment, stderr.decode('utf-8')) def run_and_print_table(self) -> Dict[str, TestResult]: results = {} @@ -84,7 +84,7 @@ def run_and_print_table(self) -> Dict[str, TestResult]: try: with mp.Pool(self.cpus) as pool: for i, result in enumerate(pool.imap(self.run_test, executions)): - table_data.results[result.test_path].set_results(result.points, result.ok, result.comment) + table_data.results[result.test_path].set_results(result.points, result.ok, result.comment, result.stderr) table_data.i = i except KeyboardInterrupt: keyboard_interrupt = True @@ -99,7 +99,7 @@ def run_and_print_table(self) -> Dict[str, TestResult]: return results def run(self, args): - args = util.init_package_command(args) + self.args = util.init_package_command(args) self.task_id = package_util.get_task_id() self.task_type = package_util.get_task_type("time", None) self.contest_type = contest_types.get_contest_type() diff --git a/src/sinol_make/commands/chkwer/chkwer_util.py b/src/sinol_make/commands/chkwer/chkwer_util.py index c4708eb1..4291135b 100644 --- a/src/sinol_make/commands/chkwer/chkwer_util.py +++ b/src/sinol_make/commands/chkwer/chkwer_util.py @@ -59,7 +59,9 @@ def print_line_separator(): output = [] if result.run: - if result.comment: + if result.stderr != "": + print(util.error("stderr:"), result.stderr, end=' | ') + elif result.comment: print(result.comment) else: print(util.color_gray("No comment")) diff --git a/src/sinol_make/commands/export/__init__.py b/src/sinol_make/commands/export/__init__.py index 7eeb367d..232c5b37 100644 --- a/src/sinol_make/commands/export/__init__.py +++ b/src/sinol_make/commands/export/__init__.py @@ -56,7 +56,7 @@ def generate_input_tests(self): if ingen_exists(self.task_id): ingen_path = get_ingen(self.task_id) ingen_path = os.path.join(prog_dir, os.path.basename(ingen_path)) - ingen_exe = compile_ingen(ingen_path, self.args, self.args.compile_mode) + ingen_exe = compile_ingen(ingen_path, self.args, self.args.compile_mode, use_sanitizers=self.args.sanitize) if not run_ingen(ingen_exe, in_dir): util.exit_with_error('Failed to run ingen.') @@ -76,7 +76,7 @@ def generate_output_files(self): if len(outputs) > 0: outgen = OutgenCommand() correct_solution_exe = compile_correct_solution(get_correct_solution(self.task_id), self.args, - self.args.compile_mode) + self.args.compile_mode, use_sanitizers=self.args.sanitize) outgen.args = self.args outgen.correct_solution_exe = correct_solution_exe outgen.generate_outputs(outputs) diff --git a/src/sinol_make/commands/gen/__init__.py b/src/sinol_make/commands/gen/__init__.py index c568252a..dbff0d03 100644 --- a/src/sinol_make/commands/gen/__init__.py +++ b/src/sinol_make/commands/gen/__init__.py @@ -37,7 +37,6 @@ def configure_subparser(self, subparser): parsers.add_cpus_argument(parser, 'number of cpus to use to generate output files') parser.add_argument('-n', '--no-validate', default=False, action='store_true', help='do not validate test contents') - parsers.add_fsanitize_argument(parser) parsers.add_compilation_arguments(parser) return parser diff --git a/src/sinol_make/commands/ingen/__init__.py b/src/sinol_make/commands/ingen/__init__.py index 6f45acaa..d20fe0e3 100644 --- a/src/sinol_make/commands/ingen/__init__.py +++ b/src/sinol_make/commands/ingen/__init__.py @@ -31,7 +31,6 @@ def configure_subparser(self, subparser): parser.add_argument('-n', '--no-validate', default=False, action='store_true', help='do not validate test contents') parsers.add_cpus_argument(parser, 'number of cpus used for validating tests') - parsers.add_fsanitize_argument(parser) parsers.add_compilation_arguments(parser) return parser @@ -67,7 +66,8 @@ def run(self, args: argparse.Namespace): util.change_stack_size_to_unlimited() self.ingen = get_ingen(self.task_id, args.ingen_path) print(f'Using ingen file {os.path.basename(self.ingen)}') - self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, self.args.fsanitize) + self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, + use_sanitizers=self.args.sanitize) previous_tests = [] try: diff --git a/src/sinol_make/commands/ingen/ingen_util.py b/src/sinol_make/commands/ingen/ingen_util.py index 2c6eecb5..03542289 100644 --- a/src/sinol_make/commands/ingen/ingen_util.py +++ b/src/sinol_make/commands/ingen/ingen_util.py @@ -47,7 +47,7 @@ def get_ingen(task_id, ingen_path=None): return correct_ingen -def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False): +def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default', use_sanitizers='no'): """ Compiles ingen and returns path to compiled executable. If ingen_path is shell script, then it will be returned. @@ -57,7 +57,7 @@ def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags=' compilers = compiler.verify_compilers(args, [ingen_path]) ingen_exe, compile_log_path = compile.compile_file(ingen_path, package_util.get_executable(ingen_path), - compilers, compilation_flags, use_fsanitize=use_fsanitize, + compilers, compilation_flags, use_sanitizers=use_sanitizers, additional_flags='-D_INGEN', use_extras=False) if ingen_exe is None: @@ -84,7 +84,7 @@ def run_ingen(ingen_exe, working_dir=None): os.chmod(ingen_exe, st.st_mode | stat.S_IEXEC) print(util.bold(' Ingen output '.center(util.get_terminal_size()[1], '='))) - process = subprocess.Popen([ingen_exe], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + process = subprocess.Popen([ingen_exe], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=working_dir, shell=is_shell) whole_output = '' while process.poll() is None: @@ -98,6 +98,11 @@ def run_ingen(ingen_exe, working_dir=None): exit_code = process.returncode print(util.bold(' End of ingen output '.center(util.get_terminal_size()[1], '='))) + stderr = process.stderr.read() + if stderr: + print(util.error('Ingen error output:')) + print(stderr.decode('utf-8'), end='\n\n') + if util.has_sanitizer_error(whole_output, exit_code): print(util.warning('Warning: if ingen failed due to sanitizer errors, you can either run ' '`sudo sysctl vm.mmap_rnd_bits=28` to fix this or disable sanitizers with the ' diff --git a/src/sinol_make/commands/inwer/__init__.py b/src/sinol_make/commands/inwer/__init__.py index a050a759..10562293 100644 --- a/src/sinol_make/commands/inwer/__init__.py +++ b/src/sinol_make/commands/inwer/__init__.py @@ -40,7 +40,6 @@ def configure_subparser(self, subparser: argparse.ArgumentParser): parser.add_argument('-t', '--tests', type=str, nargs='+', help='test to verify, for example in/abc{0,1}*') parsers.add_cpus_argument(parser, 'number of cpus to use when verifying tests') - parsers.add_fsanitize_argument(parser) parsers.add_compilation_arguments(parser) return parser @@ -204,7 +203,8 @@ def run(self, args: argparse.Namespace): print('Verifying tests: ' + util.bold(', '.join(self.tests))) util.change_stack_size_to_unlimited() - self.inwer_executable = inwer_util.compile_inwer(self.inwer, args, args.compile_mode, args.fsanitize) + self.inwer_executable = inwer_util.compile_inwer(self.inwer, args, args.compile_mode, + use_sanitizers=args.sanitize) results: Dict[str, TestResult] = self.verify_and_print_table() print('') diff --git a/src/sinol_make/commands/inwer/inwer_util.py b/src/sinol_make/commands/inwer/inwer_util.py index b8cc31cd..a4523c39 100644 --- a/src/sinol_make/commands/inwer/inwer_util.py +++ b/src/sinol_make/commands/inwer/inwer_util.py @@ -29,13 +29,13 @@ def get_inwer_path(task_id: str, path=None) -> Union[str, None]: return None -def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False): +def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='default', use_sanitizers='no'): """ Compiles inwer and returns path to compiled executable and path to compile log. """ compilers = compiler.verify_compilers(args, [inwer_path]) inwer_exe, compile_log_path = compile.compile_file(inwer_path, package_util.get_executable(inwer_path), compilers, - compilation_flags, use_fsanitize=use_fsanitize, + compilation_flags, use_sanitizers=use_sanitizers, additional_flags='-D_INWER', use_extras=False) if inwer_exe is None: diff --git a/src/sinol_make/commands/outgen/__init__.py b/src/sinol_make/commands/outgen/__init__.py index fc71abf8..21300e07 100644 --- a/src/sinol_make/commands/outgen/__init__.py +++ b/src/sinol_make/commands/outgen/__init__.py @@ -43,12 +43,16 @@ def generate_outputs(self, outputs_to_generate): with mp.Pool(self.args.cpus) as pool: results = [] - for i, result in enumerate(pool.imap(generate_output, arguments)): + for i, (result, stderr) in enumerate(pool.imap(generate_output, arguments)): results.append(result) + output_file = os.path.basename(arguments[i].output_test) + if stderr: + print(util.error(f'Outgen stderr on {output_file}:')) + print(stderr.decode('utf-8'), end='\n\n') if result: - print(f'Successfully generated output file {os.path.basename(arguments[i].output_test)}') + print(f'Successfully generated output file {output_file}') else: - print(util.error(f'Failed to generate output file {os.path.basename(arguments[i].output_test)}')) + print(util.error(f'Failed to generate output file {output_file}')) if not all(results): util.exit_with_error('Failed to generate some output files.') @@ -123,7 +127,8 @@ def run(self, args: argparse.Namespace): else: self.clean_cache(from_inputs) self.correct_solution_exe = compile_correct_solution(self.correct_solution, self.args, - self.args.compile_mode) + self.args.compile_mode, + use_sanitizers=self.args.sanitize) self.generate_outputs(outputs_to_generate) with open(os.path.join(os.getcwd(), 'in', '.md5sums'), 'w') as f: yaml.dump(md5_sums, f) diff --git a/src/sinol_make/commands/outgen/outgen_util.py b/src/sinol_make/commands/outgen/outgen_util.py index 8557345b..763e663e 100644 --- a/src/sinol_make/commands/outgen/outgen_util.py +++ b/src/sinol_make/commands/outgen/outgen_util.py @@ -21,13 +21,13 @@ def get_correct_solution(task_id): return correct_solution[0] -def compile_correct_solution(solution_path: str, args: argparse.Namespace, compilation_flags='default'): +def compile_correct_solution(solution_path: str, args: argparse.Namespace, compilation_flags='default', use_sanitizers='no'): """ Compiles correct solution and returns path to compiled executable. """ compilers = compiler.verify_compilers(args, [solution_path]) correct_solution_exe, compile_log_path = compile.compile_file(solution_path, package_util.get_executable(solution_path), compilers, - compilation_flags) + compilation_flags, use_sanitizers=use_sanitizers) if correct_solution_exe is None: util.exit_with_error('Failed compilation of correct solution.', lambda: compile.print_compile_log(compile_log_path)) @@ -49,7 +49,7 @@ def generate_output(arguments): input_file = open(input_test, 'r') output_file = open(output_test, 'w') - process = subprocess.Popen([correct_solution_exe], stdin=input_file, stdout=output_file, preexec_fn=os.setsid) + process = subprocess.Popen([correct_solution_exe], stdin=input_file, stdout=output_file, stderr=subprocess.PIPE, preexec_fn=os.setsid) previous_sigint_handler = signal.getsignal(signal.SIGINT) def sigint_handler(signum, frame): @@ -60,10 +60,10 @@ def sigint_handler(signum, frame): sys.exit(1) signal.signal(signal.SIGINT, sigint_handler) - process.wait() + _, stderr = process.communicate() signal.signal(signal.SIGINT, previous_sigint_handler) exit_code = process.returncode input_file.close() output_file.close() - return exit_code == 0 + return exit_code == 0, stderr diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index 1ce6efc7..113a6963 100644 --- a/src/sinol_make/commands/run/__init__.py +++ b/src/sinol_make/commands/run/__init__.py @@ -304,7 +304,9 @@ def configure_subparser(self, subparser): help='allow running the script without full outputs') parser.add_argument('-o', '--comments', dest='comments', action='store_true', help="show checker's comments") - parsers.add_compilation_arguments(parser) + parsers.add_compilation_arguments(parser, custom_sanitize_help='When using sanitizers, make sure ' + 'that you are running with `time` tool for measuring time and memory. ' + 'Sio2jail does not support sanitizers.') return parser def extract_file_name(self, file_path): @@ -365,7 +367,8 @@ def compile(self, solution, dest=None, use_extras=False, clear_cache=False, name try: with open(compile_log_file, "w") as compile_log: compile.compile(source_file, output, self.compilers, compile_log, self.args.compile_mode, - extra_compilation_args, extra_compilation_files, clear_cache=clear_cache) + extra_compilation_args, extra_compilation_files, clear_cache=clear_cache, + use_sanitizers=self.args.sanitize) print(util.info(f"Compilation of {name} was successful.")) return True except CompilationError as e: diff --git a/src/sinol_make/helpers/cache.py b/src/sinol_make/helpers/cache.py index 5c3923eb..1bf5b52c 100644 --- a/src/sinol_make/helpers/cache.py +++ b/src/sinol_make/helpers/cache.py @@ -34,7 +34,7 @@ def get_cache_file(solution_path: str) -> CacheFile: return CacheFile() -def check_compiled(file_path: str, compilation_flags: str, sanitizers: bool) -> Union[str, None]: +def check_compiled(file_path: str, compilation_flags: str, sanitizers: str) -> Union[str, None]: """ Check if a file is compiled :param file_path: Path to the file diff --git a/src/sinol_make/helpers/compile.py b/src/sinol_make/helpers/compile.py index 30f445ba..720e4734 100644 --- a/src/sinol_make/helpers/compile.py +++ b/src/sinol_make/helpers/compile.py @@ -14,7 +14,7 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, compilation_flags='default', - extra_compilation_args=None, extra_compilation_files=None, clear_cache=False, use_fsanitize=False): + extra_compilation_args=None, extra_compilation_files=None, clear_cache=False, use_sanitizers='no'): """ Compile a program. :param program: Path to the program to compile @@ -25,7 +25,7 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp :param extra_compilation_args: Extra compilation arguments :param extra_compilation_files: Extra compilation files :param clear_cache: Set to True if you want to delete all cached test results. - :param use_fsanitize: Whether to use fsanitize when compiling C/C++ programs. Sanitizes address and undefined behavior. + :param use_sanitizers: Whether to use sanitizers for C / C++ programs. Check flag --sanitize for available options. """ if extra_compilation_args is None: extra_compilation_args = [] @@ -34,8 +34,8 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp assert isinstance(extra_compilation_args, list) and all(isinstance(arg, str) for arg in extra_compilation_args) # Address and undefined sanitizer is not yet supported on Apple Silicon. - if use_fsanitize and util.is_macos_arm(): - use_fsanitize = False + if use_sanitizers and util.is_macos_arm(): + use_sanitizers = 'no' if compilation_flags == 'w': compilation_flags = 'weak' @@ -47,7 +47,7 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp if extra_compilation_files is None: extra_compilation_files = [] - compiled_exe = check_compiled(program, compilation_flags, use_fsanitize) + compiled_exe = check_compiled(program, compilation_flags, use_sanitizers) if compiled_exe is not None: if compile_log is not None: compile_log.write(f'Using cached executable {compiled_exe}\n') @@ -77,14 +77,10 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp arguments = [compilers.cpp_compiler_path or compiler.get_cpp_compiler_path(), program] + \ extra_compilation_args + ['-o', output] + \ f'--std=c++20 -O3 -lm{gcc_compilation_flags} -fdiagnostics-color'.split(' ') - if use_fsanitize and compilation_flags != 'weak': - arguments += ['-fsanitize=address,undefined', '-fno-sanitize-recover'] elif ext == '.c': arguments = [compilers.c_compiler_path or compiler.get_c_compiler_path(), program] + \ extra_compilation_args + ['-o', output] + \ f'--std=gnu99 -O3 -lm{gcc_compilation_flags} -fdiagnostics-color'.split(' ') - if use_fsanitize and compilation_flags != 'weak': - arguments += ['-fsanitize=address,undefined', '-fno-sanitize-recover'] elif ext == '.py': if sys.platform == 'win32' or sys.platform == 'cygwin': # TODO: Make this work on Windows @@ -103,6 +99,17 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp else: raise CompilationError('Unknown file extension: ' + ext) + if use_sanitizers != 'no' and compilation_flags != 'weak': + if use_sanitizers == 'simple': + arguments += ['-fsanitize=address,undefined', '-fno-sanitize-recover'] + elif use_sanitizers == 'full' and ext == '.cpp': + arguments = [compilers.cpp_compiler_path or compiler.get_cpp_compiler_path(), program] + \ + extra_compilation_args + ['-o', output, '-fdiagnostics-color'] + arguments += ['-Wall', '-Wextra', '-pedantic', '-std=c++20', '-O2', '-Wshadow', '-Wformat=2', '-Wfloat-equal', + '-Wconversion', '-Wlogical-op', '-Wshift-overflow=2', '-Wduplicated-cond', '-Wcast-qual', + '-Wcast-align', '-D_GLIBCXX_DEBUG', '-D_GLIBCXX_DEBUG_PEDANTIC', '-D_FORTIFY_SOURCE=2', + '-fsanitize=address', '-fsanitize=undefined', '-fno-sanitize-recover', '-fstack-protector'] + process = subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out, _ = process.communicate() if compile_log is not None: @@ -114,12 +121,12 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp if process.returncode != 0: raise CompilationError('Compilation failed') else: - save_compiled(program, output, compilation_flags, use_fsanitize, clear_cache) + save_compiled(program, output, compilation_flags, use_sanitizers, clear_cache) return True def compile_file(file_path: str, name: str, compilers: Compilers, compilation_flags='default', - use_fsanitize=False, additional_flags=None, use_extras=True) \ + use_sanitizers='no', additional_flags=None, use_extras=True) \ -> Tuple[Union[str, None], str]: """ Compile a file @@ -127,7 +134,7 @@ def compile_file(file_path: str, name: str, compilers: Compilers, compilation_fl :param name: Name of the executable :param compilers: Compilers object :param compilation_flags: Group of compilation flags to use - :param use_fsanitize: Whether to use fsanitize when compiling C/C++ programs. Sanitizes address and undefined behavior. + :param use_sanitizers: Whether to use sanitizers for C / C++ programs. Check flag --sanitize for available options. :param additional_flags: Additional flags for c / c++ compiler. :param use_extras: Whether to use extra compilation files and arguments from config :return: Tuple of (executable path or None if compilation failed, log path) @@ -158,7 +165,7 @@ def compile_file(file_path: str, name: str, compilers: Compilers, compilation_fl with open(compile_log_path, 'w') as compile_log: try: if compile(file_path, output, compilers, compile_log, compilation_flags, extra_compilation_args, - extra_compilation_files, use_fsanitize=use_fsanitize): + extra_compilation_files, use_sanitizers=use_sanitizers): return output, compile_log_path except CompilationError: pass diff --git a/src/sinol_make/helpers/parsers.py b/src/sinol_make/helpers/parsers.py index 00ec840a..f3e64a9d 100644 --- a/src/sinol_make/helpers/parsers.py +++ b/src/sinol_make/helpers/parsers.py @@ -5,7 +5,7 @@ from sinol_make.helpers import compiler -def add_compilation_arguments(parser: argparse.ArgumentParser): +def add_compilation_arguments(parser: argparse.ArgumentParser, custom_sanitize_help=""): if sys.platform == 'darwin': gcc_versions = 'gcc-9, gcc-10, gcc-11' gpp_versions = 'g++-9, g++-10, g++-11' @@ -30,6 +30,13 @@ def add_compilation_arguments(parser: argparse.ArgumentParser): ' oioioi / o - uses the same flags as oioioi:\n' ' (-Wall -Wno-unused-result -Werror)' ' weak / w - disable all warning flags during C and C++ compilation', default='default') + parser.add_argument('--sanitize', dest='sanitize', choices=['no', 'simple', 'full'], + help='Use sanitizers for compilation. Available options:\n' + ' no - no sanitizers\n' + ' simple - use address and undefined sanitizer\n' + ' full - use a lot of sanitizers, full list: https://codeforces.com/blog/entry/15547\n' + 'Sanitizers may fail on some systems. To fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`. ' + \ + custom_sanitize_help,) def add_cpus_argument(parser: argparse.ArgumentParser, help: str): diff --git a/src/sinol_make/structs/cache_structs.py b/src/sinol_make/structs/cache_structs.py index 41efa19b..1de9d2eb 100644 --- a/src/sinol_make/structs/cache_structs.py +++ b/src/sinol_make/structs/cache_structs.py @@ -70,11 +70,15 @@ def to_dict(self) -> Dict: @staticmethod def from_dict(dict) -> 'CacheFile': + # Backwards compatibility. Before v1.9.8, sanitizers were stored as a bool. + if type(dict.get("sanitizers", "no")) == bool: + dict["sanitizers"] = "simple" if dict["sanitizers"] else "no" + return CacheFile( md5sum=dict.get("md5sum", ""), executable_path=dict.get("executable_path", ""), compilation_flags=dict.get("compilation_flags", "default"), - sanitizers=dict.get("sanitizers", False), + sanitizers=dict.get("sanitizers", 'no'), tests={k: CacheTest( time_limit=v["time_limit"], memory_limit=v["memory_limit"], diff --git a/src/sinol_make/structs/chkwer_structs.py b/src/sinol_make/structs/chkwer_structs.py index 2f339511..2daad36a 100644 --- a/src/sinol_make/structs/chkwer_structs.py +++ b/src/sinol_make/structs/chkwer_structs.py @@ -14,6 +14,7 @@ class TestResult: points: int ok: bool comment: str + stderr: str def __init__(self, test_path, task_id): self.test_path = test_path @@ -24,12 +25,14 @@ def __init__(self, test_path, task_id): self.points = 0 self.ok = False self.run = False + self.stderr = "" - def set_results(self, points, ok, output): + def set_results(self, points, ok, output, stderr): self.run = True self.points = points self.ok = ok self.comment = output + self.stderr = stderr @dataclass @@ -66,3 +69,4 @@ class RunResult: ok: bool points: int comment: str + stderr: str diff --git a/tests/conftest.py b/tests/conftest.py index e42622bb..53637676 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,7 +21,7 @@ def _compile(args): use_fsanitize = fnmatch.fnmatch(basename, "*ingen*") or fnmatch.fnmatch(basename, "*inwer*") try: with open(compile_log_path, "w") as compile_log: - compile.compile(file_path, output, compile_log=compile_log, use_fsanitize=use_fsanitize) + compile.compile(file_path, output, compile_log=compile_log, use_sanitizers=use_fsanitize) except CompilationError: compile.print_compile_log(compile_log_path) raise