Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ Requirements:
- `g++-15` (for AtCoder-compatible compilation)
- `uv` is optional; `setup.sh` falls back to `venv` and `pip`
- `tree` is optional for `ly`
- Graphviz `dot` is optional for Graphviz output

Run the setup script from the repository root:

```
$ ./setup.sh
$ bash setup.sh
```

The script creates `.env` and installs the Python dependencies listed in `pyproject.toml`.
Expand All @@ -29,12 +28,27 @@ $ export PATH="$PWD/bin:$PATH"
You may want to put the corresponding absolute path in your shell configuration.

## Usage
Runs `hoge.cpp` (create and copy the base template if not created) by
Create, compile, and run `hoge.cpp` with:

```
$ ysn hoge
```

Pass the source path without the `.cpp` extension. If `hoge.cpp` does not
exist, `ysn` creates it from the base template. The compiled executable is
written to `hoge.exe`.

Compile without running, or force recompilation, with:

```
$ ysn hoge --just
$ ysn hoge --force
```

The short forms are `-j` and `-f`, respectively.

Install or remove templates as

```
$ ysn hoge --install modint1e9p7 --remove modint998244353
```
Expand All @@ -43,8 +57,14 @@ or briefly
$ ysn hoge -i modint1e9p7 -r modint998244353
```

(for debigging) you can see the dependency graph by
Template names are paths relative to `template/`; the `.hpp` extension is
optional. Dependencies included with `#include "..."` are expanded
recursively. Installing or removing templates only rewrites the source, so run
`ysn hoge` afterward to compile and execute it.

Search or list available templates with:

```
$ ysn hoge -i modint1e9p7 -r modint998244353 -d hoge.svg
$ lf modint
$ ly
```
(`.svg` can be replaced by `.pdf`, `.png` etc)
12 changes: 9 additions & 3 deletions bin/find.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@
ysn_path = Path(__file__).parent.parent
template_path = ysn_path / "template"

parser = argparse.ArgumentParser()
parser.add_argument("query", type=str, help="search query")
parser = argparse.ArgumentParser(
prog="lf",
description="Find templates by a case-sensitive substring of their path."
)
parser.add_argument(
"query",
metavar="QUERY",
help="substring to search for under template/",
)

args = parser.parse_args()
query = args.query
Expand All @@ -17,4 +24,3 @@
item = str(candidate_path.relative_to(template_path))
if query in item:
print(item)

2 changes: 1 addition & 1 deletion bin/lf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PROJ_DIR="$(dirname "$SCRIPT_DIR")"
VENV_ACTIVATE="$PROJ_DIR/.env/bin/activate"

if [ ! -f "$VENV_ACTIVATE" ]; then
printf 'Python environment is missing. Run ./setup.sh from %s first.\n' "$PROJ_DIR" >&2
printf 'Python environment is missing. Run bash setup.sh from %s first.\n' "$PROJ_DIR" >&2
exit 1
fi

Expand Down
11 changes: 11 additions & 0 deletions bin/ly
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJ_DIR="$(dirname "$SCRIPT_DIR")"
TEMPLATE_DIR="$PROJ_DIR/template"

if [ "$#" -gt 0 ]; then
if [ "$#" -eq 1 ] && { [ "$1" = "-h" ] || [ "$1" = "--help" ]; }; then
printf 'usage: ly\n\nList the template/ directory tree.\n'
exit 0
fi

printf 'ly: unexpected argument: %s\n' "$1" >&2
printf 'usage: ly\n' >&2
exit 2
fi

if ! command -v tree >/dev/null 2>&1; then
printf 'tree command is missing. Install tree to use ly.\n' >&2
exit 1
Expand Down
2 changes: 1 addition & 1 deletion bin/ysn
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ VENV_ACTIVATE="$PROJ_DIR/.env/bin/activate"

# Most paths still need Python, so fail early with a setup hint if the venv is missing.
if [ ! -f "$VENV_ACTIVATE" ]; then
printf 'Python environment is missing. Run ./setup.sh from %s first.\n' "$PROJ_DIR" >&2
printf 'Python environment is missing. Run bash setup.sh from %s first.\n' "$PROJ_DIR" >&2
exit 1
fi

Expand Down
40 changes: 32 additions & 8 deletions bin/ysn.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,38 @@ def normalize_module_name(module_name: str) -> str:

if __name__ == "__main__":

parser = argparse.ArgumentParser()

