diff --git a/example_package/config.yml b/example_package/config.yml index 4b34596a..6af5ff88 100644 --- a/example_package/config.yml +++ b/example_package/config.yml @@ -43,20 +43,20 @@ override_limits: # Each language can have different extra arguments. # extra_compilation_args: -# cpp: 'abclib.cpp' +# cpp: '__ID__lib.cpp' # The arguments can also be in an array: # extra_compilation_args: # cpp: -# - 'abclib.cpp' -# - 'abclib2.cpp' +# - '__ID__lib.cpp' +# - '__ID__lib2.cpp' # Additional files used in compilation can be defined in `extra_compilation_files` key. # They are copied to the directory where the source code is compiled. # All languages have the same additional files. -# extra_compilation_files: ['abclib.cpp', 'abclib.py'] +# extra_compilation_files: ['__ID__lib.cpp', '__ID__lib.py'] ### Keys used by sinol-make: @@ -65,7 +65,7 @@ override_limits: # 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_task_id: __ID__ # sinol-make can behave differently depending on the value of `sinol_contest_type` key. # Mainly, it affects how points are calculated. @@ -76,7 +76,7 @@ sinol_contest_type: oi # You can specify which tests are static (handwritten). This allows sinol-make to differentiate between # old and handwritten tests. If this key is not present old tests won't be removed. # This key is optional and should be a list of tests. -sinol_static_tests: ["abc0.in", "abc0a.in"] +sinol_static_tests: ["__ID__0.in", "__ID__0a.in"] # sinol-make can check if the solutions run as expected when using `run` command. # Key `sinol_expected_scores` defines expected scores for each solution on each tests. diff --git a/example_package/prog/abc.cpp b/example_package/prog/__ID__.cpp similarity index 100% rename from example_package/prog/abc.cpp rename to example_package/prog/__ID__.cpp diff --git a/example_package/prog/abcingen.cpp b/example_package/prog/__ID__ingen.cpp similarity index 86% rename from example_package/prog/abcingen.cpp rename to example_package/prog/__ID__ingen.cpp index b5a0f7db..8b5d2174 100644 --- a/example_package/prog/abcingen.cpp +++ b/example_package/prog/__ID__ingen.cpp @@ -3,7 +3,7 @@ using namespace std; // Change this function to generate one test for stresstesting. -// The script prog/abcingen.sh in 10 seconds generates +// The script prog/__ID__ingen.sh in 10 seconds generates // as much tests as possible and compares the outputs // of the model solution and brute solution. // The tests shouldn't be very big, but should be able to cover edge cases. @@ -12,7 +12,7 @@ void generate_one_stresstest(oi::Random &rng) { } // Change this function to create a test with the given name. -// The lists of tests to generate needs to be written in prog/abcingen.sh +// The lists of tests to generate needs to be written in prog/__ID__ingen.sh void generate_proper_test(string test_name, oi::Random &rng) { if (test_name == "0a") cout << "0 1" << endl; @@ -34,7 +34,7 @@ int main(int argc, char *argv[]) { return 0; } if (argc != 2) { - cerr << "Run prog/abcingen.sh to stresstest and create proper tests." << endl; + cerr << "Run prog/__ID__ingen.sh to stresstest and create proper tests." << endl; exit(1); } string test_name = argv[1]; diff --git a/example_package/prog/abcingen.sh b/example_package/prog/__ID__ingen.sh similarity index 100% rename from example_package/prog/abcingen.sh rename to example_package/prog/__ID__ingen.sh diff --git a/example_package/prog/abcinwer.cpp b/example_package/prog/__ID__inwer.sh similarity index 100% rename from example_package/prog/abcinwer.cpp rename to example_package/prog/__ID__inwer.sh diff --git a/example_package/prog/abcs.cpp b/example_package/prog/__ID__s.cpp similarity index 100% rename from example_package/prog/abcs.cpp rename to example_package/prog/__ID__s.cpp diff --git a/src/sinol_make/commands/init/__init__.py b/src/sinol_make/commands/init/__init__.py index 6b281078..51f6277b 100644 --- a/src/sinol_make/commands/init/__init__.py +++ b/src/sinol_make/commands/init/__init__.py @@ -13,6 +13,10 @@ class Command(BaseCommand): Class for "init" """ + TEMPLATE_ID='__ID__' + DEFAULT_TEMPLATE = 'https://github.com/sio2project/sinol-make.git' + DEFAULT_SUBDIR = 'example_package' + def get_name(self): return "init" @@ -23,21 +27,35 @@ def configure_subparser(self, subparser: argparse.ArgumentParser): description='Create package from predefined template with given id.' ) parser.add_argument('task_id', type=str, help='id of the task to create') - parser.add_argument('directory', type=str, nargs='?', + parser.add_argument('-o', '--output', type=str, help='destination directory to copy the template into, defaults to task_id') parser.add_argument('-f', '--force', action='store_true', help='overwrite files in destination directory if they already exist') + parser.add_argument('-t', '--template', nargs='+', default=[self.DEFAULT_TEMPLATE, self.DEFAULT_SUBDIR], + help='specify template repository or directory, optionally subdirectory after space' + f' (default: {self.DEFAULT_TEMPLATE} {self.DEFAULT_SUBDIR})') + parser.add_argument('-v', '--verbose', action='store_true') return parser def download_template(self): - repo = 'https://github.com/sio2project/sinol-make.git' - package_dir = 'sinol-make/example_package' + template = self.template[0] + subdir = self.template[1] if len(self.template) > 1 else '' + self.used_tmpdir = tempfile.TemporaryDirectory() tmp_dir = self.used_tmpdir.name - ret = subprocess.run(['git', 'clone', '-q', '--depth', '1', repo], cwd=tmp_dir) - if ret.returncode != 0: - util.exit_with_error("Could not access repository. Please try again.") - path = os.path.join(tmp_dir, package_dir) + + is_url = template.startswith("https://") or template.startswith("git@") + print(('Cloning' if is_url else 'Copying') + ' template ' + + (f'{subdir} from {template}' if subdir else f'{template}')) + if is_url: + ret = subprocess.run(['git', 'clone', '-v' if self.verbose else '-q', '--depth', '1', template, tmp_dir]) + if ret.returncode != 0: + util.exit_with_error("Could not access repository. Please try again.") + path = os.path.join(tmp_dir, subdir) + else: + path = os.path.join(tmp_dir, 'template') + shutil.copytree(os.path.join(os.getcwd(), template, subdir), path) + if os.path.exists(os.path.join(path, '.git')): shutil.rmtree(os.path.join(path, '.git')) return path @@ -55,22 +73,31 @@ def move_folder(self): raise for file in files: dest_filename = file - if file[:3] == 'abc': - dest_filename = self.task_id + file[3:] + if file[:len(self.TEMPLATE_ID)] == self.TEMPLATE_ID: + dest_filename = self.task_id + file[len(self.TEMPLATE_ID):] shutil.move(os.path.join(root, file), os.path.join(mapping[root], dest_filename)) - def update_config(self): - with open(os.path.join(os.getcwd(), 'config.yml')) as config: - config_data = config.read() - config_data = config_data.replace('sinol_task_id: abc', f'sinol_task_id: {self.task_id}') + def update_task_id(self): + for root, dirs, files in os.walk(os.getcwd()): + for file in files: + path = os.path.join(os.getcwd(), root, file) + with open(path) as file: + try: + file_data = file.read() + except UnicodeDecodeError: + # ignore non-text files + continue + file_data = file_data.replace(self.TEMPLATE_ID, self.task_id) - with open(os.path.join(os.getcwd(), 'config.yml'), 'w') as config: - config.write(config_data) + with open(path, 'w') as file: + file.write(file_data) def run(self, args: argparse.Namespace): self.task_id = args.task_id self.force = args.force - destination = args.directory or self.task_id + self.template = args.template + self.verbose = args.verbose + destination = args.output or self.task_id if not os.path.isabs(destination): destination = os.path.join(os.getcwd(), destination) try: @@ -80,13 +107,17 @@ def run(self, args: argparse.Namespace): util.exit_with_error(f"Destination {destination} already exists. " f"Provide a different task id or directory name, " f"or use the --force flag to overwrite.") - os.chdir(destination) + try: + self.template_dir = self.download_template() - self.template_dir = self.download_template() + os.chdir(destination) - self.move_folder() - self.update_config() + self.move_folder() + self.update_task_id() - self.used_tmpdir.cleanup() + self.used_tmpdir.cleanup() - print(util.info(f'Successfully created task "{self.task_id}"')) + print(util.info(f'Successfully created task "{self.task_id}"')) + except: + shutil.rmtree(destination) + raise