Skip to content

Commit

Permalink
Merge branch 'main' into doc-command
Browse files Browse the repository at this point in the history
  • Loading branch information
MasloMaslane authored Sep 19, 2023
2 parents 57fc5f6 + 8e35b27 commit cbf53ff
Show file tree
Hide file tree
Showing 35 changed files with 372 additions and 110 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ build
.idea
__pycache__
/tests/packages/**/cache
/tests/packages/**/.cache
src/sinol_make/data

# pytest-cov
Expand Down
6 changes: 6 additions & 0 deletions examples/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ extra_compilation_files: ['abclib.cpp', 'abclib.py']

### Keys used by sinol-make:

# This key represents the short name (consisting of 3 letters) of the task.
# The names of files in `prog/`, `doc/`, `in/` and `out/` directories have to start with this task id.
# This key is only used by `sinol-make`: running `sinol-make export` creates
# an archive with the proper name, which sio2 uses as the task id.
sinol_task_id: abc

# sinol-make can behave differently depending on the value of `sinol_contest_type` key.
# Mainly, it affects how points are calculated.
# If the key is not specified, then `default` is used.
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ install_requires =
PyYAML
dictdiffer
importlib-resources
psutil

[options.packages.find]
where = src
Expand Down
2 changes: 1 addition & 1 deletion src/sinol_make/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from sinol_make import util, oiejq

__version__ = "1.5.3"
__version__ = "1.5.6"

