CLion + Rust(Cargo) での競プロ構成

Rustによる競プロで、IDEとしてCLionを使った場合のファイル構成をどうするか。

やりたいこと

  • 全てのコンテストの問題を1つのIDEプロジェクトで管理したい
  • 任意の.rsファイルについて、
    • コード補完が効くよう、ビルド構成に入れられるようにしたい
    • そこを起点として簡単にビルド+実行したい
  • 解いた問題のコードは残しておきたい
  • 解いている問題のコード以外(ビルド設定ファイルなど)は、頻繁には編集せずに済むようにしたい

まぁ、管理といっても main.rs から常に全てのファイルがincludeされて繋がっている必要は無くて、必要に応じて加えられるようにプロジェクト下にソースが置いてあればよい。

コンテスト前に、その一連の問題をビルド構成に入れるなど、ある程度まとまった単位での事前準備は許容する。

そもそもの背景とか

JetBrains社は様々なプログラミング言語の統合開発環境(IDE)ソフトウェアを提供しているが、 Rustで開発するには、C/C++用IDEであるCLionに、Rustプラグインを追加するのが最も親和性が高い。

IDEを用いての競技プログラミングは、コード補完が強力だったりデバッグが簡易に行えたりと様々なメリットがある一方、 ビルドツール(Rustの場合Cargo)を使うことがある程度前提となっており、構成をちゃんとしないと恩恵を十分に得られないことがある。

安易に凝ったことしようとすると「ビルドツールの流儀」と「IDEがそれを使う際の流儀」を両方調べる沼にずぶずぶハマっていくので、 なるたけチュートリアル等、公式が「こうしなはれ」と言っていることに添いたい。


さて、競プロコンテスト用プロジェクト作成の流れの一例が以下に紹介されている。

これは問題1問毎にCargoプロジェクトを作成する方法であり、通常はもちろんこれを参考に従えばよい。

ただ、IDEでは、1つの「IDEプロジェクト」で、複数の「Cargoプロジェクト」を管理することを許容するかという問題がある。

どういうことかというと、IDEを使う場合は(少なくともCLionは)基本的に「IDEのプロジェクト」を作成すると、 1つの「Cargoプロジェクト」がそのままぽんと出来上がる(あらかじめinitしてくれてる)。

つまり、流儀的には、1つのIDEのプロジェクトで1つのCargoプロジェクトを管理する前提である。

この下で、さらにサブディレクトリにCargoプロジェクトを作ると、それに起因する問題が発生したときに解決策を追いづらいという懸念がある。 (まぁ、よほど変なことをしない限り、Cargoを使う分には大丈夫と思うが……)

許容する場合、上記の解説ページの流れに添えばよい。

許容しない場合、Cargoプロジェクト単位で管理していくことになるが、 かといって、素直に1問毎に1プロジェクトを作成していたら、以下のような問題が発生する。

  • 問題の切り替えに時間がかかる
    • プロジェクト毎にプロセスが別になるので、切り替えるたびに、IDEプロジェクト設定の読み込み、補完するための関係性ツリーの構築、などが走る
  • プロジェクトをまたいだコード管理・検索がIDE内でやりにくくなる
  • IDEの設定ファイルが肥大化する

従って、なるべく競プロ用に使うIDEプロジェクト・Cargoプロジェクトは1つだけにしたい。という背景。

とりあえずはビルドして走らせればよしとする。(以下のように専用のテストツールとか使い出すと、また話は別になるだろう)

1つのCargoプロジェクトで管理する方法

main.rs を書き換え続ける

一番シンプル。cargo init で作成される素の構成のまま準備もほぼいらないのは楽。だが、解いたコードは残らない。

もちろん、競プロでは提出したコードはサーバ上に記録されでいつでも見られるが、手元で途中まで書いて提出せずに他の問題に移ったりは普通にある。

解き終わるたびにコピーしておく、もう一回それを実行したい場合はmain.rsにコピーする、などで対処は可能か。

src/bin 直下に入れる

Cargoのデフォルト設定では、src/main.rs が起点ファイルだが、src/bin フォルダを作成してその中に ***.rs を入れても、同時にコンパイル対象となる。