parser.add_argument("source_path", type=str, help="C++ source file to be templated")
parser.add_argument("--force", "-f", action="store_true", help="compile even though the source is not modified")
parser.add_argument("--just", "-j", action="store_true", help="just compile, not execute")
parser.add_argument("--install", "-i", type=str, nargs="*", help="install template if specified")
parser.add_argument("--remove", "-r", type=str, nargs="*", help="remove template if specified")
parser.add_argument("--dependency-graph", "-d", type=str, help="output dependency graph if specified")
parser = argparse.ArgumentParser(
prog="ysn",
description="Create, compile, run, and rewrite a templated C++ source.",
epilog=(
"Pass SOURCE without the .cpp extension (for example, 'ysn a' uses "
"a.cpp). --install and --remove only rewrite the source; run ysn "
"again without them to compile and execute. Use 'lf QUERY' to find "
"templates and 'ly' to list the template directory tree."
),
)

parser.add_argument(
"source_path",
metavar="SOURCE",
help="source path without the .cpp extension",
)
parser.add_argument(
"--force", "-f", action="store_true",
help="recompile even when the executable is newer than the source",
)
parser.add_argument(
"--just", "-j", action="store_true",
help="compile without executing",
)
parser.add_argument(
"--install", "-i", metavar="MODULE", nargs="*",
help="install templates (paths relative to template/, .hpp optional)",
)
parser.add_argument(
"--remove", "-r", metavar="MODULE", nargs="*",
help="remove installed templates (.hpp optional)",
)

args = parser.parse_args()

Expand Down
7 changes: 4 additions & 3 deletions documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ ysn <source>
```

指定した C++ ソースをコンパイルして実行します。
ソース名の `.cpp` は省略でき、たとえば `ysn a` は `a.cpp` を対象にします。
ソース名は `.cpp` を付けずに指定し、たとえば `ysn a` は `a.cpp` を対象にします。
ソースが存在しない場合は `bin/__base__.cpp` から新規作成し、テンプレート展開用の領域を初期化します。
実行ファイルはソース名に対応する `.exe` として作成されます。

Expand All @@ -66,6 +66,7 @@ ysn <source> -r <module...>
`--install` は `-i`、`--remove` は `-r` と省略できます。
モジュール名は `template/` からの相対パスで指定し、拡張子を省略した場合は `.hpp` として扱われます。
指定したモジュールが他のヘッダを `#include "..."` している場合、その依存も再帰的に展開されます。
追加・削除はソースの書き換えだけを行い、コンパイルや実行は行いません。書き換え後は改めて `ysn <source>` を実行します。

```text
ysn <source> --just
Expand All @@ -88,7 +89,7 @@ ly

### 内部構成

`bin/ysn` は `.env` の Python 仮想環境を有効化して `bin/ysn.py` を呼び出す薄いシェルラッパーです
`bin/ysn` は、更新済みの実行ファイルを直接起動できる場合は fast path を使い、それ以外では `.env` の Python 仮想環境を有効化して `bin/ysn.py` を呼び出すシェルラッパーです
`bin/ysn.py` は CLI 引数を解釈し、テンプレート編集が必要なら `engine.py`、コンパイル・実行が必要なら `run.py` に処理を渡します。

`bin/engine.py` はテンプレート展開の中核です。
Expand All @@ -106,7 +107,7 @@ ly
`setup.sh` は `.env` を作成し、`pyproject.toml` に記述された Python 依存を導入します。
`uv` が使える環境では `uv venv` と `uv pip install` を使い、なければ標準の `venv` と `pip` にフォールバックします。
セットアップ時には `.env/pch/` に `bits/stdc++.h` 用のローカル PCH も作成し、以後の C++ コンパイルで利用します。
`ly` や Graphviz 出力で使う `tree` と `dot` コマンドが見つからない場合は、追加で必要なシステムコマンドとして案内します。
`ly` で使う `tree` コマンドが見つからない場合は、追加で必要なシステムコマンドとして案内します。

C++ 側のテストは `CMakeLists.txt` と `vcpkg.json` で管理されます。
`vcpkg.json` では `gtest` を依存として宣言し、`test/` 以下の `.cpp` を GTest 実行ファイルとしてビルドします。
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ description = "Template tools for competitive programming"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"graphviz",
"networkx",
]
6 changes: 1 addition & 5 deletions setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ if ! command -v tree >/dev/null 2>&1; then
missing_commands+=("tree")
fi

if ! command -v dot >/dev/null 2>&1; then
missing_commands+=("graphviz")
fi

if [ "${#missing_commands[@]}" -ne 0 ]; then
printf 'Missing optional system command(s): %s\n' "${missing_commands[*]}"
printf 'Install them with your system package manager if you use ly or Graphviz output.\n'
printf 'Install them with your system package manager if you use ly.\n'
fi
Loading