sinol-make 1.5.29__tar.gz → 1.6.0.dev2__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.5.29/src/sinol_make.egg-info → sinol_make-1.6.0.dev2}/PKG-INFO +2 -1
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/setup.cfg +1 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/__init__.py +13 -4
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/doc/__init__.py +1 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/export/__init__.py +16 -2
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/gen/__init__.py +4 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/ingen/__init__.py +5 -1
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/ingen/ingen_util.py +15 -5
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/init/__init__.py +2 -2
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/inwer/__init__.py +12 -2
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/inwer/inwer_util.py +2 -2
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/outgen/__init__.py +1 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/run/__init__.py +1 -0
- sinol_make-1.6.0.dev2/src/sinol_make/commands/verify/__init__.py +160 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/contest_types/default.py +7 -1
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/contest_types/oi.py +13 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/cache.py +6 -7
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/compile.py +12 -6
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/package_util.py +19 -2
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/structs/cache_structs.py +11 -1
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/util.py +20 -24
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2/src/sinol_make.egg-info}/PKG-INFO +2 -1
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make.egg-info/SOURCES.txt +1 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make.egg-info/requires.txt +1 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/tests/test_multiple_arguments.py +1 -1
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/tests/test_util.py +23 -22
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/LICENSE +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/README.md +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/pyproject.toml +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/commands/outgen/outgen_util.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/contest_types/__init__.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/contest_types/icpc.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/__init__.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/compiler.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/parsers.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/paths.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/helpers/printer.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/interfaces/BaseCommand.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/interfaces/Errors.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/interfaces/__init__.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/oiejq/__init__.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/oiejq/perf_test.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/structs/__init__.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/structs/compiler_structs.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/structs/gen_structs.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/structs/inwer_structs.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/structs/run_structs.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make/structs/status_structs.py +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make.egg-info/dependency_links.txt +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make.egg-info/entry_points.txt +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/src/sinol_make.egg-info/top_level.txt +0 -0
- {sinol_make-1.5.29 → sinol_make-1.6.0.dev2}/tests/test_oiejq.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sinol_make
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0.dev2
|
|
4
4
|
Summary: CLI tool for creating sio2 task packages
|
|
5
5
|
Author: Mateusz Masiarz
|
|
6
6
|
Author-email: m.masiarz@fri.edu.pl
|
|
@@ -21,6 +21,7 @@ Requires-Dist: PyYAML
|
|
|
21
21
|
Requires-Dist: dictdiffer
|
|
22
22
|
Requires-Dist: importlib-resources
|
|
23
23
|
Requires-Dist: psutil
|
|
24
|
+
Requires-Dist: packaging
|
|
24
25
|
Provides-Extra: tests
|
|
25
26
|
Requires-Dist: pytest; extra == "tests"
|
|
26
27
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
@@ -6,7 +6,7 @@ import argcomplete
|
|
|
6
6
|
|
|
7
7
|
from sinol_make import util, oiejq
|
|
8
8
|
|
|
9
|
-
__version__ = "1.
|
|
9
|
+
__version__ = "1.6.0.dev2"
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def configure_parsers():
|
|
@@ -79,6 +79,9 @@ def main_exn():
|
|
|
79
79
|
def main():
|
|
80
80
|
new_version = None
|
|
81
81
|
try:
|
|
82
|
+
if util.is_dev(__version__):
|
|
83
|
+
print(util.warning('You are using a development version of sinol-make. '
|
|
84
|
+
'It may be unstable and contain bugs.'))
|
|
82
85
|
new_version = util.check_for_updates(__version__)
|
|
83
86
|
main_exn()
|
|
84
87
|
except argparse.ArgumentError as err:
|
|
@@ -92,6 +95,12 @@ def main():
|
|
|
92
95
|
'https://github.com/sio2project/sinol-make/#reporting-bugs-and-contributing-code')
|
|
93
96
|
finally:
|
|
94
97
|
if new_version is not None:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
if not util.is_dev(new_version):
|
|
99
|
+
print(util.warning(
|
|
100
|
+
f'New version of sinol-make is available (your version: {__version__}, available version: '
|
|
101
|
+
f'{new_version}).\nYou can update it by running `pip3 install sinol-make --upgrade`.'))
|
|
102
|
+
elif util.is_dev(new_version):
|
|
103
|
+
print(util.warning(
|
|
104
|
+
f'New development version of sinol-make is available (your version: {__version__}, available '
|
|
105
|
+
f'version: {new_version}).\nYou can update it by running `pip3 install sinol-make --pre --upgrade`.'
|
|
106
|
+
))
|
|
@@ -76,6 +76,7 @@ class Command(BaseCommand):
|
|
|
76
76
|
' pdflatex - uses pdflatex. Works with .png and .jpg images.\n'
|
|
77
77
|
' latex_dvi - uses latex and dvipdf. Works with .ps and .eps images.', default='auto')
|
|
78
78
|
parser.add_argument('files', type=str, nargs='*', help='files to compile')
|
|
79
|
+
return parser
|
|
79
80
|
|
|
80
81
|
def run(self, args: argparse.Namespace):
|
|
81
82
|
args = util.init_package_command(args)
|
|
@@ -37,6 +37,7 @@ class Command(BaseCommand):
|
|
|
37
37
|
parser.add_argument('--export-ocen', dest='export_ocen', action='store_true',
|
|
38
38
|
help='Create ocen archive')
|
|
39
39
|
parsers.add_compilation_arguments(parser)
|
|
40
|
+
return parser
|
|
40
41
|
|
|
41
42
|
def generate_input_tests(self):
|
|
42
43
|
print('Generating tests...')
|
|
@@ -97,6 +98,7 @@ class Command(BaseCommand):
|
|
|
97
98
|
Creates ocen archive for sio2.
|
|
98
99
|
:param target_dir: Path to exported package.
|
|
99
100
|
"""
|
|
101
|
+
print('Generating ocen archive...')
|
|
100
102
|
attachments_dir = os.path.join(target_dir, 'attachments')
|
|
101
103
|
if not os.path.exists(attachments_dir):
|
|
102
104
|
os.makedirs(attachments_dir)
|
|
@@ -109,12 +111,25 @@ class Command(BaseCommand):
|
|
|
109
111
|
os.makedirs(in_dir)
|
|
110
112
|
out_dir = os.path.join(ocen_dir, 'out')
|
|
111
113
|
os.makedirs(out_dir)
|
|
114
|
+
num_tests = 0
|
|
112
115
|
for ext in ['in', 'out']:
|
|
113
116
|
for test in glob.glob(os.path.join(tests_dir, ext, f'{self.task_id}0*.{ext}')) + \
|
|
114
117
|
glob.glob(os.path.join(tests_dir, ext, f'{self.task_id}*ocen.{ext}')):
|
|
115
118
|
shutil.copy(test, os.path.join(ocen_dir, ext, os.path.basename(test)))
|
|
119
|
+
num_tests += 1
|
|
116
120
|
|
|
117
|
-
|
|
121
|
+
if num_tests == 0:
|
|
122
|
+
print(util.warning('No ocen tests found.'))
|
|
123
|
+
else:
|
|
124
|
+
shutil.make_archive(os.path.join(attachments_dir, f'{self.task_id}ocen'), 'zip', tmpdir)
|
|
125
|
+
|
|
126
|
+
dlazaw_dir = os.path.join(os.getcwd(), 'dlazaw')
|
|
127
|
+
if os.path.exists(dlazaw_dir):
|
|
128
|
+
print('Archiving dlazaw directory and adding to attachments.')
|
|
129
|
+
os.makedirs(os.path.join(tmpdir, 'dlazaw'), exist_ok=True)
|
|
130
|
+
shutil.copytree(dlazaw_dir, os.path.join(tmpdir, 'dlazaw', 'dlazaw'))
|
|
131
|
+
shutil.make_archive(os.path.join(attachments_dir, 'dlazaw'), 'zip',
|
|
132
|
+
os.path.join(tmpdir, 'dlazaw'))
|
|
118
133
|
|
|
119
134
|
def compile_statement(self):
|
|
120
135
|
command = DocCommand()
|
|
@@ -168,7 +183,6 @@ class Command(BaseCommand):
|
|
|
168
183
|
|
|
169
184
|
self.generate_output_files()
|
|
170
185
|
if self.args.export_ocen:
|
|
171
|
-
print('Generating ocen archive...')
|
|
172
186
|
self.create_ocen(target_dir)
|
|
173
187
|
|
|
174
188
|
def clear_files(self, target_dir: str):
|
|
@@ -37,7 +37,11 @@ class Command(BaseCommand):
|
|
|
37
37
|
default=util.default_cpu_count())
|
|
38
38
|
parser.add_argument('-n', '--no-validate', default=False, action='store_true',
|
|
39
39
|
help='do not validate test contents')
|
|
40
|
+
parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
|
|
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
43
|
parsers.add_compilation_arguments(parser)
|
|
44
|
+
return parser
|
|
41
45
|
|
|
42
46
|
def run(self, args: argparse.Namespace):
|
|
43
47
|
args = util.init_package_command(args)
|
|
@@ -30,7 +30,11 @@ 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
|
+
parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
|
|
34
|
+
help='Use -fsanitize=address,undefined for compilation. Warning: this may fail on some '
|
|
35
|
+
'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
|
|
33
36
|
parsers.add_compilation_arguments(parser)
|
|
37
|
+
return parser
|
|
34
38
|
|
|
35
39
|
def run(self, args: argparse.Namespace):
|
|
36
40
|
args = util.init_package_command(args)
|
|
@@ -42,7 +46,7 @@ class Command(BaseCommand):
|
|
|
42
46
|
util.change_stack_size_to_unlimited()
|
|
43
47
|
self.ingen = get_ingen(self.task_id, args.ingen_path)
|
|
44
48
|
print(util.info(f'Using ingen file {os.path.basename(self.ingen)}'))
|
|
45
|
-
self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode)
|
|
49
|
+
self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, self.args.fsanitize)
|
|
46
50
|
|
|
47
51
|
previous_tests = []
|
|
48
52
|
try:
|
|
@@ -47,7 +47,7 @@ def get_ingen(task_id, ingen_path=None):
|
|
|
47
47
|
return correct_ingen
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default'):
|
|
50
|
+
def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False):
|
|
51
51
|
"""
|
|
52
52
|
Compiles ingen and returns path to compiled executable.
|
|
53
53
|
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='
|
|
|
57
57
|
|
|
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
|
-
compilers, compilation_flags, use_fsanitize=
|
|
60
|
+
compilers, compilation_flags, use_fsanitize=use_fsanitize,
|
|
61
61
|
additional_flags='-D_INGEN')
|
|
62
62
|
|
|
63
63
|
if ingen_exe is None:
|
|
@@ -86,11 +86,21 @@ def run_ingen(ingen_exe, working_dir=None):
|
|
|
86
86
|
print(util.bold(' Ingen output '.center(util.get_terminal_size()[1], '=')))
|
|
87
87
|
process = subprocess.Popen([ingen_exe], stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
|
88
88
|
cwd=working_dir, shell=is_shell)
|
|
89
|
+
whole_output = ''
|
|
89
90
|
while process.poll() is None:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
out = process.stdout.readline().decode('utf-8')
|
|
92
|
+
if out != '':
|
|
93
|
+
print(out, end='')
|
|
94
|
+
whole_output += out
|
|
95
|
+
out = process.stdout.read().decode('utf-8')
|
|
96
|
+
whole_output += out
|
|
97
|
+
print(out, end='')
|
|
93
98
|
exit_code = process.returncode
|
|
94
99
|
print(util.bold(' End of ingen output '.center(util.get_terminal_size()[1], '=')))
|
|
95
100
|
|
|
101
|
+
if util.has_sanitizer_error(whole_output, exit_code):
|
|
102
|
+
print(util.warning('Warning: if ingen failed due to sanitizer errors, you can either run '
|
|
103
|
+
'`sudo sysctl vm.mmap_rnd_bits = 28` to fix this or disable sanitizers with the '
|
|
104
|
+
'--no-fsanitize flag.'))
|
|
105
|
+
|
|
96
106
|
return exit_code == 0
|
|
@@ -23,6 +23,7 @@ class Command(BaseCommand):
|
|
|
23
23
|
description='Create package from predefined template with given id.'
|
|
24
24
|
)
|
|
25
25
|
parser.add_argument('task_id', type=str, help='Id of the task to create')
|
|
26
|
+
return parser
|
|
26
27
|
|
|
27
28
|
def download_template(self):
|
|
28
29
|
repo = 'https://github.com/sio2project/sinol-make.git'
|
|
@@ -69,8 +70,7 @@ class Command(BaseCommand):
|
|
|
69
70
|
|
|
70
71
|
self.move_folder()
|
|
71
72
|
self.update_config()
|
|
72
|
-
|
|
73
|
+
|
|
73
74
|
self.used_tmpdir.cleanup()
|
|
74
75
|
|
|
75
76
|
print(util.info(f'Successfully created task "{self.task_id}"'))
|
|
76
|
-
|
|
@@ -39,7 +39,11 @@ class Command(BaseCommand):
|
|
|
39
39
|
help='test to verify, for example in/abc{0,1}*')
|
|
40
40
|
parser.add_argument('-c', '--cpus', type=int,
|
|
41
41
|
help=f'number of cpus to use (default: {util.default_cpu_count()})')
|
|
42
|
+
parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
|
|
43
|
+
help='Use -fsanitize=address,undefined for compilation. Warning: this may fail on some '
|
|
44
|
+
'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
|
|
42
45
|
add_compilation_arguments(parser)
|
|
46
|
+
return parser
|
|
43
47
|
|
|
44
48
|
@staticmethod
|
|
45
49
|
def verify_test(execution: InwerExecution) -> VerificationResult:
|
|
@@ -94,11 +98,14 @@ class Command(BaseCommand):
|
|
|
94
98
|
thr.start()
|
|
95
99
|
|
|
96
100
|
keyboard_interrupt = False
|
|
101
|
+
sanitizer_error = False
|
|
97
102
|
try:
|
|
98
103
|
with mp.Pool(self.cpus) as pool:
|
|
99
104
|
for i, result in enumerate(pool.imap(self.verify_test, executions)):
|
|
100
105
|
table_data.results[result.test_path].set_results(result.valid, result.output)
|
|
101
106
|
table_data.i = i
|
|
107
|
+
if util.has_sanitizer_error(result.output, 0 if result.valid else 1):
|
|
108
|
+
sanitizer_error = True
|
|
102
109
|
except KeyboardInterrupt:
|
|
103
110
|
keyboard_interrupt = True
|
|
104
111
|
|
|
@@ -108,6 +115,10 @@ class Command(BaseCommand):
|
|
|
108
115
|
|
|
109
116
|
print("\n".join(inwer_util.print_view(terminal_width, terminal_height, table_data)[0]))
|
|
110
117
|
|
|
118
|
+
if sanitizer_error:
|
|
119
|
+
print(util.warning('Warning: if inwer failed due to sanitizer errors, you can either run '
|
|
120
|
+
'`sudo sysctl vm.mmap_rnd_bits = 28` to fix this or disable sanitizers with the '
|
|
121
|
+
'--no-fsanitize flag.'))
|
|
111
122
|
if keyboard_interrupt:
|
|
112
123
|
util.exit_with_error('Keyboard interrupt.')
|
|
113
124
|
|
|
@@ -203,7 +214,7 @@ class Command(BaseCommand):
|
|
|
203
214
|
print('Verifying tests: ' + util.bold(', '.join(self.tests)))
|
|
204
215
|
|
|
205
216
|
util.change_stack_size_to_unlimited()
|
|
206
|
-
self.inwer_executable = inwer_util.compile_inwer(self.inwer, args, args.compile_mode)
|
|
217
|
+
self.inwer_executable = inwer_util.compile_inwer(self.inwer, args, args.compile_mode, args.fsanitize)
|
|
207
218
|
results: Dict[str, TestResult] = self.verify_and_print_table()
|
|
208
219
|
print('')
|
|
209
220
|
|
|
@@ -218,4 +229,3 @@ class Command(BaseCommand):
|
|
|
218
229
|
print("Verifying tests order...")
|
|
219
230
|
self.verify_tests_order()
|
|
220
231
|
print(util.info('Verification successful.'))
|
|
221
|
-
exit(0)
|
|
@@ -29,13 +29,13 @@ def get_inwer_path(task_id: str, path=None) -> Union[str, None]:
|
|
|
29
29
|
return None
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='default'):
|
|
32
|
+
def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False):
|
|
33
33
|
"""
|
|
34
34
|
Compiles inwer and returns path to compiled executable and path to compile log.
|
|
35
35
|
"""
|
|
36
36
|
compilers = compiler.verify_compilers(args, [inwer_path])
|
|
37
37
|
inwer_exe, compile_log_path = compile.compile_file(inwer_path, package_util.get_executable(inwer_path), compilers,
|
|
38
|
-
compilation_flags, use_fsanitize=
|
|
38
|
+
compilation_flags, use_fsanitize=use_fsanitize,
|
|
39
39
|
additional_flags='-D_INWER')
|
|
40
40
|
|
|
41
41
|
if inwer_exe is None:
|
|
@@ -34,6 +34,7 @@ class Command(BaseCommand):
|
|
|
34
34
|
parser.add_argument('-n', '--no-validate', default=False, action='store_true',
|
|
35
35
|
help='do not validate test contents')
|
|
36
36
|
parsers.add_compilation_arguments(parser)
|
|
37
|
+
return parser
|
|
37
38
|
|
|
38
39
|
def generate_outputs(self, outputs_to_generate):
|
|
39
40
|
print(f'Generating output files for {len(outputs_to_generate)} tests on {self.args.cpus} cpus.')
|
|
@@ -301,6 +301,7 @@ class Command(BaseCommand):
|
|
|
301
301
|
parser.add_argument('-a', '--apply-suggestions', dest='apply_suggestions', action='store_true',
|
|
302
302
|
help='apply suggestions from expected scores report')
|
|
303
303
|
add_compilation_arguments(parser)
|
|
304
|
+
return parser
|
|
304
305
|
|
|
305
306
|
def parse_time(self, time_str):
|
|
306
307
|
if len(time_str) < 3: return -1
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import stat
|
|
3
|
+
import shutil
|
|
4
|
+
import argparse
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
from sinol_make import util, contest_types
|
|
8
|
+
from sinol_make.helpers import parsers, package_util, paths
|
|
9
|
+
from sinol_make.interfaces.BaseCommand import BaseCommand
|
|
10
|
+
from sinol_make.commands.gen import Command as GenCommand
|
|
11
|
+
from sinol_make.commands.doc import Command as DocCommand
|
|
12
|
+
from sinol_make.commands.inwer import Command as InwerCommand, inwer_util
|
|
13
|
+
from sinol_make.commands.run import Command as RunCommand
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Command(BaseCommand):
|
|
17
|
+
"""
|
|
18
|
+
Class for `verify` command.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def get_name(self):
|
|
22
|
+
return "verify"
|
|
23
|
+
|
|
24
|
+
def configure_subparser(self, subparser):
|
|
25
|
+
parser = subparser.add_parser(
|
|
26
|
+
self.get_name(),
|
|
27
|
+
help='Verify the package',
|
|
28
|
+
description='Verify the whole package. This command will first '
|
|
29
|
+
'run stress tests (if the file `prog/{task_id}stresstest.sh` exists), '
|
|
30
|
+
'verify the config, generate tests, generate problem '
|
|
31
|
+
'statements, run inwer and run all solutions. '
|
|
32
|
+
'Ingen and inwer are compiled with sanitizers (-fsanitize=address,undefined), '
|
|
33
|
+
'which may fail on some systems. To fix this, run `sudo sysctl vm.mmap_rnd_bits = 28` '
|
|
34
|
+
'or disable sanitizers with --no-fsanitize.'
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
parser.add_argument('-f', '--no-fsanitize', action='store_true', default=False,
|
|
38
|
+
help='do not use sanitizers for ingen and inwer programs')
|
|
39
|
+
parser.add_argument('-c', '--cpus', type=int,
|
|
40
|
+
help=f'number of cpus that sinol-make will use '
|
|
41
|
+
f'(default: {util.default_cpu_count()})',
|
|
42
|
+
default=util.default_cpu_count())
|
|
43
|
+
parsers.add_compilation_arguments(parser)
|
|
44
|
+
|
|
45
|
+
def remove_cache(self):
|
|
46
|
+
"""
|
|
47
|
+
Remove whole cache dir
|
|
48
|
+
"""
|
|
49
|
+
cache_dir = paths.get_cache_path()
|
|
50
|
+
if os.path.exists(cache_dir):
|
|
51
|
+
shutil.rmtree(cache_dir)
|
|
52
|
+
|
|
53
|
+
def check_extra_files(self):
|
|
54
|
+
"""
|
|
55
|
+
Checks if extra_compilation_files and extra_execution_files exist.
|
|
56
|
+
"""
|
|
57
|
+
extra_compilation_files = self.config.get('extra_compilation_files', [])
|
|
58
|
+
for file in extra_compilation_files:
|
|
59
|
+
if not os.path.exists(os.path.join(os.getcwd(), "prog", file)):
|
|
60
|
+
util.exit_with_error(f"Extra compilation file `{file}` does not exist. "
|
|
61
|
+
f"It should be in `prog` directory.")
|
|
62
|
+
if extra_compilation_files:
|
|
63
|
+
print(util.info("All extra compilation files exist."))
|
|
64
|
+
|
|
65
|
+
extra_execution_files = self.config.get('extra_execution_files', {})
|
|
66
|
+
for lang, files in extra_execution_files.items():
|
|
67
|
+
for file in files:
|
|
68
|
+
if not os.path.exists(os.path.join(os.getcwd(), "prog", file)):
|
|
69
|
+
util.exit_with_error(f"Extra execution file `{file}` for language `{lang}` does not exist. "
|
|
70
|
+
f"It should be in `prog` directory.")
|
|
71
|
+
if extra_execution_files:
|
|
72
|
+
print(util.info("All extra execution files exist."))
|
|
73
|
+
|
|
74
|
+
def verify_scores(self, scored_groups):
|
|
75
|
+
config_scores = self.config.get('scores', {})
|
|
76
|
+
if not config_scores:
|
|
77
|
+
return
|
|
78
|
+
if '0' in scored_groups:
|
|
79
|
+
scored_groups.remove('0')
|
|
80
|
+
if 0 in scored_groups:
|
|
81
|
+
scored_groups.remove(0)
|
|
82
|
+
|
|
83
|
+
for group in scored_groups:
|
|
84
|
+
if int(group) not in config_scores:
|
|
85
|
+
util.exit_with_error(f"Score for group '{group}' not found. "
|
|
86
|
+
f"You must either provide scores for all groups "
|
|
87
|
+
f"or not provide them at all (to have them assigned automatically).")
|
|
88
|
+
|
|
89
|
+
for group in config_scores:
|
|
90
|
+
if int(group) not in scored_groups:
|
|
91
|
+
util.exit_with_error(f"Score for group '{group}' found in config, "
|
|
92
|
+
f"but no such test group exists in scored groups. "
|
|
93
|
+
f"You must either provide scores for all groups "
|
|
94
|
+
f"or not provide them at all (to have them assigned automatically).")
|
|
95
|
+
|
|
96
|
+
print(util.info("All scores are provided for all groups."))
|
|
97
|
+
|
|
98
|
+
def prepare_args(self, command):
|
|
99
|
+
parser = argparse.ArgumentParser()
|
|
100
|
+
subparser = parser.add_subparsers(dest='command')
|
|
101
|
+
command_parser = command.configure_subparser(subparser)
|
|
102
|
+
command_args = command_parser.parse_args([])
|
|
103
|
+
for key, value in vars(self.args).items():
|
|
104
|
+
setattr(command_args, key, value)
|
|
105
|
+
setattr(command_args, 'fsanitize', not self.args.no_fsanitize)
|
|
106
|
+
return command_args
|
|
107
|
+
|
|
108
|
+
def run_stresstests(self):
|
|
109
|
+
stresstests_path = os.path.join(os.getcwd(), 'prog', self.task_id + 'stresstest.sh')
|
|
110
|
+
if not os.path.exists(stresstests_path):
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
print(util.bold(' Running stress tests '.center(util.get_terminal_size()[1], '=')))
|
|
114
|
+
print(f"See the comments in `prog/{self.task_id}stresstest.sh` for details.".center(
|
|
115
|
+
util.get_terminal_size()[1], ' '))
|
|
116
|
+
st = os.stat(stresstests_path)
|
|
117
|
+
os.chmod(stresstests_path, st.st_mode | stat.S_IEXEC)
|
|
118
|
+
p = subprocess.Popen([stresstests_path], shell=True)
|
|
119
|
+
p.wait()
|
|
120
|
+
if p.returncode != 0:
|
|
121
|
+
util.exit_with_error("Stress tests failed.")
|
|
122
|
+
|
|
123
|
+
def run(self, args: argparse.Namespace):
|
|
124
|
+
self.args = util.init_package_command(args)
|
|
125
|
+
self.config = package_util.get_config()
|
|
126
|
+
self.task_id = package_util.get_task_id()
|
|
127
|
+
self.contest = contest_types.get_contest_type()
|
|
128
|
+
|
|
129
|
+
self.remove_cache()
|
|
130
|
+
self.check_extra_files()
|
|
131
|
+
self.contest.verify_pre_gen()
|
|
132
|
+
|
|
133
|
+
# Run stresstests (if present)
|
|
134
|
+
self.run_stresstests()
|
|
135
|
+
|
|
136
|
+
# Generate tests
|
|
137
|
+
print(util.bold(' Generating tests '.center(util.get_terminal_size()[1], '=')))
|
|
138
|
+
gen = GenCommand()
|
|
139
|
+
gen.run(self.prepare_args(gen))
|
|
140
|
+
self.verify_scores(package_util.get_groups(package_util.get_all_inputs(self.task_id), self.task_id))
|
|
141
|
+
|
|
142
|
+
# Generate problem statements
|
|
143
|
+
print(util.bold(' Generating problem statements '.center(util.get_terminal_size()[1], '=')))
|
|
144
|
+
doc = DocCommand()
|
|
145
|
+
doc.run(self.prepare_args(doc))
|
|
146
|
+
|
|
147
|
+
# Run inwer
|
|
148
|
+
if inwer_util.get_inwer_path(self.task_id) is None:
|
|
149
|
+
print(util.warning("Package doesn't have inwer."))
|
|
150
|
+
else:
|
|
151
|
+
print(util.bold(' Running inwer '.center(util.get_terminal_size()[1], '=')))
|
|
152
|
+
inwer = InwerCommand()
|
|
153
|
+
inwer.run(self.prepare_args(inwer))
|
|
154
|
+
|
|
155
|
+
# Run solutions
|
|
156
|
+
print(util.bold(' Running solutions '.center(util.get_terminal_size()[1], '=')))
|
|
157
|
+
run = RunCommand()
|
|
158
|
+
run.run(self.prepare_args(run))
|
|
159
|
+
|
|
160
|
+
print(util.info('Package verification successful.'))
|
|
@@ -64,7 +64,7 @@ class DefaultContest:
|
|
|
64
64
|
:param scores: Dictionary: {"<group>": <points>}
|
|
65
65
|
:return: Maximum possible score.
|
|
66
66
|
"""
|
|
67
|
-
if groups[0] == 0 and len(groups) == 1:
|
|
67
|
+
if len(groups) == 0 or (groups[0] == 0 and len(groups) == 1):
|
|
68
68
|
return 0
|
|
69
69
|
|
|
70
70
|
possible_score = 0
|
|
@@ -126,3 +126,9 @@ class DefaultContest:
|
|
|
126
126
|
Returns maximum score for single test
|
|
127
127
|
"""
|
|
128
128
|
return 100
|
|
129
|
+
|
|
130
|
+
def verify_pre_gen(self):
|
|
131
|
+
"""
|
|
132
|
+
Called by verify command before generating tests.
|
|
133
|
+
"""
|
|
134
|
+
pass
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
|
|
3
|
+
from sinol_make import util
|
|
4
|
+
from sinol_make.helpers import package_util
|
|
3
5
|
from sinol_make.structs.status_structs import ExecutionResult
|
|
4
6
|
from sinol_make.contest_types.default import DefaultContest
|
|
5
7
|
|
|
@@ -28,3 +30,14 @@ class OIContest(DefaultContest):
|
|
|
28
30
|
return result.Points
|
|
29
31
|
else:
|
|
30
32
|
return 1 + int((result.Points - 1) * ((time_limit - result.Time) / (time_limit / 2.0)))
|
|
33
|
+
|
|
34
|
+
def verify_pre_gen(self):
|
|
35
|
+
"""
|
|
36
|
+
Verify if scores sum up to 100.
|
|
37
|
+
"""
|
|
38
|
+
config = package_util.get_config()
|
|
39
|
+
if 'scores' not in config:
|
|
40
|
+
return
|
|
41
|
+
total_score = sum(config['scores'].values())
|
|
42
|
+
if total_score != 100:
|
|
43
|
+
util.exit_with_error(f"Total score in config is {total_score}, but should be 100.")
|
|
@@ -35,7 +35,7 @@ def get_cache_file(solution_path: str) -> CacheFile:
|
|
|
35
35
|
return CacheFile()
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def check_compiled(file_path: str) -> Union[str, None]:
|
|
38
|
+
def check_compiled(file_path: str, compilation_flags: str, sanitizers: bool) -> Union[str, None]:
|
|
39
39
|
"""
|
|
40
40
|
Check if a file is compiled
|
|
41
41
|
:param file_path: Path to the file
|
|
@@ -44,7 +44,7 @@ def check_compiled(file_path: str) -> Union[str, None]:
|
|
|
44
44
|
md5sum = util.get_file_md5(file_path)
|
|
45
45
|
try:
|
|
46
46
|
info = get_cache_file(file_path)
|
|
47
|
-
if info.md5sum == md5sum:
|
|
47
|
+
if info.md5sum == md5sum and info.compilation_flags == compilation_flags and info.sanitizers == sanitizers:
|
|
48
48
|
exe_path = info.executable_path
|
|
49
49
|
if os.path.exists(exe_path):
|
|
50
50
|
return exe_path
|
|
@@ -53,19 +53,18 @@ def check_compiled(file_path: str) -> Union[str, None]:
|
|
|
53
53
|
return None
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def save_compiled(file_path: str, exe_path: str, is_checker: bool = False):
|
|
56
|
+
def save_compiled(file_path: str, exe_path: str, compilation_flags: str, sanitizers: bool, is_checker: bool = False):
|
|
57
57
|
"""
|
|
58
58
|
Save the compiled executable path to cache in `.cache/md5sums/<basename of file_path>`,
|
|
59
59
|
which contains the md5sum of the file and the path to the executable.
|
|
60
60
|
:param file_path: Path to the file
|
|
61
61
|
:param exe_path: Path to the compiled executable
|
|
62
|
+
:param compilation_flags: Compilation flags used
|
|
63
|
+
:param sanitizers: Whether -fsanitize=undefined,address was used
|
|
62
64
|
:param is_checker: Whether the compiled file is a checker. If True, all cached tests are removed.
|
|
63
65
|
"""
|
|
64
|
-
info = CacheFile()
|
|
65
|
-
info.executable_path = exe_path
|
|
66
|
-
info.md5sum = util.get_file_md5(file_path)
|
|
66
|
+
info = CacheFile(util.get_file_md5(file_path), exe_path, compilation_flags, sanitizers)
|
|
67
67
|
info.save(file_path)
|
|
68
|
-
|
|
69
68
|
if is_checker:
|
|
70
69
|
remove_results_cache()
|
|
71
70
|
|
|
@@ -37,10 +37,17 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp
|
|
|
37
37
|
if use_fsanitize and util.is_macos_arm():
|
|
38
38
|
use_fsanitize = False
|
|
39
39
|
|
|
40
|
+
if compilation_flags == 'w':
|
|
41
|
+
compilation_flags = 'weak'
|
|
42
|
+
elif compilation_flags == 'o':
|
|
43
|
+
compilation_flags = 'oioioi'
|
|
44
|
+
elif compilation_flags == 'd':
|
|
45
|
+
compilation_flags = 'default'
|
|
46
|
+
|
|
40
47
|
if extra_compilation_files is None:
|
|
41
48
|
extra_compilation_files = []
|
|
42
49
|
|
|
43
|
-
compiled_exe = check_compiled(program)
|
|
50
|
+
compiled_exe = check_compiled(program, compilation_flags, use_fsanitize)
|
|
44
51
|
if compiled_exe is not None:
|
|
45
52
|
if compile_log is not None:
|
|
46
53
|
compile_log.write(f'Using cached executable {compiled_exe}\n')
|
|
@@ -53,12 +60,11 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp
|
|
|
53
60
|
shutil.copy(file, os.path.join(os.path.dirname(output), os.path.basename(file)))
|
|
54
61
|
|
|
55
62
|
gcc_compilation_flags = ''
|
|
56
|
-
if compilation_flags == 'weak'
|
|
57
|
-
compilation_flags = 'weak'
|
|
63
|
+
if compilation_flags == 'weak':
|
|
58
64
|
gcc_compilation_flags = '' # Disable all warnings
|
|
59
|
-
elif compilation_flags == 'oioioi'
|
|
65
|
+
elif compilation_flags == 'oioioi':
|
|
60
66
|
gcc_compilation_flags = ' -Wall -Wno-unused-result -Werror' # Same flags as oioioi
|
|
61
|
-
elif compilation_flags == 'default'
|
|
67
|
+
elif compilation_flags == 'default':
|
|
62
68
|
gcc_compilation_flags = ' -Werror -Wall -Wextra -Wshadow -Wconversion -Wno-unused-result -Wfloat-equal'
|
|
63
69
|
else:
|
|
64
70
|
util.exit_with_error(f'Unknown compilation flags group: {compilation_flags}')
|
|
@@ -108,7 +114,7 @@ def compile(program, output, compilers: Compilers = None, compile_log=None, comp
|
|
|
108
114
|
if process.returncode != 0:
|
|
109
115
|
raise CompilationError('Compilation failed')
|
|
110
116
|
else:
|
|
111
|
-
save_compiled(program, output, is_checker)
|
|
117
|
+
save_compiled(program, output, compilation_flags, use_fsanitize, is_checker)
|
|
112
118
|
return True
|
|
113
119
|
|
|
114
120
|
|
|
@@ -266,6 +266,14 @@ def get_memory_limit(test_path, config, lang, task_id, args=None):
|
|
|
266
266
|
return _get_limit(LimitTypes.MEMORY_LIMIT, test_path, str_config, lang, task_id)
|
|
267
267
|
|
|
268
268
|
|
|
269
|
+
def get_in_tests_re(task_id: str) -> re.Pattern:
|
|
270
|
+
return re.compile(r'^%s(([0-9]+)([a-z]?[a-z0-9]*))\.in$' % re.escape(task_id))
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def get_out_tests_re(task_id: str) -> re.Pattern:
|
|
274
|
+
return re.compile(r'^%s(([0-9]+)([a-z]?[a-z0-9]*))\.out$' % re.escape(task_id))
|
|
275
|
+
|
|
276
|
+
|
|
269
277
|
def validate_test_names(task_id):
|
|
270
278
|
"""
|
|
271
279
|
Checks if all files in the package have valid names.
|
|
@@ -278,12 +286,12 @@ def validate_test_names(task_id):
|
|
|
278
286
|
invalid_files.append(os.path.basename(file))
|
|
279
287
|
return invalid_files
|
|
280
288
|
|
|
281
|
-
in_test_re =
|
|
289
|
+
in_test_re = get_in_tests_re(task_id)
|
|
282
290
|
invalid_in_tests = get_invalid_files(os.path.join("in", "*.in"), in_test_re)
|
|
283
291
|
if len(invalid_in_tests) > 0:
|
|
284
292
|
util.exit_with_error(f'Input tests with invalid names: {", ".join(invalid_in_tests)}.')
|
|
285
293
|
|
|
286
|
-
out_test_re =
|
|
294
|
+
out_test_re = get_out_tests_re(task_id)
|
|
287
295
|
invalid_out_tests = get_invalid_files(os.path.join("out", "*.out"), out_test_re)
|
|
288
296
|
if len(invalid_out_tests) > 0:
|
|
289
297
|
util.exit_with_error(f'Output tests with invalid names: {", ".join(invalid_out_tests)}.')
|
|
@@ -376,3 +384,12 @@ def validate_test(test_path: str):
|
|
|
376
384
|
|
|
377
385
|
if num_empty != 0:
|
|
378
386
|
util.exit_with_error(f'Exactly one empty line expected in {basename}')
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def get_all_inputs(task_id):
|
|
390
|
+
in_test_re = get_in_tests_re(task_id)
|
|
391
|
+
inputs = []
|
|
392
|
+
for file in glob.glob(os.path.join(os.getcwd(), "in", "*.in")):
|
|
393
|
+
if in_test_re.match(os.path.basename(file)):
|
|
394
|
+
inputs.append(file)
|
|
395
|
+
return inputs
|
|
@@ -43,20 +43,28 @@ class CacheFile:
|
|
|
43
43
|
md5sum: str
|
|
44
44
|
# Path to the executable
|
|
45
45
|
executable_path: str
|
|
46
|
+
# Compilation flags used
|
|
47
|
+
compilation_flags: str
|
|
48
|
+
# Whether -fsanitize=undefined,address was used
|
|
49
|
+
sanitizers: bool
|
|
46
50
|
# Test results
|
|
47
51
|
tests: Dict[str, CacheTest]
|
|
48
52
|
|
|
49
|
-
def __init__(self, md5sum="", executable_path="", tests=None):
|
|
53
|
+
def __init__(self, md5sum="", executable_path="", compilation_flags="default", sanitizers=False, tests=None):
|
|
50
54
|
if tests is None:
|
|
51
55
|
tests = {}
|
|
52
56
|
self.md5sum = md5sum
|
|
53
57
|
self.executable_path = executable_path
|
|
58
|
+
self.compilation_flags = compilation_flags
|
|
59
|
+
self.sanitizers = sanitizers
|
|
54
60
|
self.tests = tests
|
|
55
61
|
|
|
56
62
|
def to_dict(self) -> Dict:
|
|
57
63
|
return {
|
|
58
64
|
"md5sum": self.md5sum,
|
|
59
65
|
"executable_path": self.executable_path,
|
|
66
|
+
"compilation_flags": self.compilation_flags,
|
|
67
|
+
"sanitizers": self.sanitizers,
|
|
60
68
|
"tests": {k: v.to_dict() for k, v in self.tests.items()}
|
|
61
69
|
}
|
|
62
70
|
|
|
@@ -65,6 +73,8 @@ class CacheFile:
|
|
|
65
73
|
return CacheFile(
|
|
66
74
|
md5sum=dict.get("md5sum", ""),
|
|
67
75
|
executable_path=dict.get("executable_path", ""),
|
|
76
|
+
compilation_flags=dict.get("compilation_flags", "default"),
|
|
77
|
+
sanitizers=dict.get("sanitizers", False),
|
|
68
78
|
tests={k: CacheTest(
|
|
69
79
|
time_limit=v["time_limit"],
|
|
70
80
|
memory_limit=v["memory_limit"],
|
|
@@ -7,6 +7,7 @@ import hashlib
|
|
|
7
7
|
import multiprocessing
|
|
8
8
|
import resource
|
|
9
9
|
from typing import Union
|
|
10
|
+
from packaging.version import parse as parse_version
|
|
10
11
|
|
|
11
12
|
from sinol_make.contest_types import get_contest_type
|
|
12
13
|
from sinol_make.helpers import paths, cache
|
|
@@ -150,10 +151,18 @@ def import_importlib_resources():
|
|
|
150
151
|
return importlib
|
|
151
152
|
|
|
152
153
|
|
|
153
|
-
def
|
|
154
|
+
def is_dev(version):
|
|
155
|
+
"""
|
|
156
|
+
Function to check if the version is a development version.
|
|
157
|
+
"""
|
|
158
|
+
return parse_version(version).is_devrelease
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def check_for_updates(current_version, check=True) -> Union[str, None]:
|
|
154
162
|
"""
|
|
155
163
|
Function to check if there is a new version of sinol-make.
|
|
156
164
|
:param current_version: current version of sinol-make
|
|
165
|
+
:param check: whether to check for new version
|
|
157
166
|
:return: returns new version if there is one, None otherwise
|
|
158
167
|
"""
|
|
159
168
|
importlib = import_importlib_resources()
|
|
@@ -164,8 +173,9 @@ def check_for_updates(current_version) -> Union[str, None]:
|
|
|
164
173
|
|
|
165
174
|
# We check for new version asynchronously, so that it doesn't slow down the program.
|
|
166
175
|
# If the main process exits, the check_version process will also exit.
|
|
167
|
-
|
|
168
|
-
|
|
176
|
+
if check:
|
|
177
|
+
process = multiprocessing.Process(target=check_version, daemon=True)
|
|
178
|
+
process.start()
|
|
169
179
|
version_file = data_dir.joinpath("version")
|
|
170
180
|
|
|
171
181
|
try:
|
|
@@ -178,7 +188,9 @@ def check_for_updates(current_version) -> Union[str, None]:
|
|
|
178
188
|
return None
|
|
179
189
|
|
|
180
190
|
try:
|
|
181
|
-
if
|
|
191
|
+
if not is_dev(version) and parse_version(version) > parse_version(current_version):
|
|
192
|
+
return version
|
|
193
|
+
if is_dev(current_version) and is_dev(version) and parse_version(version) > parse_version(current_version):
|
|
182
194
|
return version
|
|
183
195
|
else:
|
|
184
196
|
return None
|
|
@@ -217,26 +229,6 @@ def check_version():
|
|
|
217
229
|
pass
|
|
218
230
|
|
|
219
231
|
|
|
220
|
-
def compare_versions(version_a, version_b):
|
|
221
|
-
"""
|
|
222
|
-
Function to compare two versions.
|
|
223
|
-
Returns 1 if version_a > version_b, 0 if version_a == version_b, -1 if version_a < version_b.
|
|
224
|
-
"""
|
|
225
|
-
|
|
226
|
-
def convert(version):
|
|
227
|
-
return tuple(map(int, version.split(".")))
|
|
228
|
-
|
|
229
|
-
version_a = convert(version_a)
|
|
230
|
-
version_b = convert(version_b)
|
|
231
|
-
|
|
232
|
-
if version_a > version_b:
|
|
233
|
-
return 1
|
|
234
|
-
elif version_a == version_b:
|
|
235
|
-
return 0
|
|
236
|
-
else:
|
|
237
|
-
return -1
|
|
238
|
-
|
|
239
|
-
|
|
240
232
|
def lines_diff(lines1, lines2):
|
|
241
233
|
"""
|
|
242
234
|
Function to compare two lists of lines.
|
|
@@ -441,3 +433,7 @@ def exit_with_error(text, func=None):
|
|
|
441
433
|
except TypeError:
|
|
442
434
|
pass
|
|
443
435
|
exit(1)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def has_sanitizer_error(output, exit_code):
|
|
439
|
+
return ('ELF_ET_DYN_BASE' in output or 'ASan' in output) and exit_code != 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sinol_make
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0.dev2
|
|
4
4
|
Summary: CLI tool for creating sio2 task packages
|
|
5
5
|
Author: Mateusz Masiarz
|
|
6
6
|
Author-email: m.masiarz@fri.edu.pl
|
|
@@ -21,6 +21,7 @@ Requires-Dist: PyYAML
|
|
|
21
21
|
Requires-Dist: dictdiffer
|
|
22
22
|
Requires-Dist: importlib-resources
|
|
23
23
|
Requires-Dist: psutil
|
|
24
|
+
Requires-Dist: packaging
|
|
24
25
|
Provides-Extra: tests
|
|
25
26
|
Requires-Dist: pytest; extra == "tests"
|
|
26
27
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
@@ -21,6 +21,7 @@ src/sinol_make/commands/inwer/inwer_util.py
|
|
|
21
21
|
src/sinol_make/commands/outgen/__init__.py
|
|
22
22
|
src/sinol_make/commands/outgen/outgen_util.py
|
|
23
23
|
src/sinol_make/commands/run/__init__.py
|
|
24
|
+
src/sinol_make/commands/verify/__init__.py
|
|
24
25
|
src/sinol_make/contest_types/__init__.py
|
|
25
26
|
src/sinol_make/contest_types/default.py
|
|
26
27
|
src/sinol_make/contest_types/icpc.py
|
|
@@ -21,7 +21,7 @@ def test_simple_package(create_package):
|
|
|
21
21
|
|
|
22
22
|
@pytest.mark.parametrize("create_package", [util.get_inwer_package_path()], indirect=True)
|
|
23
23
|
def test_inwer_package(create_package):
|
|
24
|
-
run("sinol-make
|
|
24
|
+
run("sinol-make gen inwer run")
|
|
25
25
|
run("sinol-make ingen prog/weringen.cpp inwer prog/werinwer.cpp --tests wer1a.in run --tests wer2a.in")
|
|
26
26
|
run("sinol-make ingen inwer run export --no-statement")
|
|
27
27
|
|
|
@@ -44,28 +44,6 @@ def test_file_diff():
|
|
|
44
44
|
assert util.file_diff(a_file, b_file) is False
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def test_compare_versions():
|
|
48
|
-
"""
|
|
49
|
-
Tests for compare_versions function
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
assert util.compare_versions('1.0.0', '1.0.0') == 0
|
|
53
|
-
assert util.compare_versions('1.0.0', '1.0.1') == -1
|
|
54
|
-
assert util.compare_versions('1.0.1', '1.0.0') == 1
|
|
55
|
-
assert util.compare_versions('1.0.0', '1.1.0') == -1
|
|
56
|
-
assert util.compare_versions('1.1.0', '1.0.0') == 1
|
|
57
|
-
assert util.compare_versions('1.0.0', '2.0.0') == -1
|
|
58
|
-
assert util.compare_versions('2.0.0', '1.0.0') == 1
|
|
59
|
-
with pytest.raises(ValueError):
|
|
60
|
-
util.compare_versions('1.0.0', '')
|
|
61
|
-
with pytest.raises(ValueError):
|
|
62
|
-
util.compare_versions('', '1.0.0')
|
|
63
|
-
with pytest.raises(ValueError):
|
|
64
|
-
util.compare_versions('1.0.0', 'abc')
|
|
65
|
-
with pytest.raises(ValueError):
|
|
66
|
-
util.compare_versions('abc', '1.0.0')
|
|
67
|
-
|
|
68
|
-
|
|
69
47
|
@requests_mock.Mocker(kw="mocker")
|
|
70
48
|
def test_check_version(**kwargs):
|
|
71
49
|
"""
|
|
@@ -74,6 +52,29 @@ def test_check_version(**kwargs):
|
|
|
74
52
|
"""
|
|
75
53
|
mocker = kwargs["mocker"]
|
|
76
54
|
|
|
55
|
+
mocker.get("https://pypi.python.org/pypi/sinol-make/json", json={"info": {"version": "1.0.0.dev2"}})
|
|
56
|
+
util.check_version()
|
|
57
|
+
version = util.check_for_updates("1.0.0.dev1", False)
|
|
58
|
+
assert version == "1.0.0.dev2"
|
|
59
|
+
assert util.is_dev(version)
|
|
60
|
+
|
|
61
|
+
mocker.get("https://pypi.python.org/pypi/sinol-make/json", json={"info": {"version": "1.0.0"}})
|
|
62
|
+
util.check_version()
|
|
63
|
+
version = util.check_for_updates("1.0.0.dev1", False)
|
|
64
|
+
assert version == "1.0.0"
|
|
65
|
+
assert not util.is_dev(version)
|
|
66
|
+
|
|
67
|
+
mocker.get("https://pypi.python.org/pypi/sinol-make/json", json={"info": {"version": "2.0.0.dev1"}})
|
|
68
|
+
util.check_version()
|
|
69
|
+
version = util.check_for_updates("1.0.0", False)
|
|
70
|
+
assert version is None
|
|
71
|
+
|
|
72
|
+
mocker.get("https://pypi.python.org/pypi/sinol-make/json", json={"info": {"version": "1.0.1"}})
|
|
73
|
+
util.check_version()
|
|
74
|
+
version = util.check_for_updates("1.0.0", False)
|
|
75
|
+
assert version == "1.0.1"
|
|
76
|
+
assert not util.is_dev(version)
|
|
77
|
+
|
|
77
78
|
importlib = util.import_importlib_resources()
|
|
78
79
|
|
|
79
80
|
data_dir = importlib.files('sinol_make').joinpath("data")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|