33import stat
44import shutil
55import tarfile
6+ import tempfile
67import argparse
78import yaml
89
910from sinol_make import util
1011from sinol_make .commands .ingen .ingen_util import get_ingen , compile_ingen , run_ingen , ingen_exists
1112from sinol_make .helpers import package_util , parsers , paths
1213from sinol_make .interfaces .BaseCommand import BaseCommand
14+ from sinol_make .commands .outgen import Command as OutgenCommand , compile_correct_solution , get_correct_solution
1315
1416
1517class Command (BaseCommand ):
@@ -25,34 +27,91 @@ def configure_subparser(self, subparser: argparse.ArgumentParser):
2527 self .get_name (),
2628 help = 'Create archive for oioioi upload' ,
2729 description = 'Creates archive in the current directory ready to upload to sio2 or szkopul.' )
30+ parser .add_argument ('-c' , '--cpus' , type = int ,
31+ help = f'number of cpus to use to generate output files '
32+ f'(default: { util .default_cpu_count ()} )' ,
33+ default = util .default_cpu_count ())
2834 parsers .add_compilation_arguments (parser )
2935
30- def get_generated_tests (self ):
31- """
32- Returns list of generated tests.
33- Executes ingen to check what tests are generated.
34- """
35- if not ingen_exists (self .task_id ):
36- return []
37-
36+ def generate_input_tests (self ):
37+ print ('Generating tests...' )
3838 temp_package = paths .get_cache_path ('export' , 'tests' )
3939 if os .path .exists (temp_package ):
4040 shutil .rmtree (temp_package )
4141 os .makedirs (temp_package )
4242 in_dir = os .path .join (temp_package , 'in' )
43- prog_dir = os .path .join (temp_package , 'prog' )
4443 os .makedirs (in_dir )
45- shutil .copytree (os .path .join (os .getcwd (), 'prog' ), prog_dir )
44+ out_dir = os .path .join (temp_package , 'out' )
45+ os .makedirs (out_dir )
46+ prog_dir = os .path .join (temp_package , 'prog' )
47+ if os .path .exists (os .path .join (os .getcwd (), 'prog' )):
48+ shutil .copytree (os .path .join (os .getcwd (), 'prog' ), prog_dir )
49+
50+ if ingen_exists (self .task_id ):
51+ ingen_path = get_ingen (self .task_id )
52+ ingen_path = os .path .join (prog_dir , os .path .basename (ingen_path ))
53+ ingen_exe = compile_ingen (ingen_path , self .args , self .args .weak_compilation_flags )
54+ if not run_ingen (ingen_exe , in_dir ):
55+ util .exit_with_error ('Failed to run ingen.' )
56+
57+ def generate_output_files (self ):
58+ tests = paths .get_cache_path ('export' , 'tests' )
59+ in_dir = os .path .join (tests , 'in' )
60+ os .makedirs (in_dir , exist_ok = True )
61+ out_dir = os .path .join (tests , 'out' )
62+ os .makedirs (out_dir , exist_ok = True )
63+
64+ # Only example output tests are required for export.
65+ ocen_tests = glob .glob (os .path .join (in_dir , f'{ self .task_id } 0*.in' )) + \
66+ glob .glob (os .path .join (in_dir , f'{ self .task_id } *ocen.in' ))
67+ outputs = []
68+ for test in ocen_tests :
69+ outputs .append (os .path .join (out_dir , os .path .basename (test ).replace ('.in' , '.out' )))
70+ if len (outputs ) > 0 :
71+ outgen = OutgenCommand ()
72+ correct_solution_exe = compile_correct_solution (get_correct_solution (self .task_id ), self .args ,
73+ self .args .weak_compilation_flags )
74+ outgen .args = self .args
75+ outgen .correct_solution_exe = correct_solution_exe
76+ outgen .generate_outputs (outputs )
4677
47- ingen_path = get_ingen (self .task_id )
48- ingen_path = os .path .join (prog_dir , os .path .basename (ingen_path ))
49- ingen_exe = compile_ingen (ingen_path , self .args , self .args .weak_compilation_flags )
50- if not run_ingen (ingen_exe , in_dir ):
51- util .exit_with_error ('Failed to run ingen.' )
78+ def get_generated_tests (self ):
79+ """
80+ Returns list of generated tests.
81+ Executes ingen to check what tests are generated.
82+ """
83+ if not ingen_exists (self .task_id ):
84+ return []
5285
86+ in_dir = paths .get_cache_path ('export' , 'tests' , 'in' )
5387 tests = glob .glob (os .path .join (in_dir , f'{ self .task_id } *.in' ))
5488 return [package_util .extract_test_id (test , self .task_id ) for test in tests ]
5589
90+ def create_ocen (self , target_dir : str ):
91+ """
92+ Creates ocen archive for sio2.
93+ :param target_dir: Path to exported package.
94+ """
95+ attachments_dir = os .path .join (target_dir , 'attachments' )
96+ if not os .path .exists (attachments_dir ):
97+ os .makedirs (attachments_dir )
98+ tests_dir = paths .get_cache_path ('export' , 'tests' )
99+
100+ with tempfile .TemporaryDirectory () as tmpdir :
101+ ocen_dir = os .path .join (tmpdir , self .task_id )
102+ os .makedirs (ocen_dir )
103+ in_dir = os .path .join (ocen_dir , 'in' )
104+ os .makedirs (in_dir )
105+ out_dir = os .path .join (ocen_dir , 'out' )
106+ os .makedirs (out_dir )
107+ for ext in ['in' , 'out' ]:
108+ for test in glob .glob (os .path .join (tests_dir , ext , f'{ self .task_id } 0*.{ ext } ' )) + \
109+ glob .glob (os .path .join (tests_dir , ext , f'{ self .task_id } *ocen.{ ext } ' )):
110+ shutil .copy (test , os .path .join (ocen_dir , ext , os .path .basename (test )))
111+
112+ with tarfile .open (os .path .join (attachments_dir , f'{ self .task_id } ocen.tgz' ), "w:gz" ) as tar :
113+ tar .add (ocen_dir , arcname = os .path .basename (ocen_dir ))
114+
56115 def copy_package_required_files (self , target_dir : str ):
57116 """
58117 Copies package files and directories from
@@ -79,19 +138,24 @@ def copy_package_required_files(self, target_dir: str):
79138 for test in glob .glob (os .path .join (os .getcwd (), ext , f'{ self .task_id } 0*.{ ext } ' )):
80139 shutil .copy (test , os .path .join (target_dir , ext ))
81140
82- print ('Generating tests...' )
83141 generated_tests = self .get_generated_tests ()
84142 tests_to_copy = []
85143 for ext in ['in' , 'out' ]:
86144 for test in glob .glob (os .path .join (os .getcwd (), ext , f'{ self .task_id } *.{ ext } ' )):
87145 if package_util .extract_test_id (test , self .task_id ) not in generated_tests :
88146 tests_to_copy .append ((ext , test ))
89147
148+ cache_test_dir = paths .get_cache_path ('export' , 'tests' )
90149 if len (tests_to_copy ) > 0 :
91150 print (util .warning (f'Found { len (tests_to_copy )} tests that are not generated by ingen.' ))
92151 for test in tests_to_copy :
93152 print (util .warning (f'Copying { os .path .basename (test [1 ])} ...' ))
94153 shutil .copy (test [1 ], os .path .join (target_dir , test [0 ], os .path .basename (test [1 ])))
154+ shutil .copy (test [1 ], os .path .join (cache_test_dir , test [0 ], os .path .basename (test [1 ])))
155+
156+ self .generate_output_files ()
157+ print ('Generating ocen archive...' )
158+ self .create_ocen (target_dir )
95159
96160 def clear_files (self , target_dir : str ):
97161 """
@@ -112,6 +176,7 @@ def create_makefile_in(self, target_dir: str, config: dict):
112176 with open (os .path .join (target_dir , 'makefile.in' ), 'w' ) as f :
113177 cxx_flags = '-std=c++20'
114178 c_flags = '-std=gnu99'
179+
115180 def format_multiple_arguments (obj ):
116181 if isinstance (obj , str ):
117182 return obj
@@ -163,6 +228,7 @@ def run(self, args: argparse.Namespace):
163228 os .makedirs (export_package_path )
164229
165230 util .change_stack_size_to_unlimited ()
231+ self .generate_input_tests ()
166232 self .copy_package_required_files (export_package_path )
167233 self .clear_files (export_package_path )
168234 self .create_makefile_in (export_package_path , config )
0 commit comments