CLionのRustプラグインではこの仕様を勘案して、src/bin内のファイルをエディタで編集中に Shift+Alt+F10(起動構成の実行)すると、 ポップアップされる実行コマンドの選択肢の中に、そのファイルを起点としてビルド・実行するものが現れる。

  • あくまで src/bin 直下であり、フォルダ分けはできないので、沢山の問題の管理には向かない
  • 一度作成されたtarget内のexeは特に明示的に消さない限り残る
    • この方法の場合、作成されるexe名は問題毎に異なったものになるため、解いた問題数だけexeが残り続ける
    • 容量としてはわずかだが、気になる場合は定期的に削除する必要がある

Cargo.toml のbinセクションで設定する

Cargo.tomlは、[[bin]] セクションにより、起点となるファイルとバイナリ名を複数指定することができる。

[[bin]]
name = "binary_name"
path = "src/path/to/ignition.rs"

指定しておくと、ビルド時に cargo build –bin NAME とすることで、そのファイルを起点としたビルドになる。

そして、CLionのRustプラグインでは、この [[bin]] にて指定されたファイルをエディタで編集中に Shift+Alt+F10(起動構成の実行)すると、 ポップアップされる実行コマンドの選択肢の中に、そのファイルを起点としてビルド・実行するものが現れる。

よって、これから解こうとする問題をCargo.tomlに記述する必要は生じるが、 問題数だけ [[bin]] を用意しておけば、編集中のコードを簡単に実行できる。

ビルドされたバイナリは特に削除しない限りtargetフォルダ内に残り続けるので、なるべく共通した名前を使い回し、上書きされるようにする。 (デバッグビルドでは .pdb というファイルも生成されるため、あわせてだいたい1問につき2~3MB)

ファイル名とバイナリ名を個別に設定できるところも地味によい。 CLionはタブエディタなのでタブにファイル名が記載されるが、ここは“abc001_a.rs”などコンテスト名も同時に分かった方がありがたい。 一方、バイナリは上記の理由により、なるべく共有の名前を使い回したいので、“a.exe”などでよい。これを個別に設定できる。

もちろんこれは考え方次第で、たとえば過去のコードを実行するとき、バイナリの更新日時よりソースが古ければ新たにはコンパイルされないため、 一度何かしら編集してから実行する必要がある。 target内が肥大化することを気にしないなら、バイナリ名も固有にしておく方が、こういう一手間をなくせる面はある。

まぁ、今のところこれが(自分が見つけている中では)ベターかなあ。

Cargo.toml例

複数のCargoプロジェクトで管理する方法

IDEのプロジェクトとビルドツールのプロジェクトは基本、1対1対応とは言ったが、 Cargoは比較的、1つのIDEプロジェクト内にプロジェクトを複数作りやすい感じはする(ちゃんと分離しやすいように、ツールが構成されている)。

全てを1つのCargoで管理しようとすると大変でも、コンテスト単位など、少量の単位なら管理しやすくなる。

上記の src/bin に入れる方法でも、コンテスト単位など管理対象が小さければまとめて入れておいても全然問題にならないし、 [[bin]] セクションを作る方法も、設定をそのまま残しておける。

問題は、やはりtargetフォルダが別になることにより、使用するクレートのビルドが、プロジェクトの初回コンパイルのたびに走ってしまうこと。

これは、基本的にtargetディレクトリの位置は個別に変更できないっぽい(グローバルに~/.cargo/configで変更する方法はあるが)ので、 シンボリックリンクなどで解決するしかない。

(例)
> mklink /d src/abc/abc001/target target

また、1つのIDEプロジェクト内に存在するCargoプロジェクトが多くなってきたときのパフォーマンスへの影響も未知数ではある。

比較まとめ

src/bin Cargo.toml [[bin]]
準備 ちょっと手間(自動化は可能)
フォルダ分け 不可
複数プロジェクトに分けるなら多少緩和
ファイル名とバイナリ名 (シンプルを維持するなら)同名 異なる名前可

C/C++では

以下の情報がある

また、使ったことないので使用感は不明だが、以下のプラグインが、1ファイルだけ実行するのに使えそう

software/intellij/clion/procon.txt · 最終更新: 2020/04/24 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0