def configure_parsers():
parser = argparse.ArgumentParser(
Expand Down
11 changes: 6 additions & 5 deletions src/sinol_make/commands/export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import yaml

from sinol_make import util
from sinol_make.helpers import package_util, parsers
from sinol_make.helpers import package_util, parsers, paths
from sinol_make.commands.gen import gen_util
from sinol_make.interfaces.BaseCommand import BaseCommand

Expand Down Expand Up @@ -34,7 +34,7 @@ def get_generated_tests(self):
if not gen_util.ingen_exists(self.task_id):
return []

working_dir = os.path.join(os.getcwd(), 'cache', 'export', 'tests')
working_dir = paths.get_cache_path('export', 'tests')
if os.path.exists(working_dir):
shutil.rmtree(working_dir)
os.makedirs(working_dir)
Expand Down Expand Up @@ -100,8 +100,8 @@ def create_makefile_in(self, target_dir: str, config: dict):
:param config: Config dictionary.
"""
with open(os.path.join(target_dir, 'makefile.in'), 'w') as f:
cxx_flags = '-std=c++17'
c_flags = '-std=c17'
cxx_flags = '-std=c++20'
c_flags = '-std=gnu99'
def format_multiple_arguments(obj):
if isinstance(obj, str):
return obj
Expand Down Expand Up @@ -146,11 +146,12 @@ def run(self, args: argparse.Namespace):
with open(os.path.join(os.getcwd(), 'config.yml'), 'r') as config_file:
config = yaml.load(config_file, Loader=yaml.FullLoader)

export_package_path = os.path.join(os.getcwd(), 'cache', 'export', self.task_id)
export_package_path = paths.get_cache_path('export', self.task_id)
if os.path.exists(export_package_path):
shutil.rmtree(export_package_path)
os.makedirs(export_package_path)

util.change_stack_size_to_unlimited()
self.copy_package_required_files(export_package_path)
self.clear_files(export_package_path)
self.create_makefile_in(export_package_path, config)
Expand Down
1 change: 1 addition & 0 deletions src/sinol_make/commands/gen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def run(self, args: argparse.Namespace):
self.correct_solution = gen_util.get_correct_solution(self.task_id)
self.ingen_exe = gen_util.compile_ingen(self.ingen, self.args, self.args.weak_compilation_flags)

util.change_stack_size_to_unlimited()
if gen_util.run_ingen(self.ingen_exe):
print(util.info('Successfully generated input files.'))
else:
Expand Down
12 changes: 8 additions & 4 deletions src/sinol_make/commands/gen/gen_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,14 @@ def get_ingen(task_id=None, ingen_path=None):
util.exit_with_error(f'Ingen source file for task {task_id} does not exist.')

# Sio2 first chooses shell scripts, then non-shell source codes.
if os.path.splitext(ingen[0])[1] == '.sh' and len(ingen) > 1:
return ingen[1]
else:
return ingen[0]
correct_ingen = None
for i in ingen:
if os.path.splitext(i)[1] == '.sh':
correct_ingen = i
break
if correct_ingen is None:
correct_ingen = ingen[0]
return correct_ingen


def compile_ingen(ingen_path: str, args: argparse.Namespace, weak_compilation_flags=False):
Expand Down
5 changes: 3 additions & 2 deletions src/sinol_make/commands/inwer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from sinol_make import util
from sinol_make.commands.inwer.structs import TestResult, InwerExecution, VerificationResult, TableData
from sinol_make.helpers import package_util, compile, printer
from sinol_make.helpers import package_util, compile, printer, paths
from sinol_make.helpers.parsers import add_compilation_arguments
from sinol_make.interfaces.BaseCommand import BaseCommand
from sinol_make.commands.inwer import inwer_util
Expand Down Expand Up @@ -52,7 +52,7 @@ def verify_test(execution: InwerExecution) -> VerificationResult:
"""
Verifies a test and returns the result of inwer on this test.
"""
output_dir = os.path.join(os.getcwd(), 'cache', 'executions', execution.test_name)
output_dir = paths.get_executables_path(execution.test_name)
os.makedirs(output_dir, exist_ok=True)

command = [execution.inwer_exe_path]
Expand Down Expand Up @@ -140,6 +140,7 @@ def run(self, args: argparse.Namespace):
else:
print('Verifying tests: ' + util.bold(', '.join(self.tests)))

util.change_stack_size_to_unlimited()
self.compile_inwer(args)
results: Dict[str, TestResult] = self.verify_and_print_table()
print('')
Expand Down
13 changes: 8 additions & 5 deletions src/sinol_make/commands/inwer/inwer_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,15 @@ def print_line_separator():
else:
output.append("")

print(output[0].ljust(column_lengths[3]))
output.pop(0)
if output == []:
print(util.color_gray("No output"))
else:
print(output[0].ljust(column_lengths[3]))
output.pop(0)

for line in output:
print(" " * (column_lengths[0] + 2) + " | " + " " * (column_lengths[1] - 1) + " | " +
" " * (column_lengths[2] - 1) + " | " + line.ljust(column_lengths[3]))
for line in output:
print(" " * (column_lengths[0] + 2) + " | " + " " * (column_lengths[1] - 1) + " | " +
" " * (column_lengths[2] - 1) + " | " + line.ljust(column_lengths[3]))

print_line_separator()
print()
Expand Down
66 changes: 42 additions & 24 deletions src/sinol_make/commands/run/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import subprocess
import signal
import threading
from io import StringIO
import time
import psutil
import glob
from io import StringIO
from typing import Dict

from sinol_make import contest_types, oiejq
Expand All @@ -14,7 +16,7 @@
from sinol_make.helpers.parsers import add_compilation_arguments
from sinol_make.interfaces.BaseCommand import BaseCommand
from sinol_make.interfaces.Errors import CompilationError, CheckerOutputException, UnknownContestType
from sinol_make.helpers import compile, compiler, package_util, printer
from sinol_make.helpers import compile, compiler, package_util, printer, paths
from sinol_make.structs.status_structs import Status
import sinol_make.util as util
import yaml, os, collections, sys, re, math, dictdiffer
Expand Down Expand Up @@ -339,8 +341,8 @@ def get_groups(self, tests):


def compile_solutions(self, solutions):
os.makedirs(self.COMPILATION_DIR, exist_ok=True)
os.makedirs(self.EXECUTABLES_DIR, exist_ok=True)
os.makedirs(paths.get_compilation_log_path(), exist_ok=True)
os.makedirs(paths.get_executables_path(), exist_ok=True)
print("Compiling %d solutions..." % len(solutions))
args = [(solution, True) for solution in solutions]
with mp.Pool(self.cpus) as pool:
Expand All @@ -349,10 +351,9 @@ def compile_solutions(self, solutions):


def compile(self, solution, use_extras = False):
compile_log_file = os.path.join(
self.COMPILATION_DIR, "%s.compile_log" % package_util.get_file_name(solution))
compile_log_file = paths.get_compilation_log_path("%s.compile_log" % package_util.get_file_name(solution))
source_file = os.path.join(os.getcwd(), "prog", self.get_solution_from_exe(solution))
output = os.path.join(self.EXECUTABLES_DIR, package_util.get_executable(solution))
output = paths.get_executables_path(package_util.get_executable(solution))

extra_compilation_args = []
extra_compilation_files = []
Expand Down Expand Up @@ -504,7 +505,10 @@ def sigint_handler(signum, frame):

def execute_time(self, command, name, result_file_path, input_file_path, output_file_path, answer_file_path,
time_limit, memory_limit, hard_time_limit):

executable = package_util.get_executable(name)
timeout = False
mem_limit_exceeded = False
with open(input_file_path, "r") as input_file:
process = subprocess.Popen(command, stdin=input_file, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
preexec_fn=os.setsid)
Expand All @@ -517,11 +521,27 @@ def sigint_handler(signum, frame):
sys.exit(1)
signal.signal(signal.SIGINT, sigint_handler)

try:
output, _ = process.communicate(timeout=hard_time_limit)
except subprocess.TimeoutExpired:
timeout = True
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
start_time = time.time()
while process.poll() is None:
try:
time_process = psutil.Process(process.pid)
executable_process = None
for child in time_process.children():
if child.name() == executable:
executable_process = child
break
if executable_process is not None and executable_process.memory_info().rss > memory_limit * 1024:
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
mem_limit_exceeded = True
break
except psutil.NoSuchProcess:
pass

if time.time() - start_time > hard_time_limit:
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
timeout = True
break
output, _ = process.communicate()

result = ExecutionResult()
program_exit_code = None
Expand All @@ -547,7 +567,7 @@ def sigint_handler(signum, frame):
Command terminated by signal 11
"""
program_exit_code = int(lines[0].strip().split(" ")[-1])
else:
elif not mem_limit_exceeded:
result.Status = Status.RE
result.Error = "Unexpected output from time command: " + "\n".join(lines)
return result
Expand All @@ -556,6 +576,9 @@ def sigint_handler(signum, frame):
result.Status = Status.RE
elif timeout:
result.Status = Status.TL
elif mem_limit_exceeded:
result.Memory = memory_limit + 1 # Add one so that the memory is red in the table
result.Status = Status.ML
elif result.Time > time_limit:
result.Status = Status.TL
elif result.Memory > memory_limit:
Expand All @@ -581,7 +604,7 @@ def run_solution(self, data_for_execution: ExecutionData):
"""

(name, executable, test, time_limit, memory_limit, timetool_path) = data_for_execution
file_no_ext = os.path.join(self.EXECUTIONS_DIR, name, package_util.extract_test_id(test))
file_no_ext = paths.get_executions_path(name, package_util.extract_test_id(test))
output_file = file_no_ext + ".out"
result_file = file_no_ext + ".res"
hard_time_limit_in_s = math.ceil(2 * time_limit / 1000.0)
Expand Down Expand Up @@ -620,7 +643,7 @@ def run_solutions(self, compiled_commands, names, solutions):
executions.append((name, executable, test, package_util.get_time_limit(test, self.config, lang, self.args),
package_util.get_memory_limit(test, self.config, lang, self.args), self.timetool_path))
all_results[name][self.get_group(test)][test] = ExecutionResult(Status.PENDING)
os.makedirs(os.path.join(self.EXECUTIONS_DIR, name), exist_ok=True)
os.makedirs(paths.get_executions_path(name), exist_ok=True)
else:
for test in self.tests:
all_results[name][self.get_group(test)][test] = ExecutionResult(Status.CE)
Expand Down Expand Up @@ -685,9 +708,8 @@ def compile_and_run(self, solutions):
for i in range(len(solutions)):
if not compilation_results[i]:
self.failed_compilations.append(solutions[i])
os.makedirs(self.EXECUTIONS_DIR, exist_ok=True)
executables = [os.path.join(self.EXECUTABLES_DIR, package_util.get_executable(solution))
for solution in solutions]
os.makedirs(paths.get_executions_path(), exist_ok=True)
executables = [paths.get_executables_path(package_util.get_executable(solution)) for solution in solutions]
compiled_commands = zip(solutions, executables, compilation_results)
names = solutions
return self.run_solutions(compiled_commands, names, solutions)
Expand Down Expand Up @@ -966,12 +988,7 @@ def set_group_result(solution, group, result):

def set_constants(self):
self.ID = package_util.get_task_id()
self.TMP_DIR = os.path.join(os.getcwd(), "cache")
self.COMPILATION_DIR = os.path.join(self.TMP_DIR, "compilation")
self.EXECUTIONS_DIR = os.path.join(self.TMP_DIR, "executions")
self.EXECUTABLES_DIR = os.path.join(self.TMP_DIR, "executables")
self.SOURCE_EXTENSIONS = ['.c', '.cpp', '.py', '.java']
self.PROGRAMS_IN_ROW = 8
self.SOLUTIONS_RE = re.compile(r"^%s[bs]?[0-9]*\.(cpp|cc|java|py|pas)$" % self.ID)


Expand Down Expand Up @@ -1138,7 +1155,7 @@ def run(self, args):
print(util.info("Checker found: %s" % os.path.basename(checker[0])))
self.checker = checker[0]
checker_basename = os.path.basename(self.checker)
self.checker_executable = os.path.join(self.EXECUTABLES_DIR, checker_basename + ".e")
self.checker_executable = paths.get_executables_path(checker_basename + ".e")

checker_compilation = self.compile_solutions([self.checker])
if not checker_compilation[0]:
Expand All @@ -1154,6 +1171,7 @@ def run(self, args):
self.failed_compilations = []
solutions = self.get_solutions(self.args.solutions)

util.change_stack_size_to_unlimited()
for solution in solutions:
lang = package_util.get_file_lang(solution)
for test in self.tests:
Expand Down
Loading

0 comments on commit cbf53ff

Please sign in to comment.