From 73ca1fe9bf83fa832780d7ac0522e76abcd025b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Czech?= Date: Thu, 7 Mar 2024 19:51:37 +0100 Subject: [PATCH] Additional compilation flags (#200) * Change weak flag option to compile mode Added flages used by oioioi as an option * Minor changes and additional test * Update descriptions * Add aliases for compile_mode arguments --- src/sinol_make/commands/export/__init__.py | 4 +-- src/sinol_make/commands/ingen/__init__.py | 2 +- src/sinol_make/commands/ingen/ingen_util.py | 4 +-- src/sinol_make/commands/inwer/__init__.py | 2 +- src/sinol_make/commands/inwer/inwer_util.py | 4 +-- src/sinol_make/commands/outgen/__init__.py | 2 +- src/sinol_make/commands/outgen/outgen_util.py | 4 +-- src/sinol_make/commands/run/__init__.py | 2 +- src/sinol_make/helpers/compile.py | 30 +++++++++++-------- src/sinol_make/helpers/parsers.py | 24 +++++++++------ tests/commands/export/test_unit.py | 2 +- tests/commands/export/util.py | 2 +- tests/commands/run/test_integration.py | 25 ++++++++++++++-- tests/commands/run/test_unit.py | 2 +- tests/commands/run/util.py | 2 +- tests/packages/oioioi_flags/config.yml | 11 +++++++ tests/packages/oioioi_flags/in/.gitkeep | 0 tests/packages/oioioi_flags/in/oif1a.in | 1 + tests/packages/oioioi_flags/no-precompile | 0 tests/packages/oioioi_flags/out/.gitkeep | 0 tests/packages/oioioi_flags/out/oif1a.out | 1 + tests/packages/oioioi_flags/prog/oif.cpp | 10 +++++++ tests/packages/wcf/config.yml | 2 +- tests/util.py | 7 +++++ 24 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 tests/packages/oioioi_flags/config.yml create mode 100644 tests/packages/oioioi_flags/in/.gitkeep create mode 100644 tests/packages/oioioi_flags/in/oif1a.in create mode 100644 tests/packages/oioioi_flags/no-precompile create mode 100644 tests/packages/oioioi_flags/out/.gitkeep create mode 100644 tests/packages/oioioi_flags/out/oif1a.out create mode 100644 tests/packages/oioioi_flags/prog/oif.cpp diff --git a/src/sinol_make/commands/export/__init__.py b/src/sinol_make/commands/export/__init__.py index e8248006..5d072040 100644 --- a/src/sinol_make/commands/export/__init__.py +++ b/src/sinol_make/commands/export/__init__.py @@ -55,7 +55,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.weak_compilation_flags) + ingen_exe = compile_ingen(ingen_path, self.args, self.args.compile_mode) if not run_ingen(ingen_exe, in_dir): util.exit_with_error('Failed to run ingen.') @@ -75,7 +75,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.weak_compilation_flags) + self.args.compile_mode) outgen.args = self.args outgen.correct_solution_exe = correct_solution_exe outgen.generate_outputs(outputs) diff --git a/src/sinol_make/commands/ingen/__init__.py b/src/sinol_make/commands/ingen/__init__.py index d767b8ff..03d7e6b8 100644 --- a/src/sinol_make/commands/ingen/__init__.py +++ b/src/sinol_make/commands/ingen/__init__.py @@ -42,7 +42,7 @@ def run(self, args: argparse.Namespace): util.change_stack_size_to_unlimited() self.ingen = get_ingen(self.task_id, args.ingen_path) print(util.info(f'Using ingen file {os.path.basename(self.ingen)}')) - self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.weak_compilation_flags) + self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode) if run_ingen(self.ingen_exe): print(util.info('Successfully generated input files.')) diff --git a/src/sinol_make/commands/ingen/ingen_util.py b/src/sinol_make/commands/ingen/ingen_util.py index 31269ed4..5e9263fe 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, weak_compilation_flags=False): +def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default'): """ 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, weak_compilation_fl compilers = compiler.verify_compilers(args, [ingen_path]) ingen_exe, compile_log_path = compile.compile_file(ingen_path, package_util.get_executable(ingen_path), compilers, - weak_compilation_flags, use_fsanitize=True) + compilation_flags, use_fsanitize=True) if ingen_exe is None: compile.print_compile_log(compile_log_path) diff --git a/src/sinol_make/commands/inwer/__init__.py b/src/sinol_make/commands/inwer/__init__.py index 740c9e0e..40bcf8d0 100644 --- a/src/sinol_make/commands/inwer/__init__.py +++ b/src/sinol_make/commands/inwer/__init__.py @@ -42,7 +42,7 @@ def configure_subparser(self, subparser: argparse.ArgumentParser): add_compilation_arguments(parser) def compile_inwer(self, args: argparse.Namespace): - self.inwer_executable, compile_log_path = inwer_util.compile_inwer(self.inwer, args, args.weak_compilation_flags) + self.inwer_executable, compile_log_path = inwer_util.compile_inwer(self.inwer, args, args.compile_mode) if self.inwer_executable is None: util.exit_with_error('Compilation failed.', lambda: compile.print_compile_log(compile_log_path)) else: diff --git a/src/sinol_make/commands/inwer/inwer_util.py b/src/sinol_make/commands/inwer/inwer_util.py index 03c87cbd..d423f5db 100644 --- a/src/sinol_make/commands/inwer/inwer_util.py +++ b/src/sinol_make/commands/inwer/inwer_util.py @@ -29,12 +29,12 @@ def get_inwer_path(task_id: str, path = None) -> Union[str, None]: return None -def compile_inwer(inwer_path: str, args: argparse.Namespace, weak_compilation_flags=False): +def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='default'): """ Compiles inwer and returns path to compiled executable and path to compile log. """ compilers = compiler.verify_compilers(args, [inwer_path]) - return compile.compile_file(inwer_path, package_util.get_executable(inwer_path), compilers, weak_compilation_flags, + return compile.compile_file(inwer_path, package_util.get_executable(inwer_path), compilers, compilation_flags, use_fsanitize=True) diff --git a/src/sinol_make/commands/outgen/__init__.py b/src/sinol_make/commands/outgen/__init__.py index 7b8719a0..4c77ad1e 100644 --- a/src/sinol_make/commands/outgen/__init__.py +++ b/src/sinol_make/commands/outgen/__init__.py @@ -106,7 +106,7 @@ def run(self, args: argparse.Namespace): print(util.info('All output files are up to date.')) else: self.correct_solution_exe = compile_correct_solution(self.correct_solution, self.args, - self.args.weak_compilation_flags) + self.args.compile_mode) 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 0acbf3c3..8557345b 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, weak_compilation_flags=False): +def compile_correct_solution(solution_path: str, args: argparse.Namespace, compilation_flags='default'): """ 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, - weak_compilation_flags) + compilation_flags) if correct_solution_exe is None: util.exit_with_error('Failed compilation of correct solution.', lambda: compile.print_compile_log(compile_log_path)) diff --git a/src/sinol_make/commands/run/__init__.py b/src/sinol_make/commands/run/__init__.py index 6033f4e8..a6cd727d 100644 --- a/src/sinol_make/commands/run/__init__.py +++ b/src/sinol_make/commands/run/__init__.py @@ -361,7 +361,7 @@ def compile(self, solution, use_extras = False, is_checker = False): try: with open(compile_log_file, "w") as compile_log: - compile.compile(source_file, output, self.compilers, compile_log, self.args.weak_compilation_flags, + compile.compile(source_file, output, self.compilers, compile_log, self.args.compile_mode, extra_compilation_args, extra_compilation_files, is_checker=is_checker) print(util.info("Compilation of file %s was successful." % package_util.get_file_name(solution))) diff --git a/src/sinol_make/helpers/compile.py b/src/sinol_make/helpers/compile.py index 5c1c9375..7b99e17c 100644 --- a/src/sinol_make/helpers/compile.py +++ b/src/sinol_make/helpers/compile.py @@ -4,7 +4,6 @@ import shutil import stat import subprocess -import yaml import sinol_make.helpers.compiler as compiler from sinol_make import util @@ -14,15 +13,15 @@ from sinol_make.structs.compiler_structs import Compilers -def compile(program, output, compilers: Compilers = None, compile_log = None, weak_compilation_flags = False, - extra_compilation_args = None, extra_compilation_files = None, is_checker = False, use_fsanitize = False): +def compile(program, output, compilers: Compilers = None, compile_log=None, compilation_flags='default', + extra_compilation_args=None, extra_compilation_files=None, is_checker=False, use_fsanitize=False): """ Compile a program. :param program: Path to the program to compile :param output: Path to the output file :param compilers: Compilers object :param compile_log: File to write the compilation log to - :param weak_compilation_flags: If True, disable all warnings + :param compilation_flags: Group of compilation flags to use :param extra_compilation_args: Extra compilation arguments :param extra_compilation_files: Extra compilation files :param is_checker: Set to True if compiling a checker. This will remove all cached test results. @@ -53,30 +52,37 @@ def compile(program, output, compilers: Compilers = None, compile_log = None, we for file in extra_compilation_files: shutil.copy(file, os.path.join(os.path.dirname(output), os.path.basename(file))) - gcc_compilation_flags = ' -Werror -Wall -Wextra -Wshadow -Wconversion -Wno-unused-result -Wfloat-equal' - if weak_compilation_flags: + gcc_compilation_flags = '' + if compilation_flags == 'weak' or compilation_flags == 'w': + compilation_flags = 'weak' gcc_compilation_flags = '' # Disable all warnings + elif compilation_flags == 'oioioi' or compilation_flags == 'o': + gcc_compilation_flags = ' -Wall -Wno-unused-result -Werror' # Same flags as oioioi + elif compilation_flags == 'default' or compilation_flags == 'd': + gcc_compilation_flags = ' -Werror -Wall -Wextra -Wshadow -Wconversion -Wno-unused-result -Wfloat-equal' + else: + util.exit_with_error(f'Unknown compilation flags group: {compilation_flags}') if compilers is None: compilers = Compilers() ext = os.path.splitext(program)[1] - arguments = [] if ext == '.cpp': 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 not weak_compilation_flags: + 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 not weak_compilation_flags: + 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 + print(util.error('Python is not supported on Windows')) pass else: with open(output, 'w') as output_file, open(program, 'r') as program_file: @@ -106,14 +112,14 @@ def compile(program, output, compilers: Compilers = None, compile_log = None, we return True -def compile_file(file_path: str, name: str, compilers: Compilers, weak_compilation_flags = False, use_fsanitize = False) \ +def compile_file(file_path: str, name: str, compilers: Compilers, compilation_flags='default', use_fsanitize=False) \ -> Tuple[Union[str, None], str]: """ Compile a file :param file_path: Path to the file to compile :param name: Name of the executable :param compilers: Compilers object - :param weak_compilation_flags: Use weaker compilation flags + :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. :return: Tuple of (executable path or None if compilation failed, log path) """ @@ -134,7 +140,7 @@ def compile_file(file_path: str, name: str, compilers: Compilers, weak_compilati compile_log_path = paths.get_compilation_log_path(os.path.splitext(name)[0] + '.compile_log') with open(compile_log_path, 'w') as compile_log: try: - if compile(file_path, output, compilers, compile_log, weak_compilation_flags, extra_compilation_args, + if compile(file_path, output, compilers, compile_log, compilation_flags, extra_compilation_args, extra_compilation_files, use_fsanitize=use_fsanitize): return output, compile_log_path except CompilationError: diff --git a/src/sinol_make/helpers/parsers.py b/src/sinol_make/helpers/parsers.py index c2d7278f..83f1ae74 100644 --- a/src/sinol_make/helpers/parsers.py +++ b/src/sinol_make/helpers/parsers.py @@ -13,13 +13,19 @@ def add_compilation_arguments(parser: argparse.ArgumentParser): gcc_versions = 'gcc' gpp_versions = 'g++' - parser.add_argument('--c-compiler-path', dest='c_compiler_path', type=str, default=compiler.get_c_compiler_path(), - help=f'C compiler to use (default: {gcc_versions})') - parser.add_argument('--cpp-compiler-path', dest='cpp_compiler_path', type=str, default=compiler.get_cpp_compiler_path(), - help=f'C++ compiler to use (default: {gpp_versions})') - parser.add_argument('--python-interpreter-path', dest='python_interpreter_path', type=str, default=compiler.get_python_interpreter_path(), + parser.add_argument('--c-compiler-path', dest='c_compiler_path', type=str, + default=compiler.get_c_compiler_path(), help=f'C compiler to use (default: {gcc_versions})') + parser.add_argument('--cpp-compiler-path', dest='cpp_compiler_path', type=str, + default=compiler.get_cpp_compiler_path(), help=f'C++ compiler to use (default: {gpp_versions})') + parser.add_argument('--python-interpreter-path', dest='python_interpreter_path', type=str, + default=compiler.get_python_interpreter_path(), help='Python interpreter to use (default: python3)') - parser.add_argument('--java-compiler-path', dest='java_compiler_path', type=str, default=compiler.get_java_compiler_path(), - help='Java compiler to use (default: javac)') - parser.add_argument('-W', '--weak-compilation-flags', dest='weak_compilation_flags', action='store_true', - help='disable all warning flags during C and C++ compilation') + parser.add_argument('--java-compiler-path', dest='java_compiler_path', type=str, + default=compiler.get_java_compiler_path(), help='Java compiler to use (default: javac)') + parser.add_argument('--compile-mode', '-C', dest='compile_mode', choices=['default', 'oioioi', 'weak', 'd', 'o', 'w'], + help='Warning flag groups used to compile C/C++ files. Available options:\n' + ' default / d - uses default flags: \n' + ' (-Wshadow -Wconversion -Wno-unused-result -Wfloat-equal) + oioioi flags\n' + ' 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') diff --git a/tests/commands/export/test_unit.py b/tests/commands/export/test_unit.py index e79a1b2e..fc152378 100644 --- a/tests/commands/export/test_unit.py +++ b/tests/commands/export/test_unit.py @@ -12,7 +12,7 @@ def _create_package(tmpdir, path): os.chdir(package_path) command = get_command() util.create_ins_outs(package_path) - command.args = argparse.Namespace(cpus=1, weak_compilation_flags=False, + command.args = argparse.Namespace(cpus=1, compile_mode='default', cpp_compiler_path=compiler.get_cpp_compiler_path(), c_compiler_path=None, python_interpreter_path=None, java_compiler_path=None, export_ocen=False) diff --git a/tests/commands/export/util.py b/tests/commands/export/util.py index 9bf2f8bd..e4c374f5 100644 --- a/tests/commands/export/util.py +++ b/tests/commands/export/util.py @@ -10,7 +10,7 @@ def get_command(): command = Command() command.task_id = package_util.get_task_id() command.args = argparse.Namespace( - weak_compilation_flags=False, + compile_mode='default', c_compiler_path=compiler.get_c_compiler_path(), cpp_compiler_path=compiler.get_cpp_compiler_path(), python_interpreter_path=compiler.get_python_interpreter_path(), diff --git a/tests/commands/run/test_integration.py b/tests/commands/run/test_integration.py index 7b0876f0..c0c90d90 100644 --- a/tests/commands/run/test_integration.py +++ b/tests/commands/run/test_integration.py @@ -250,9 +250,28 @@ def test_flag_solutions_multiple(capsys, create_package, time_tool): @pytest.mark.parametrize("create_package", [get_weak_compilation_flags_package_path()], indirect=True) def test_weak_compilation_flags(create_package): """ - Test flag --weak-compilation-flags. + Test flag --compile-mode weak flag. + """ + parser = configure_parsers() + args = parser.parse_args(["run", "--time-tool", "time"]) + command = Command() + + with pytest.raises(SystemExit) as e: + command.run(args) + + assert e.type == SystemExit + assert e.value.code == 1 + + args = parser.parse_args(["run", "--compile-mode", "weak", "--time-tool", "time"]) + command = Command() + command.run(args) + + +@pytest.mark.parametrize("create_package", [get_oioioi_compilation_flags_package_path()], indirect=True) +def test_oioioi_compilation_flags(create_package): + """ + Test flag --compile-mode oioioi flag. """ - package_path = create_package parser = configure_parsers() args = parser.parse_args(["run", "--time-tool", "time"]) command = Command() @@ -263,7 +282,7 @@ def test_weak_compilation_flags(create_package): assert e.type == SystemExit assert e.value.code == 1 - args = parser.parse_args(["run", "--weak-compilation-flags", "--time-tool", "time"]) + args = parser.parse_args(["run", "--compile-mode", "oioioi", "--time-tool", "time"]) command = Command() command.run(args) diff --git a/tests/commands/run/test_unit.py b/tests/commands/run/test_unit.py index 140be030..6ca11b8e 100644 --- a/tests/commands/run/test_unit.py +++ b/tests/commands/run/test_unit.py @@ -46,7 +46,7 @@ def test_execution(create_package, time_tool): def test_run_solutions(create_package, time_tool): package_path = create_package command = get_command(package_path) - command.args = argparse.Namespace(solutions_report=False, time_tool=time_tool, weak_compilation_flags=False, + command.args = argparse.Namespace(solutions_report=False, time_tool=time_tool, compile_mode='default', hide_memory=False) create_ins_outs(package_path) command.tests = package_util.get_tests("abc", None) diff --git a/tests/commands/run/util.py b/tests/commands/run/util.py index ea66f0f8..26f9b18e 100644 --- a/tests/commands/run/util.py +++ b/tests/commands/run/util.py @@ -36,6 +36,6 @@ def get_command(path = None): def set_default_args(command): command.args = argparse.Namespace( - weak_compilation_flags=False, + compile_mode='default', print_expected_scores=True, ) diff --git a/tests/packages/oioioi_flags/config.yml b/tests/packages/oioioi_flags/config.yml new file mode 100644 index 00000000..df9ebc60 --- /dev/null +++ b/tests/packages/oioioi_flags/config.yml @@ -0,0 +1,11 @@ +title: Package for testing --compile-mode oioioi flag +memory_limit: 16000 +time_limit: 1000 +sinol_task_id: oif +scores: + 1: 100 +sinol_expected_scores: + oif.cpp: + expected: + 1: {points: 100, status: OK} + points: 100 diff --git a/tests/packages/oioioi_flags/in/.gitkeep b/tests/packages/oioioi_flags/in/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/packages/oioioi_flags/in/oif1a.in b/tests/packages/oioioi_flags/in/oif1a.in new file mode 100644 index 00000000..2fb73a07 --- /dev/null +++ b/tests/packages/oioioi_flags/in/oif1a.in @@ -0,0 +1 @@ +1 1 diff --git a/tests/packages/oioioi_flags/no-precompile b/tests/packages/oioioi_flags/no-precompile new file mode 100644 index 00000000..e69de29b diff --git a/tests/packages/oioioi_flags/out/.gitkeep b/tests/packages/oioioi_flags/out/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/packages/oioioi_flags/out/oif1a.out b/tests/packages/oioioi_flags/out/oif1a.out new file mode 100644 index 00000000..0cfbf088 --- /dev/null +++ b/tests/packages/oioioi_flags/out/oif1a.out @@ -0,0 +1 @@ +2 diff --git a/tests/packages/oioioi_flags/prog/oif.cpp b/tests/packages/oioioi_flags/prog/oif.cpp new file mode 100644 index 00000000..5c29e0d9 --- /dev/null +++ b/tests/packages/oioioi_flags/prog/oif.cpp @@ -0,0 +1,10 @@ +#include + +using namespace std; + +int main() { + long long a, b; + cin >> a >> b; + int c = a + b; // conversion from ‘long long int’ to ‘int’ + cout << c << endl; +} diff --git a/tests/packages/wcf/config.yml b/tests/packages/wcf/config.yml index 39945cf5..bb4ab87b 100644 --- a/tests/packages/wcf/config.yml +++ b/tests/packages/wcf/config.yml @@ -1,4 +1,4 @@ -title: Package for testing --weak-compilation-flags +title: Package for testing --compile-mode weak memory_limit: 16000 time_limit: 1000 scores: diff --git a/tests/util.py b/tests/util.py index 6778635c..c29d99c3 100644 --- a/tests/util.py +++ b/tests/util.py @@ -45,6 +45,13 @@ def get_weak_compilation_flags_package_path(): return os.path.join(os.path.dirname(__file__), "packages", "wcf") +def get_oioioi_compilation_flags_package_path(): + """ + Get path to package for testing oioioi compilation flags (/test/packages/oioioi_flags) + """ + return os.path.join(os.path.dirname(__file__), "packages", "oioioi_flags") + + def get_inwer_package_path(): """ Get path to package for inwer command (/test/packages/wer)