From 3e641d8cad8413f242624edfb88bd4ac3ea1e4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Czech?= Date: Mon, 26 Feb 2024 19:48:11 +0100 Subject: [PATCH 1/5] Update statement compilation (#197) * Pdflatex compilation * Additional tests * Requested description --- src/sinol_make/commands/doc/__init__.py | 44 +++++++++++++++++--- tests/commands/doc/test_integration.py | 52 ++++++++++++++++++++++++ tests/commands/doc/test_unit.py | 9 +++- tests/packages/doc/doc/doctest.tex | 5 +++ tests/packages/doc/doc/test_image.png | Bin 0 -> 15543 bytes tests/packages/ps_doc/config.yml | 4 ++ tests/packages/ps_doc/doc/doctest.tex | 5 +++ tests/packages/ps_doc/doc/doczad.tex | 4 ++ tests/packages/ps_doc/doc/test_image.ps | Bin 0 -> 110228 bytes tests/packages/ps_doc/in/.gitkeep | 0 tests/packages/ps_doc/out/.gitkeep | 0 tests/util.py | 7 ++++ 12 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 tests/packages/doc/doc/doctest.tex create mode 100644 tests/packages/doc/doc/test_image.png create mode 100644 tests/packages/ps_doc/config.yml create mode 100644 tests/packages/ps_doc/doc/doctest.tex create mode 100644 tests/packages/ps_doc/doc/doczad.tex create mode 100644 tests/packages/ps_doc/doc/test_image.ps create mode 100644 tests/packages/ps_doc/in/.gitkeep create mode 100644 tests/packages/ps_doc/out/.gitkeep diff --git a/src/sinol_make/commands/doc/__init__.py b/src/sinol_make/commands/doc/__init__.py index d495853d..3d624bed 100644 --- a/src/sinol_make/commands/doc/__init__.py +++ b/src/sinol_make/commands/doc/__init__.py @@ -17,8 +17,8 @@ class Command(BaseCommand): def get_name(self): return "doc" - def compile_file(self, file_path): - print(util.info(f'Compiling {os.path.basename(file_path)}...')) + def compile_file_latex_div(self, file_path): + print(util.info(f'Compiling {os.path.basename(file_path)} (latex to dvi)...')) os.chdir(os.path.dirname(file_path)) subprocess.run(['latex', file_path]) dvi_file = os.path.splitext(file_path)[0] + '.dvi' @@ -34,13 +34,27 @@ def compile_file(self, file_path): print(util.info(f'Compilation successful for file {os.path.basename(file_path)}.')) return True + def compile_pdf_latex(self, file_path): + print(util.info(f'Compiling {os.path.basename(file_path)} (pdflatex)...')) + os.chdir(os.path.dirname(file_path)) + subprocess.run(['pdflatex', file_path]) + pdf_file = os.path.splitext(file_path)[0] + '.pdf' + pdf_file_path = os.path.join(os.path.dirname(file_path), pdf_file) + if not os.path.exists(pdf_file_path): + print(util.error('Compilation failed.')) + return False + return True + def make_file(self, file_path): """ Compile the file two times to get the references right. """ - if not self.compile_file(file_path): - return False - return self.compile_file(file_path) + if self.compilation_method == 'pdflatex': + return self.compile_pdf_latex(file_path) + else: + if not self.compile_file_latex_div(file_path): + return False + return self.compile_file_latex_div(file_path) def move_logs(self): output_dir = paths.get_cache_path('doc_logs') @@ -56,11 +70,31 @@ def configure_subparser(self, subparser: argparse.ArgumentParser): help='Compile latex files to pdf', description='Compiles latex files to pdf. By default compiles all files in the `doc` directory.\n' 'You can also specify files to compile.') + parser.add_argument('--latex-compiler', dest='latex_compiler', choices=['auto', 'pdflatex', 'latex_dvi'], + help='Compiler used to compile documents. Available options:\n' + ' auto - uses the compiler based on the image types (default option).\n' + ' pdflatex - uses pdflatex. Works with .png and .jpg images.\n' + ' latex_dvi - uses latex and dvipdf. Works with .ps and .eps images.', default='auto') parser.add_argument('files', type=str, nargs='*', help='files to compile') def run(self, args: argparse.Namespace): args = util.init_package_command(args) + if not hasattr(args, 'latex_compiler'): + args.latex_compiler = 'auto' + + if args.latex_compiler == 'pdflatex': + self.compilation_method = 'pdflatex' + elif args.latex_compiler == 'latex_dvi': + self.compilation_method = 'latex_dvi' + elif args.latex_compiler == 'auto': + self.compilation_method = 'pdflatex' + for extension in ['ps', 'eps']: + if glob.glob(os.path.join(os.getcwd(), 'doc', f'*.{extension}')) != []: + self.compilation_method = 'latex_dvi' + else: + util.exit_with_error("Unrecognized latex compiler") + if args.files == []: self.files = glob.glob(os.path.join(os.getcwd(), 'doc', '*.tex')) else: diff --git a/tests/commands/doc/test_integration.py b/tests/commands/doc/test_integration.py index e3e9d2e5..fcf057ea 100644 --- a/tests/commands/doc/test_integration.py +++ b/tests/commands/doc/test_integration.py @@ -36,6 +36,7 @@ def test_argument(capsys, create_package): command.run(args) out = capsys.readouterr().out assert "Compilation was successful for all files." in out + assert "pdflatex" in out # In auto mode this command will use pdflatex logs_exist = False logs_dir = paths.get_cache_path('doc_logs') @@ -43,3 +44,54 @@ def test_argument(capsys, create_package): assert glob.glob(os.path.join(os.getcwd(), 'doc', pattern)) == [] logs_exist = logs_exist | (glob.glob(os.path.join(logs_dir, pattern)) != []) assert logs_exist + +def run_doc(capsys, command_args, expected, not_expected): + """ + Run doc command + """ + parser = configure_parsers() + args = parser.parse_args(command_args) + command = Command() + command.run(args) + out = capsys.readouterr().out + assert "Compilation was successful for all files." in out + assert expected in out + assert not_expected not in out + +@pytest.mark.parametrize("create_package", [util.get_ps_doc_package_path()], indirect=True) +def test_ps_images(capsys, create_package): + """ + Test `doc` command with ps images. + """ + run_doc( + capsys=capsys, + command_args=["doc"], + expected="latex to dvi", # In auto mode this command should use latex and dvipdf + not_expected="pdflatex" # and shouldn't use pdflatex for any compilation + ) + + +@pytest.mark.parametrize("create_package", [util.get_ps_doc_package_path()], indirect=True) +def test_compilation_mode(capsys, create_package): + """ + Test `doc` with compilation mode directly specified. + """ + run_doc( + capsys=capsys, + command_args=["doc", "doc/doczad.tex", "--latex-compiler", "pdflatex"], + expected="pdflatex", + not_expected="latex to dvi" + ) + + +@pytest.mark.parametrize("create_package", [util.get_doc_package_path()], indirect=True) +def test_compilation_mode_2(capsys, create_package): + """ + Test `doc` with compilation mode directly specified. + """ + run_doc( + capsys=capsys, + command_args=["doc", "doc/doczad.tex", "--latex-compiler", "latex_dvi"], + expected="latex to dvi", + not_expected="pdflatex" + ) diff --git a/tests/commands/doc/test_unit.py b/tests/commands/doc/test_unit.py index d4f4545c..aae11708 100644 --- a/tests/commands/doc/test_unit.py +++ b/tests/commands/doc/test_unit.py @@ -7,6 +7,11 @@ @pytest.mark.parametrize("create_package", [util.get_doc_package_path()], indirect=True) -def test_compile_file(create_package): +def test_compile_pdf_latex(create_package): command = Command() - assert command.compile_file(os.path.abspath(os.path.join(os.getcwd(), "doc/doczad.tex"))) is True + assert command.compile_pdf_latex(os.path.abspath(os.path.join(os.getcwd(), "doc/doczad.tex"))) is True + +@pytest.mark.parametrize("create_package", [util.get_doc_package_path()], indirect=True) +def test_compile_file_latex_div(create_package): + command = Command() + assert command.compile_file_latex_div(os.path.abspath(os.path.join(os.getcwd(), "doc/doczad.tex"))) is True diff --git a/tests/packages/doc/doc/doctest.tex b/tests/packages/doc/doc/doctest.tex new file mode 100644 index 00000000..393fd875 --- /dev/null +++ b/tests/packages/doc/doc/doctest.tex @@ -0,0 +1,5 @@ +\documentclass{article} +\usepackage{graphicx} +\begin{document} +\includegraphics{test_image.png} +\end{document} diff --git a/tests/packages/doc/doc/test_image.png b/tests/packages/doc/doc/test_image.png new file mode 100644 index 0000000000000000000000000000000000000000..0e90117b672da7a4bc781d669e51ba79f377fd0c GIT binary patch literal 15543 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 >w}K4h1=%I&f9IPXOrCJg@|+z17@91t0=L7NGq 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 Date: Mon, 26 Feb 2024 19:54:45 +0100 Subject: [PATCH 2/5] Update readme to comply with markdownlint (#199) Also remove broken link and adjust the headers. Co-authored-by: Olaf Targowski --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 901a260d..b22d583a 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ -# sinol-make +# ![Logo](https://avatars.githubusercontent.com/u/2264918?s=60&v=4) sinol-make `sinol-make` is a CLI tool for creating and verifying problem packages for [sio2](https://github.com/sio2project/oioioi) with features such as: + - measuring time and memory in the same deterministic way as sio2, - running the solutions in parallel, - keeping a git-friendly report of solutions' scores, - catching mistakes in the problem packages as early as possible, - and more. -# Contents +## Contents - [Why?](#why) - [Installation](#installation) - [Usage](#usage) -- [Configuarion](#configuration) - [Reporting bugs and contributing code](#reporting-bugs-and-contributing-code) ### Why? @@ -34,9 +34,10 @@ solutions' runtime, called `oiejq`. It's possible to directly install [sinol-make](https://pypi.org/project/sinol-make/) through Python's package manager pip, which usually is installed alongside Python: -``` +```bash pip3 install sinol-make ``` + `pip` installs the `sinol-make` executable in `~/.local/bin/` directory, so make sure this directory is in your `PATH`. [Here's](https://gist.github.com/nex3/c395b2f8fd4b02068be37c961301caa7) how to add a directory to `PATH`. @@ -46,11 +47,12 @@ As `oiejq` works only on Linux-based operating systems, Nevertheless `sinol-make` supports those operating systems, though there are additional installation steps required to use other tools for measuring time (which are non-deterministic and produce reports different from sio2): + - Debian-based systems (Ubuntu, usually Windows WSL): `apt install time` - Arch-based systems: `pacman -S time` - macOS: `brew install gnu-time coreutils` -### Autocompletion (optional) +#### Autocompletion (optional) If you would like to have autocompletion for `sinol-make` commands, run the following command and refresh the shell (e.g. by opening a new terminal): @@ -81,6 +83,7 @@ Run `sinol-make ingen --help` to see available flags. program to use, what tests to check and how many CPUs to use. Run `sinol-make inwer --help` to see available flags. - `sinol-make export` -- Creates archive ready to upload to sio2 or szkopul. Run `sinol-make export --help` to see all available flags. - `sinol-make doc` -- Compiles all LaTeX files in doc/ directory to PDF. Run `sinol-make doc --help` to see all available flags. +- `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. ### Reporting bugs and contributing code From cbf6a08bb6f18cbeb2b4377f5521875c94d5dc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Czech?= Date: Tue, 27 Feb 2024 08:13:40 +0100 Subject: [PATCH 3/5] Better indicate availability of new version. (#198) * Better indicate availability of new version. Comply with PEP 8: E501 and PEP 8: E722 * Move new version notification. Move code for checking and installing oiejq to separate function --- src/sinol_make/__init__.py | 56 ++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/sinol_make/__init__.py b/src/sinol_make/__init__.py index 33997737..54254a4b 100644 --- a/src/sinol_make/__init__.py +++ b/src/sinol_make/__init__.py @@ -1,14 +1,12 @@ # PYTHON_ARGCOMPLETE_OK +import argparse +import traceback +from time import sleep import argcomplete -import traceback -import argparse -import sys -import os from sinol_make import util, oiejq - __version__ = "1.5.26" @@ -34,6 +32,21 @@ def configure_parsers(): return parser +def check_oiejq(): + if util.is_linux() and not oiejq.check_oiejq(): + print(util.warning('`oiejq` in `~/.local/bin/` not found, installing now...')) + try: + if oiejq.install_oiejq(): + print(util.info('`oiejq` was successfully installed.')) + else: + util.exit_with_error('`oiejq` could not be installed.\n' + 'You can download it from https://oij.edu.pl/zawodnik/srodowisko/oiejq.tar.gz, ' + 'unpack it to `~/.local/bin/` and rename oiejq.sh to oiejq.\n' + 'You can also use --oiejq-path to specify path to your oiejq.') + except Exception as err: + util.exit_with_error('`oiejq` could not be installed.\n' + err) + + def main_exn(): parser = configure_parsers() args = parser.parse_args() @@ -41,26 +54,7 @@ def main_exn(): for command in commands: if command.get_name() == args.command: - new_version = util.check_for_updates(__version__) - if new_version is not None: - print(util.warning( - f'New version of sinol-make is available (your version: {__version__}, available version: {new_version}).\n' - f' You can update it by running `pip3 install sinol-make --upgrade`.')) - - if util.is_linux() and not oiejq.check_oiejq(): - print(util.warning('`oiejq` in `~/.local/bin/` not found, installing now...')) - - try: - if oiejq.install_oiejq(): - print(util.info('`oiejq` was successfully installed.')) - else: - util.exit_with_error('`oiejq` could not be installed.\n' - 'You can download it from https://oij.edu.pl/zawodnik/srodowisko/oiejq.tar.gz' - ', unpack it to `~/.local/bin/` and rename oiejq.sh to oiejq.\n' - 'You can also use --oiejq-path to specify path to your oiejq.') - except Exception as err: - util.exit_with_error('`oiejq` could not be installed.\n' + err) - + check_oiejq() command.run(args) exit(0) @@ -68,13 +62,21 @@ def main_exn(): def main(): + new_version = None try: + new_version = util.check_for_updates(__version__) main_exn() except argparse.ArgumentError as err: util.exit_with_error(err) except SystemExit as err: exit(err.code) - except: + except Exception: print(traceback.format_exc()) util.exit_with_error('An error occurred while running the command.\n' - 'If that is a bug, please report it or submit a bugfix: https://github.com/sio2project/sinol-make/#reporting-bugs-and-contributing-code') + 'If that is a bug, please report it or submit a bugfix: ' + 'https://github.com/sio2project/sinol-make/#reporting-bugs-and-contributing-code') + finally: + if new_version is not None: + print(util.warning( + f'New version of sinol-make is available (your version: {__version__}, available version: ' + f'{new_version}).\nYou can update it by running `pip3 install sinol-make --upgrade`.')) From b3d72fa68f874ec7b9abecac1a15d875283be02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Czech?= Date: Wed, 28 Feb 2024 16:55:24 +0100 Subject: [PATCH 4/5] Version bump --- src/sinol_make/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sinol_make/__init__.py b/src/sinol_make/__init__.py index 55079849..636805de 100644 --- a/src/sinol_make/__init__.py +++ b/src/sinol_make/__init__.py @@ -9,7 +9,7 @@ from sinol_make import util, oiejq -__version__ = "0.0.1" +__version__ = "0.0.2" def configure_parsers(): From 8dfcd34df4d0c2571e1c53951a9608da18b76b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Czech?= Date: Wed, 28 Feb 2024 20:25:56 +0100 Subject: [PATCH 5/5] Fix readme --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4d74c090..6c641f9e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# ![Logo](https://avatars.githubusercontent.com/u/93839068?s=60&v=4) sinol-make +# ![Logo](https://avatars.githubusercontent.com/u/93839068?s=60&v=4) st-make -`sinol-make` is a CLI tool for creating and verifying problem packages +`st-make` is a CLI tool for creating and verifying problem packages for [sio2](https://github.com/sio2project/oioioi) with features such as: @@ -10,9 +10,10 @@ with features such as: - catching mistakes in the problem packages as early as possible, - and more. -## Contents This tool is a fork of [sinol-make](https://github.com/sio2project/sinol-make), with features specific to [Talent](https://talent.edu.pl/) contests. +## Contents + - [Why?](#why) - [Installation](#installation) - [Usage](#usage) @@ -82,7 +83,7 @@ Run `st-make ingen --help` to see available flags. - `st-make outgen` -- Generate output files using the model solutions. Run `st-make outgen --help` to see available flags. - `st-make inwer` -- Verifies whether input files are correct using your "inwer.cpp" program. You can specify what inwer program to use, what tests to check and how many CPUs to use. Run `st-make inwer --help` to see available flags. -- `st-make export` -- Creates archive ready to upload to wyzwania or oboz. Run `st-make export --help` to see all available flags. +- `st-make export` -- Creates archive ready to upload to Wyzwania, Talent-camp and other sio2 instances. Run `st-make export --help` to see all available flags. - `st-make doc` -- Compiles all LaTeX files in doc/ directory to PDF. Run `st-make doc --help` to see all available flags. - `st-make init [id]` -- Creates package from template [on github](https://github.com/Stowarzyszenie-Talent/st-make/tree/main/example_package) and sets task id to provided `[id]`. Requires an internet connection to run.