sinol-make 1.6.0.dev3__tar.gz → 1.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sinol_make-1.6.0.dev3/src/sinol_make.egg-info → sinol_make-1.7.0}/PKG-INFO +31 -2
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/README.md +30 -1
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/setup.cfg +1 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/__init__.py +24 -12
- sinol_make-1.7.0/src/sinol_make/commands/chkwer/__init__.py +137 -0
- sinol_make-1.7.0/src/sinol_make/commands/chkwer/chkwer_util.py +72 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/doc/__init__.py +6 -3
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/export/__init__.py +7 -5
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/gen/__init__.py +11 -8
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/ingen/__init__.py +28 -14
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/ingen/ingen_util.py +1 -1
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/inwer/__init__.py +12 -22
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/inwer/inwer_util.py +5 -1
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/outgen/__init__.py +6 -10
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/run/__init__.py +103 -351
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/verify/__init__.py +17 -4
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/contest_types/__init__.py +5 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/contest_types/default.py +24 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/contest_types/icpc.py +9 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/contest_types/oi.py +10 -1
- sinol_make-1.7.0/src/sinol_make/contest_types/oij.py +35 -0
- sinol_make-1.7.0/src/sinol_make/executors/__init__.py +65 -0
- sinol_make-1.7.0/src/sinol_make/executors/detailed.py +91 -0
- sinol_make-1.7.0/src/sinol_make/executors/oiejq.py +85 -0
- sinol_make-1.7.0/src/sinol_make/executors/time.py +113 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/cache.py +3 -3
- sinol_make-1.7.0/src/sinol_make/helpers/classinit.py +90 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/compile.py +12 -7
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/compiler.py +11 -6
- sinol_make-1.7.0/src/sinol_make/helpers/func_cache.py +30 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/package_util.py +54 -11
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/parsers.py +17 -3
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/paths.py +8 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/interfaces/BaseCommand.py +7 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/interfaces/Errors.py +1 -1
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/oiejq/__init__.py +20 -8
- sinol_make-1.7.0/src/sinol_make/structs/chkwer_structs.py +68 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/status_structs.py +27 -6
- sinol_make-1.7.0/src/sinol_make/task_type/__init__.py +179 -0
- sinol_make-1.7.0/src/sinol_make/task_type/interactive.py +238 -0
- sinol_make-1.7.0/src/sinol_make/task_type/normal.py +37 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/util.py +4 -8
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0/src/sinol_make.egg-info}/PKG-INFO +31 -2
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/SOURCES.txt +13 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/entry_points.txt +1 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/tests/test_oiejq.py +41 -4
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/tests/test_util.py +8 -5
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/LICENSE +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/pyproject.toml +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/init/__init__.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/outgen/outgen_util.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/__init__.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/printer.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/interfaces/__init__.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/oiejq/perf_test.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/__init__.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/cache_structs.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/compiler_structs.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/gen_structs.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/inwer_structs.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/run_structs.py +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/dependency_links.txt +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/requires.txt +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/top_level.txt +0 -0
- {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/tests/test_multiple_arguments.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sinol_make
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: CLI tool for creating sio2 task packages
|
|
5
5
|
Author: Mateusz Masiarz
|
|
6
6
|
Author-email: m.masiarz@fri.edu.pl
|
|
@@ -45,6 +45,7 @@ with features such as:
|
|
|
45
45
|
- [Why?](#why)
|
|
46
46
|
- [Installation](#installation)
|
|
47
47
|
- [Usage](#usage)
|
|
48
|
+
- [Contest types](#contest-types)
|
|
48
49
|
- [Reporting bugs and contributing code](#reporting-bugs-and-contributing-code)
|
|
49
50
|
|
|
50
51
|
### Why?
|
|
@@ -93,7 +94,7 @@ activate-global-python-argcomplete
|
|
|
93
94
|
|
|
94
95
|
### Usage
|
|
95
96
|
|
|
96
|
-
The
|
|
97
|
+
The available commands (see `sinol-make --help`) are:
|
|
97
98
|
|
|
98
99
|
- `sinol-make run` -- Runs selected solutions (by default all solutions) on selected tests (by default all tests) with a given number
|
|
99
100
|
of CPUs. Measures the solutions' time with oiejq, unless specified otherwise. After running the solutions, it
|
|
@@ -113,6 +114,9 @@ Run `sinol-make ingen --help` to see available flags.
|
|
|
113
114
|
program to use, what tests to check and how many CPUs to use. Run `sinol-make inwer --help` to see available flags.
|
|
114
115
|
- `sinol-make export` -- Creates archive ready to upload to sio2 or szkopul. Run `sinol-make export --help` to see all available flags.
|
|
115
116
|
- `sinol-make doc` -- Compiles all LaTeX files in doc/ directory to PDF. Run `sinol-make doc --help` to see all available flags.
|
|
117
|
+
- `sinol-make verify` -- Verifies the package. This command runs stress tests (if available), verifies the config,
|
|
118
|
+
generates tests, generates problem statements, runs inwer and run all solutions. Ingen and inwer are compiled with
|
|
119
|
+
address and UB sanitizers. Run `sinol-make verify --help` to see all available flags.
|
|
116
120
|
- `sinol-make init [id]` -- Creates package from template [on github](https://github.com/sio2project/sinol-make/tree/main/example_package) and sets task id to provided `[id]`. Requires an internet connection to run.
|
|
117
121
|
|
|
118
122
|
You can also run multiple commands at once, for example:
|
|
@@ -121,6 +125,31 @@ You can also run multiple commands at once, for example:
|
|
|
121
125
|
sinol-make gen prog/abcingen2.cpp inwer --cpus 4 run --tests abc1*.in doc export --no-statement
|
|
122
126
|
```
|
|
123
127
|
|
|
128
|
+
There are also available short aliases for the commands:
|
|
129
|
+
- `sinol-make r` for `sinol-make run`
|
|
130
|
+
- `sinol-make g` for `sinol-make gen`
|
|
131
|
+
- `sinol-make i` for `sinol-make inwer`
|
|
132
|
+
- `sinol-make e` for `sinol-make export`
|
|
133
|
+
- `sinol-make d` for `sinol-make doc`
|
|
134
|
+
- `sinol-make v` for `sinol-make verify`
|
|
135
|
+
- `sm` for `sinol-make`
|
|
136
|
+
|
|
137
|
+
### Contest types
|
|
138
|
+
|
|
139
|
+
`sinol-make` changes its behavior depending on the contest type specified in `config.yml`. You can specify
|
|
140
|
+
the contest type with the `sinol_contest_type` key in config. Here is the table of available contest types and their
|
|
141
|
+
features:
|
|
142
|
+
|
|
143
|
+
| Feature | `default` | `oi` | `oij` | `icpc` |
|
|
144
|
+
|-----------------------------------------------------------------------------------------------------|-----------|-------|-------|--------|
|
|
145
|
+
| Max score | 100 | 100 | 100 | 1 |
|
|
146
|
+
| Default time tool | oiejq | oiejq | oiejq | time |
|
|
147
|
+
| Full score if took less than half of the time limit, <br/>otherwise linearly decreasing to 1. | ❌ | ✔️ | ❌ | ❌ |
|
|
148
|
+
| Full score if took less than the time limit | ✔️ | ❌ | ✔️ | ✔️ |
|
|
149
|
+
| Scores must sum up to 100 | ❌ | ✔️ | ✔️ | ❌ |
|
|
150
|
+
| Limits can be set for individual tests | ✔️ | ❌ | ✔️ | ✔️ |
|
|
151
|
+
| Verifies if tests are named correctly<br/> (letters within groups increase, group numbers increase) | ❌ | ✔️ | ✔️ | ✔️ |
|
|
152
|
+
|
|
124
153
|
### Reporting bugs and contributing code
|
|
125
154
|
|
|
126
155
|
- Want to report a bug or request a feature? [Open an issue](https://github.com/sio2project/sinol-make/issues).
|
|
@@ -15,6 +15,7 @@ with features such as:
|
|
|
15
15
|
- [Why?](#why)
|
|
16
16
|
- [Installation](#installation)
|
|
17
17
|
- [Usage](#usage)
|
|
18
|
+
- [Contest types](#contest-types)
|
|
18
19
|
- [Reporting bugs and contributing code](#reporting-bugs-and-contributing-code)
|
|
19
20
|
|
|
20
21
|
### Why?
|
|
@@ -63,7 +64,7 @@ activate-global-python-argcomplete
|
|
|
63
64
|
|
|
64
65
|
### Usage
|
|
65
66
|
|
|
66
|
-
The
|
|
67
|
+
The available commands (see `sinol-make --help`) are:
|
|
67
68
|
|
|
68
69
|
- `sinol-make run` -- Runs selected solutions (by default all solutions) on selected tests (by default all tests) with a given number
|
|
69
70
|
of CPUs. Measures the solutions' time with oiejq, unless specified otherwise. After running the solutions, it
|
|
@@ -83,6 +84,9 @@ Run `sinol-make ingen --help` to see available flags.
|
|
|
83
84
|
program to use, what tests to check and how many CPUs to use. Run `sinol-make inwer --help` to see available flags.
|
|
84
85
|
- `sinol-make export` -- Creates archive ready to upload to sio2 or szkopul. Run `sinol-make export --help` to see all available flags.
|
|
85
86
|
- `sinol-make doc` -- Compiles all LaTeX files in doc/ directory to PDF. Run `sinol-make doc --help` to see all available flags.
|
|
87
|
+
- `sinol-make verify` -- Verifies the package. This command runs stress tests (if available), verifies the config,
|
|
88
|
+
generates tests, generates problem statements, runs inwer and run all solutions. Ingen and inwer are compiled with
|
|
89
|
+
address and UB sanitizers. Run `sinol-make verify --help` to see all available flags.
|
|
86
90
|
- `sinol-make init [id]` -- Creates package from template [on github](https://github.com/sio2project/sinol-make/tree/main/example_package) and sets task id to provided `[id]`. Requires an internet connection to run.
|
|
87
91
|
|
|
88
92
|
You can also run multiple commands at once, for example:
|
|
@@ -91,6 +95,31 @@ You can also run multiple commands at once, for example:
|
|
|
91
95
|
sinol-make gen prog/abcingen2.cpp inwer --cpus 4 run --tests abc1*.in doc export --no-statement
|
|
92
96
|
```
|
|
93
97
|
|
|
98
|
+
There are also available short aliases for the commands:
|
|
99
|
+
- `sinol-make r` for `sinol-make run`
|
|
100
|
+
- `sinol-make g` for `sinol-make gen`
|
|
101
|
+
- `sinol-make i` for `sinol-make inwer`
|
|
102
|
+
- `sinol-make e` for `sinol-make export`
|
|
103
|
+
- `sinol-make d` for `sinol-make doc`
|
|
104
|
+
- `sinol-make v` for `sinol-make verify`
|
|
105
|
+
- `sm` for `sinol-make`
|
|
106
|
+
|
|
107
|
+
### Contest types
|
|
108
|
+
|
|
109
|
+
`sinol-make` changes its behavior depending on the contest type specified in `config.yml`. You can specify
|
|
110
|
+
the contest type with the `sinol_contest_type` key in config. Here is the table of available contest types and their
|
|
111
|
+
features:
|
|
112
|
+
|
|
113
|
+
| Feature | `default` | `oi` | `oij` | `icpc` |
|
|
114
|
+
|-----------------------------------------------------------------------------------------------------|-----------|-------|-------|--------|
|
|
115
|
+
| Max score | 100 | 100 | 100 | 1 |
|
|
116
|
+
| Default time tool | oiejq | oiejq | oiejq | time |
|
|
117
|
+
| Full score if took less than half of the time limit, <br/>otherwise linearly decreasing to 1. | ❌ | ✔️ | ❌ | ❌ |
|
|
118
|
+
| Full score if took less than the time limit | ✔️ | ❌ | ✔️ | ✔️ |
|
|
119
|
+
| Scores must sum up to 100 | ❌ | ✔️ | ✔️ | ❌ |
|
|
120
|
+
| Limits can be set for individual tests | ✔️ | ❌ | ✔️ | ✔️ |
|
|
121
|
+
| Verifies if tests are named correctly<br/> (letters within groups increase, group numbers increase) | ❌ | ✔️ | ✔️ | ✔️ |
|
|
122
|
+
|
|
94
123
|
### Reporting bugs and contributing code
|
|
95
124
|
|
|
96
125
|
- Want to report a bug or request a feature? [Open an issue](https://github.com/sio2project/sinol-make/issues).
|
|
@@ -6,7 +6,12 @@ import argcomplete
|
|
|
6
6
|
|
|
7
7
|
from sinol_make import util, oiejq
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# Required for side effects
|
|
10
|
+
from sinol_make.task_type.normal import NormalTaskType # noqa
|
|
11
|
+
from sinol_make.task_type.interactive import InteractiveTaskType # noqa
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__version__ = "1.7.0"
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
def configure_parsers():
|
|
@@ -33,7 +38,7 @@ def configure_parsers():
|
|
|
33
38
|
|
|
34
39
|
def check_oiejq():
|
|
35
40
|
if util.is_linux() and not oiejq.check_oiejq():
|
|
36
|
-
print(util.warning('`oiejq` in `~/.local/bin/` not found, installing
|
|
41
|
+
print(util.warning('Up to date `oiejq` in `~/.local/bin/` not found, installing new version...'))
|
|
37
42
|
try:
|
|
38
43
|
if oiejq.install_oiejq():
|
|
39
44
|
print(util.info('`oiejq` was successfully installed.'))
|
|
@@ -50,28 +55,35 @@ def main_exn():
|
|
|
50
55
|
parser = configure_parsers()
|
|
51
56
|
arguments = []
|
|
52
57
|
curr_args = []
|
|
58
|
+
commands = util.get_commands()
|
|
59
|
+
commands_dict = {command.get_name(): command for command in commands}
|
|
60
|
+
commands_short_dict = {command.get_short_name(): command for command in commands if command.get_short_name()}
|
|
53
61
|
for arg in sys.argv[1:]:
|
|
54
|
-
if arg in
|
|
62
|
+
if arg in commands_dict.keys() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
|
|
55
63
|
if curr_args:
|
|
56
64
|
arguments.append(curr_args)
|
|
57
65
|
curr_args = [arg]
|
|
66
|
+
elif arg in commands_short_dict.keys() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
|
|
67
|
+
if curr_args:
|
|
68
|
+
arguments.append(curr_args)
|
|
69
|
+
curr_args = [commands_short_dict[arg].get_name()]
|
|
58
70
|
else:
|
|
59
71
|
curr_args.append(arg)
|
|
60
72
|
if curr_args:
|
|
61
73
|
arguments.append(curr_args)
|
|
62
|
-
|
|
74
|
+
if not arguments:
|
|
75
|
+
parser.print_help()
|
|
76
|
+
exit(1)
|
|
63
77
|
check_oiejq()
|
|
64
78
|
|
|
65
79
|
for curr_args in arguments:
|
|
66
80
|
args = parser.parse_args(curr_args)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
command.run(args)
|
|
74
|
-
if not command_found:
|
|
81
|
+
command = commands_dict.get(args.command, None)
|
|
82
|
+
if command:
|
|
83
|
+
if len(arguments) > 1:
|
|
84
|
+
print(f' {command.get_name()} command '.center(util.get_terminal_size()[1], '='))
|
|
85
|
+
command.run(args)
|
|
86
|
+
else:
|
|
75
87
|
parser.print_help()
|
|
76
88
|
exit(1)
|
|
77
89
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Dict, List
|
|
5
|
+
import multiprocessing as mp
|
|
6
|
+
|
|
7
|
+
from sinol_make import util, contest_types
|
|
8
|
+
from sinol_make.commands.chkwer import chkwer_util
|
|
9
|
+
from sinol_make.commands.outgen import outgen_util
|
|
10
|
+
from sinol_make.helpers import package_util, parsers, compiler, compile, printer, paths
|
|
11
|
+
from sinol_make.interfaces.BaseCommand import BaseCommand
|
|
12
|
+
from sinol_make.structs.chkwer_structs import TestResult, ChkwerExecution, TableData, RunResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Command(BaseCommand):
|
|
16
|
+
"""
|
|
17
|
+
Class for `chkwer` command.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def get_name(self):
|
|
21
|
+
return "chkwer"
|
|
22
|
+
|
|
23
|
+
def get_short_name(self):
|
|
24
|
+
return "c"
|
|
25
|
+
|
|
26
|
+
def configure_subparser(self, subparser):
|
|
27
|
+
parser = subparser.add_parser(
|
|
28
|
+
self.get_name(),
|
|
29
|
+
help='Run checker with model solution and print results',
|
|
30
|
+
description='Run checker with model solution and print results. '
|
|
31
|
+
'Prints a table with points and checker\'s comments. '
|
|
32
|
+
'This command fails if the model solution didn\'t '
|
|
33
|
+
'score maximum points.'
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument('-t', '--tests', type=str, nargs='+',
|
|
36
|
+
help='test to run, for example in/abc{0,1}*')
|
|
37
|
+
parsers.add_cpus_argument(parser, 'number of cpus to use when verifying tests')
|
|
38
|
+
parsers.add_compilation_arguments(parser)
|
|
39
|
+
return parser
|
|
40
|
+
|
|
41
|
+
def compile(self, file_path, exe_path, args, name, compilation_flags):
|
|
42
|
+
print(f'Compiling {name}... ', end='')
|
|
43
|
+
compilers = compiler.verify_compilers(args, [file_path])
|
|
44
|
+
exe, compile_log_path = compile.compile_file(file_path, exe_path, compilers, compilation_flags,
|
|
45
|
+
use_fsanitize=False, use_extras=False)
|
|
46
|
+
if exe is None:
|
|
47
|
+
print(util.error('ERROR'))
|
|
48
|
+
compile.print_compile_log(compile_log_path)
|
|
49
|
+
util.exit_with_error(f'Failed {name} compilation.')
|
|
50
|
+
else:
|
|
51
|
+
print(util.info('OK'))
|
|
52
|
+
return exe
|
|
53
|
+
|
|
54
|
+
def run_test(self, execution: ChkwerExecution) -> RunResult:
|
|
55
|
+
"""
|
|
56
|
+
Verifies a test and returns the result of chkwer on this test.
|
|
57
|
+
"""
|
|
58
|
+
output_file = paths.get_chkwer_path(os.path.basename(execution.out_test_path))
|
|
59
|
+
with open(execution.in_test_path, 'r') as inf, open(output_file, 'w') as outf:
|
|
60
|
+
process = subprocess.Popen([execution.model_exe], stdin=inf, stdout=outf)
|
|
61
|
+
process.wait()
|
|
62
|
+
ok, points, comment = self.task_type.check_output(execution.in_test_path, output_file, execution.out_test_path)
|
|
63
|
+
|
|
64
|
+
return RunResult(execution.in_test_path, ok, int(points), comment)
|
|
65
|
+
|
|
66
|
+
def run_and_print_table(self) -> Dict[str, TestResult]:
|
|
67
|
+
os.makedirs(paths.get_chkwer_path(), exist_ok=True)
|
|
68
|
+
results = {}
|
|
69
|
+
sorted_tests = sorted(self.tests, key=lambda test: package_util.get_group(test, self.task_id))
|
|
70
|
+
executions: List[ChkwerExecution] = []
|
|
71
|
+
for test in sorted_tests:
|
|
72
|
+
results[test] = TestResult(test, self.task_id)
|
|
73
|
+
executions.append(ChkwerExecution(test, results[test].test_name, package_util.get_out_from_in(test),
|
|
74
|
+
self.checker_executable, self.model_executable))
|
|
75
|
+
|
|
76
|
+
has_terminal, terminal_width, terminal_height = util.get_terminal_size()
|
|
77
|
+
table_data = TableData(results, 0, self.task_id, self.contest_type.max_score_per_test())
|
|
78
|
+
if has_terminal:
|
|
79
|
+
run_event = threading.Event()
|
|
80
|
+
run_event.set()
|
|
81
|
+
thr = threading.Thread(target=printer.printer_thread, args=(run_event, chkwer_util.print_view, table_data))
|
|
82
|
+
thr.start()
|
|
83
|
+
|
|
84
|
+
keyboard_interrupt = False
|
|
85
|
+
try:
|
|
86
|
+
with mp.Pool(self.cpus) as pool:
|
|
87
|
+
for i, result in enumerate(pool.imap(self.run_test, executions)):
|
|
88
|
+
table_data.results[result.test_path].set_results(result.points, result.ok, result.comment)
|
|
89
|
+
table_data.i = i
|
|
90
|
+
except KeyboardInterrupt:
|
|
91
|
+
keyboard_interrupt = True
|
|
92
|
+
|
|
93
|
+
if has_terminal:
|
|
94
|
+
run_event.clear()
|
|
95
|
+
thr.join()
|
|
96
|
+
|
|
97
|
+
print("\n".join(chkwer_util.print_view(terminal_width, terminal_height, table_data)[0]))
|
|
98
|
+
if keyboard_interrupt:
|
|
99
|
+
util.exit_with_error("Keyboard interrupt.")
|
|
100
|
+
return results
|
|
101
|
+
|
|
102
|
+
def run(self, args):
|
|
103
|
+
args = util.init_package_command(args)
|
|
104
|
+
self.task_id = package_util.get_task_id()
|
|
105
|
+
self.task_type = package_util.get_task_type("time", None)
|
|
106
|
+
self.contest_type = contest_types.get_contest_type()
|
|
107
|
+
|
|
108
|
+
if self.task_type.name() != "normal":
|
|
109
|
+
util.exit_with_error("chkwer can be run only for normal tasks.")
|
|
110
|
+
|
|
111
|
+
self.cpus = args.cpus or util.default_cpu_count()
|
|
112
|
+
self.tests = package_util.get_tests(self.task_id, args.tests)
|
|
113
|
+
|
|
114
|
+
if len(self.tests) == 0:
|
|
115
|
+
util.exit_with_error("No tests found.")
|
|
116
|
+
else:
|
|
117
|
+
print('Will run on tests: ' + util.bold(', '.join(self.tests)))
|
|
118
|
+
util.change_stack_size_to_unlimited()
|
|
119
|
+
|
|
120
|
+
additional_files = self.task_type.additional_files_to_compile()
|
|
121
|
+
if len(additional_files) == 0:
|
|
122
|
+
util.exit_with_error("Checker not found.")
|
|
123
|
+
if len(additional_files) != 1:
|
|
124
|
+
util.exit_with_error("More than one file to compile found. How is that possible?")
|
|
125
|
+
checker_info = additional_files[0]
|
|
126
|
+
model_solution = outgen_util.get_correct_solution(self.task_id)
|
|
127
|
+
self.checker_executable = self.compile(checker_info[0], checker_info[1], args, "checker",
|
|
128
|
+
args.compile_mode)
|
|
129
|
+
self.model_executable = self.compile(model_solution, package_util.get_executable(model_solution), args,
|
|
130
|
+
"model solution", args.compile_mode)
|
|
131
|
+
print()
|
|
132
|
+
|
|
133
|
+
results = self.run_and_print_table()
|
|
134
|
+
for result in results.values():
|
|
135
|
+
if not result.ok or result.points != self.contest_type.max_score_per_test():
|
|
136
|
+
util.exit_with_error("Model solution didn't score maximum points.")
|
|
137
|
+
print(util.info("Checker verification successful."))
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from io import StringIO
|
|
3
|
+
|
|
4
|
+
from sinol_make import util
|
|
5
|
+
from sinol_make.commands.inwer.inwer_util import sort_tests
|
|
6
|
+
from sinol_make.structs.chkwer_structs import TableData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def print_view(term_width, term_height, table_data: TableData):
|
|
10
|
+
"""
|
|
11
|
+
Prints current results of test verification.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
previous_stdout = sys.stdout
|
|
15
|
+
new_stdout = StringIO()
|
|
16
|
+
sys.stdout = new_stdout
|
|
17
|
+
|
|
18
|
+
results = table_data.results
|
|
19
|
+
column_lengths = [0, len('Points') + 1, 0]
|
|
20
|
+
tests = []
|
|
21
|
+
for result in results.values():
|
|
22
|
+
column_lengths[0] = max(column_lengths[0], len(result.test_name))
|
|
23
|
+
tests.append(result.test_path)
|
|
24
|
+
tests = sort_tests(tests, table_data.task_id)
|
|
25
|
+
|
|
26
|
+
# 6 is for " | " between columns, 3 for margin.
|
|
27
|
+
column_lengths[2] = max(10, term_width - column_lengths[0] - column_lengths[1] - 6 - 3)
|
|
28
|
+
margin = " "
|
|
29
|
+
|
|
30
|
+
def print_line_separator():
|
|
31
|
+
res = "-" * (column_lengths[0] + 3) + "+" + "-" * (column_lengths[1] + 1) + "+"
|
|
32
|
+
res += "-" * (term_width - len(res) - 1)
|
|
33
|
+
print(res)
|
|
34
|
+
|
|
35
|
+
print_line_separator()
|
|
36
|
+
|
|
37
|
+
print(margin + "Test".ljust(column_lengths[0]) + " | " + "Points" + " | " + "Comment")
|
|
38
|
+
print_line_separator()
|
|
39
|
+
|
|
40
|
+
last_group = None
|
|
41
|
+
for test_path in tests:
|
|
42
|
+
result = results[test_path]
|
|
43
|
+
if last_group is not None and last_group != result.test_group:
|
|
44
|
+
print_line_separator()
|
|
45
|
+
last_group = result.test_group
|
|
46
|
+
print(margin + result.test_name.ljust(column_lengths[0]) + " | ", end='')
|
|
47
|
+
|
|
48
|
+
if result.run:
|
|
49
|
+
if result.ok:
|
|
50
|
+
if result.points == table_data.max_score:
|
|
51
|
+
print(util.info(str(result.points).ljust(column_lengths[1] - 1)), end='')
|
|
52
|
+
else:
|
|
53
|
+
print(util.warning(str(result.points).ljust(column_lengths[1] - 1)), end='')
|
|
54
|
+
else:
|
|
55
|
+
print(util.error(str(result.points).ljust(column_lengths[1] - 1)), end='')
|
|
56
|
+
else:
|
|
57
|
+
print(util.warning("...".ljust(column_lengths[1] - 1)), end='')
|
|
58
|
+
print(" | ", end='')
|
|
59
|
+
|
|
60
|
+
output = []
|
|
61
|
+
if result.run:
|
|
62
|
+
if result.comment:
|
|
63
|
+
print(result.comment)
|
|
64
|
+
else:
|
|
65
|
+
print(util.color_gray("No comment"))
|
|
66
|
+
|
|
67
|
+
print_line_separator()
|
|
68
|
+
print()
|
|
69
|
+
print()
|
|
70
|
+
|
|
71
|
+
sys.stdout = previous_stdout
|
|
72
|
+
return new_stdout.getvalue().splitlines(), None, "Use arrows to move."
|
|
@@ -17,8 +17,11 @@ class Command(BaseCommand):
|
|
|
17
17
|
def get_name(self):
|
|
18
18
|
return "doc"
|
|
19
19
|
|
|
20
|
+
def get_short_name(self):
|
|
21
|
+
return "d"
|
|
22
|
+
|
|
20
23
|
def compile_file_latex_div(self, file_path):
|
|
21
|
-
print(
|
|
24
|
+
print(f'Compiling {os.path.basename(file_path)} (latex to dvi)...')
|
|
22
25
|
os.chdir(os.path.dirname(file_path))
|
|
23
26
|
subprocess.run(['latex', file_path])
|
|
24
27
|
dvi_file = os.path.splitext(file_path)[0] + '.dvi'
|
|
@@ -35,7 +38,7 @@ class Command(BaseCommand):
|
|
|
35
38
|
return True
|
|
36
39
|
|
|
37
40
|
def compile_pdf_latex(self, file_path):
|
|
38
|
-
print(
|
|
41
|
+
print(f'Compiling {os.path.basename(file_path)} (pdflatex)...')
|
|
39
42
|
os.chdir(os.path.dirname(file_path))
|
|
40
43
|
subprocess.run(['pdflatex', file_path])
|
|
41
44
|
pdf_file = os.path.splitext(file_path)[0] + '.pdf'
|
|
@@ -62,7 +65,7 @@ class Command(BaseCommand):
|
|
|
62
65
|
for pattern in self.LOG_PATTERNS:
|
|
63
66
|
for file in glob.glob(os.path.join(os.getcwd(), 'doc', pattern)):
|
|
64
67
|
os.rename(file, os.path.join(output_dir, os.path.basename(file)))
|
|
65
|
-
print(
|
|
68
|
+
print(f'Compilation log files can be found in {os.path.relpath(output_dir, os.getcwd())}')
|
|
66
69
|
|
|
67
70
|
def configure_subparser(self, subparser: argparse.ArgumentParser):
|
|
68
71
|
parser = subparser.add_parser(
|
|
@@ -23,15 +23,15 @@ class Command(BaseCommand):
|
|
|
23
23
|
def get_name(self):
|
|
24
24
|
return "export"
|
|
25
25
|
|
|
26
|
+
def get_short_name(self):
|
|
27
|
+
return "e"
|
|
28
|
+
|
|
26
29
|
def configure_subparser(self, subparser: argparse.ArgumentParser):
|
|
27
30
|
parser = subparser.add_parser(
|
|
28
31
|
self.get_name(),
|
|
29
32
|
help='Create archive for oioioi upload',
|
|
30
33
|
description='Creates archive in the current directory ready to upload to sio2 or szkopul.')
|
|
31
|
-
|
|
32
|
-
help=f'number of cpus to use to generate output files '
|
|
33
|
-
f'(default: {util.default_cpu_count()})',
|
|
34
|
-
default=util.default_cpu_count())
|
|
34
|
+
parsers.add_cpus_argument(parser, 'number of cpus to use to generate output files')
|
|
35
35
|
parser.add_argument('--no-statement', dest='no_statement', action='store_true',
|
|
36
36
|
help='allow export without statement')
|
|
37
37
|
parser.add_argument('--export-ocen', dest='export_ocen', action='store_true',
|
|
@@ -118,12 +118,14 @@ class Command(BaseCommand):
|
|
|
118
118
|
shutil.copy(test, os.path.join(ocen_dir, ext, os.path.basename(test)))
|
|
119
119
|
num_tests += 1
|
|
120
120
|
|
|
121
|
+
dlazaw_dir = os.path.join(os.getcwd(), 'dlazaw')
|
|
121
122
|
if num_tests == 0:
|
|
122
123
|
print(util.warning('No ocen tests found.'))
|
|
124
|
+
elif os.path.exists(dlazaw_dir):
|
|
125
|
+
print(util.warning('Skipping ocen archive creation because dlazaw directory exists.'))
|
|
123
126
|
else:
|
|
124
127
|
shutil.make_archive(os.path.join(attachments_dir, f'{self.task_id}ocen'), 'zip', tmpdir)
|
|
125
128
|
|
|
126
|
-
dlazaw_dir = os.path.join(os.getcwd(), 'dlazaw')
|
|
127
129
|
if os.path.exists(dlazaw_dir):
|
|
128
130
|
print('Archiving dlazaw directory and adding to attachments.')
|
|
129
131
|
os.makedirs(os.path.join(tmpdir, 'dlazaw'), exist_ok=True)
|
|
@@ -3,7 +3,7 @@ import argparse
|
|
|
3
3
|
from sinol_make import util
|
|
4
4
|
from sinol_make.commands.ingen import Command as IngenCommand
|
|
5
5
|
from sinol_make.commands.outgen import Command as OutgenCommand
|
|
6
|
-
from sinol_make.helpers import parsers
|
|
6
|
+
from sinol_make.helpers import parsers, package_util
|
|
7
7
|
from sinol_make.interfaces.BaseCommand import BaseCommand
|
|
8
8
|
|
|
9
9
|
|
|
@@ -15,6 +15,9 @@ class Command(BaseCommand):
|
|
|
15
15
|
def get_name(self):
|
|
16
16
|
return "gen"
|
|
17
17
|
|
|
18
|
+
def get_short_name(self):
|
|
19
|
+
return "g"
|
|
20
|
+
|
|
18
21
|
def configure_subparser(self, subparser):
|
|
19
22
|
parser = subparser.add_parser(
|
|
20
23
|
self.get_name(),
|
|
@@ -31,15 +34,10 @@ class Command(BaseCommand):
|
|
|
31
34
|
help='path to ingen source file, for example prog/abcingen.cpp')
|
|
32
35
|
parser.add_argument('-i', '--only-inputs', action='store_true', help='generate input files only')
|
|
33
36
|
parser.add_argument('-o', '--only-outputs', action='store_true', help='generate output files only')
|
|
34
|
-
|
|
35
|
-
help=f'number of cpus to use to generate output files '
|
|
36
|
-
f'(default: {util.default_cpu_count()})',
|
|
37
|
-
default=util.default_cpu_count())
|
|
37
|
+
parsers.add_cpus_argument(parser, 'number of cpus to use to generate output files')
|
|
38
38
|
parser.add_argument('-n', '--no-validate', default=False, action='store_true',
|
|
39
39
|
help='do not validate test contents')
|
|
40
|
-
|
|
41
|
-
help='Use -fsanitize=address,undefined for ingen compilation. Warning: this may fail on some '
|
|
42
|
-
'systems. To fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
|
|
40
|
+
parsers.add_fsanitize_argument(parser)
|
|
43
41
|
parsers.add_compilation_arguments(parser)
|
|
44
42
|
return parser
|
|
45
43
|
|
|
@@ -49,6 +47,7 @@ class Command(BaseCommand):
|
|
|
49
47
|
self.args = args
|
|
50
48
|
self.ins = args.only_inputs
|
|
51
49
|
self.outs = args.only_outputs
|
|
50
|
+
self.task_type = package_util.get_task_type_cls()
|
|
52
51
|
# If no arguments are specified, generate both input and output files.
|
|
53
52
|
if not self.ins and not self.outs:
|
|
54
53
|
self.ins = True
|
|
@@ -58,6 +57,10 @@ class Command(BaseCommand):
|
|
|
58
57
|
command = IngenCommand()
|
|
59
58
|
command.run(args)
|
|
60
59
|
|
|
60
|
+
if not self.task_type.run_outgen():
|
|
61
|
+
print(util.warning("Outgen is not supported for this task type."))
|
|
62
|
+
return
|
|
63
|
+
|
|
61
64
|
if self.outs:
|
|
62
65
|
command = OutgenCommand()
|
|
63
66
|
command.run(args)
|
|
@@ -30,22 +30,43 @@ class Command(BaseCommand):
|
|
|
30
30
|
help='path to ingen source file, for example prog/abcingen.cpp')
|
|
31
31
|
parser.add_argument('-n', '--no-validate', default=False, action='store_true',
|
|
32
32
|
help='do not validate test contents')
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
|
|
33
|
+
parsers.add_cpus_argument(parser, 'number of cpus used for validating tests')
|
|
34
|
+
parsers.add_fsanitize_argument(parser)
|
|
36
35
|
parsers.add_compilation_arguments(parser)
|
|
37
36
|
return parser
|
|
38
37
|
|
|
38
|
+
def delete_dangling_files(self, dates):
|
|
39
|
+
to_delete = set()
|
|
40
|
+
for test in glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")):
|
|
41
|
+
basename = os.path.basename(test)
|
|
42
|
+
if basename in dates and dates[basename] == os.path.getmtime(test):
|
|
43
|
+
to_delete.add(os.path.basename(test))
|
|
44
|
+
if to_delete:
|
|
45
|
+
config = package_util.get_config()
|
|
46
|
+
if 'sinol_static_tests' not in config:
|
|
47
|
+
print(util.warning('Old input files won\'t be deleted, '
|
|
48
|
+
'because static tests are not defined. '
|
|
49
|
+
'You can define them in config.yml with `sinol_static_tests` key.'))
|
|
50
|
+
else:
|
|
51
|
+
static_files = config['sinol_static_tests']
|
|
52
|
+
if isinstance(static_files, str):
|
|
53
|
+
static_files = [static_files]
|
|
54
|
+
static_files = set([os.path.basename(test) for test in static_files])
|
|
55
|
+
to_delete = to_delete - static_files
|
|
56
|
+
if to_delete:
|
|
57
|
+
print('Cleaning up old input files.')
|
|
58
|
+
for test in to_delete:
|
|
59
|
+
os.remove(os.path.join(os.getcwd(), "in", test))
|
|
60
|
+
|
|
39
61
|
def run(self, args: argparse.Namespace):
|
|
40
62
|
args = util.init_package_command(args)
|
|
41
63
|
|
|
42
64
|
self.args = args
|
|
43
65
|
|
|
44
66
|
self.task_id = package_util.get_task_id()
|
|
45
|
-
package_util.validate_test_names(self.task_id)
|
|
46
67
|
util.change_stack_size_to_unlimited()
|
|
47
68
|
self.ingen = get_ingen(self.task_id, args.ingen_path)
|
|
48
|
-
print(
|
|
69
|
+
print(f'Using ingen file {os.path.basename(self.ingen)}')
|
|
49
70
|
self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, self.args.fsanitize)
|
|
50
71
|
|
|
51
72
|
previous_tests = []
|
|
@@ -64,18 +85,11 @@ class Command(BaseCommand):
|
|
|
64
85
|
else:
|
|
65
86
|
util.exit_with_error('Failed to generate input files.')
|
|
66
87
|
|
|
67
|
-
|
|
68
|
-
for test in glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")):
|
|
69
|
-
basename = os.path.basename(test)
|
|
70
|
-
if basename in dates and dates[basename] == os.path.getmtime(test):
|
|
71
|
-
os.unlink(test)
|
|
88
|
+
self.delete_dangling_files(dates)
|
|
72
89
|
|
|
73
90
|
with open(paths.get_cache_path("input_tests"), "w") as f:
|
|
74
91
|
f.write("\n".join(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in"))))
|
|
75
92
|
|
|
76
93
|
if not self.args.no_validate:
|
|
77
|
-
print(util.info('Validating input test contents.'))
|
|
78
94
|
tests = sorted(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")))
|
|
79
|
-
|
|
80
|
-
package_util.validate_test(test)
|
|
81
|
-
print(util.info('Input test contents are valid!'))
|
|
95
|
+
package_util.validate_tests(tests, self.args.cpus, 'input')
|
|
@@ -58,7 +58,7 @@ def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='
|
|
|
58
58
|
compilers = compiler.verify_compilers(args, [ingen_path])
|
|
59
59
|
ingen_exe, compile_log_path = compile.compile_file(ingen_path, package_util.get_executable(ingen_path),
|
|
60
60
|
compilers, compilation_flags, use_fsanitize=use_fsanitize,
|
|
61
|
-
additional_flags='-D_INGEN')
|
|
61
|
+
additional_flags='-D_INGEN', use_extras=False)
|
|
62
62
|
|
|
63
63
|
if ingen_exe is None:
|
|
64
64
|
compile.print_compile_log(compile_log_path)
|