CLionで競技プログラミング用のディレクトリ構成&CMake作成
競技プログラミングをやるときは, シングルファイルで作成→コンパイル→実行って流れになるのでなんらかのテキストエディタで書いてターミナルで実行ってやってる人が多いと思う.
でもデバッグしたりとか補完を使って楽に書きたいとかでIDE使いたいときもある. そこで, 高機能な上に学生なら無料で使えるJetBrains製のC/C++用IDE, CLionを自分は使っている.
テキストエディタ&ターミナルと違い, IDEはプロジェクト単位でオプションなどの諸々の設定をするのが基本である. CLionの場合, その管理をCMakeというものを使ってやる. よって, CMakeにその設定を書く必要がある.
ということで, 競プロ用のCMakeの設定を書く. といっても, コンパイルオプションとかの設定はここでは説明しない.
説明するのは, どこに設定ファイルをおいてソースコードをどうインクルードするかである. なぜかというと, 普通のプロジェクトの場合は一つか二つのソースコードディレクトリしかないが, 競プロやるときはコンテスト毎に複数のソースコードディレクトリを作成することになり, それをどう管理するかが結構めんどくさいからである.
ちなみにインクルードしなくてもファイルを開くことはできるが, こんな感じで薄く表示されて補完も出ない.
これがインクルードされてると, ファイル名が濃く表示されて補完も効く.
IDEの便利な機能を使うために, なるべく簡単にうまく競プロ用に設定をしたい. ということで設定の仕方を色々調べていたのだが, あまりまとまっているものが無かったので書いてみた.
ディレクトリ構成
コンテストディレクトリ, 解答ファイルなどの構成はこんな感じにしたい. 例としてAtCoderを使うが, 他のサイトでも似たような構成になると思う.
├── AtCoder
│ ├── ABC
│ │ ├── abc001
│ │ │ ├── A.cpp
│ │ │ └── B.cpp
│ │ └── abc002
│ │ ├── A.cpp
│ │ └── B.cpp
| ├── ARC
.
.
.
あとはCMakeLists.txtというCMakeの設定ファイルをどこにおいてどう書くかである. それには大きく分けて以下の三つの方法がある.
- 一つのコンテスト毎に一つ作成
- コンテストの種類毎に一つ作成
- 1, 2両方
というわけでそれぞれやり方を書いていく.
1. 一つのコンテスト毎にCMakeを作成
以下のような構成でコンテスト毎にCMakeLists.txtを作る. 作る手法としては2種類ある
├── AtCoder
│ ├── ABC
│ │ ├── abc001 # ここでプロジェクトを開く
│ │ │ ├── A.cpp
│ │ │ ├── B.cpp
│ │ │ └── CMakeLists.txt # これを作成
│ │ └── abc002 # ここでプロジェクトを開く
│ │ ├── A.cpp
│ │ ├── B.cpp
│ │ └── CMakeLists.txt # これを作成
| ├── ARC
.
.
.
-
一つのコンテスト毎にNew Projectで新しくProjectを作成
毎回New Projectで作成するとmain.cppとデフォルトのCMakeLIsts.txtができてそれらを書き替える必要がある. ぶっちゃけめんどくさい. -
一つのコンテスト毎にCMakeLists.txtだけ作成してOpen Projectで開く
先に以下のようなシンプルなCMakeLists.txtをコンテストフォルダに作っておき, Open Projectで開く.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
add_executable(ABC00XA A.cpp) # ABC00Xはコンテスト名
add_executable(ABC00XB B.cpp)
add_executable(ABC00XC C.cpp)
add_executable(ABC001D D.cpp)
シェルとかでこれを生成するスクリプト書いたり, 単純にテンプレ作っといてコピーしたりとかすれば後者の方法が楽だと思う.
利点
・プロジェクト毎に実行とかデバッグを利用できる
欠点
・毎回CMakeListsを作るのがめんどくさい
・各コンテストは別プロジェクト扱いになるのでまとめて開くとインクルードされていない
2. コンテストの種類毎にまとめて一つのCMakeを作成
毎回CMakeListsを作るのがめんどくさい場合, コンテストの種類でまとめて一つのCMakeListsを作成し, 各コンテストのファイルをそこでまとめてインクルードするやり方がある.
ディレクトリ構成はこんな感じになる.
├── AtCoder
│ ├── ABC # ここでプロジェクトを開く
│ │ ├── abc001
│ │ │ ├── A.cpp
│ │ │ └── B.cpp
│ │ ├── abc002
│ │ │ ├── A.cpp
│ │ │ └── B.cpp
│ │ └── CMakeLists.txt # これを作成
| ├── ARC
.
.
.
CMakeListsはこんな感じのを作る.
cmake_minimum_required(VERSION 3.4)
project(ABC CXX)
# サブディレクトリ内のソースをすべてプロジェクトにぶちこむマクロ
MACRO(ADD_ALL_SUBDIR result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
SET(dirlist "")
FOREACH(child ${children})
IF(IS_DIRECTORY ${curdir}/${child})
LIST(APPEND dirlist ${child})
aux_source_directory(${child} SOURCE)
ENDIF()
ENDFOREACH()
SET(${result} ${dirlist})
ENDMACRO()
ADD_ALL_SUBDIR(SUBDIRS ${ABC_SOURCE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
IF(SOURCE)
add_executable(ABC ${SOURCE})
ENDIF()
ここではサブディレクトリをマクロを利用してまとめて取得したが,
aux_source_directory(ABC001 SOURCE)
aux_source_directory(ABC002 SOURCE)
...
のように一個ずつ書いても良い.
利点
・CMakeLists.txtを毎回書かなくて良い
欠点
・実行やデバッグなどの機能を使えない(個別に設定がいる)
・各コンテストフォルダだけをCLionで開くとインクルードされてない
3. コンテストの種類毎にまとめて一つ&各コンテスト毎にCMakeを作成
この手法は,
- 1のいずれかの方法でコンテストディレクトリにCMake作成
- さらに2のCMakeも作成
というものである.
ディレクトリ構成はこんな感じになる.
├── AtCoder
│ ├── ABC # ここでプロジェクトを開くことができる
│ │ ├── abc001 # ここでもプロジェクトを開くことができる
│ │ │ ├── A.cpp
│ │ │ ├── B.cpp
│ │ │ └── CMakeLists.txt # これを作成(しなくてもよい)
│ │ ├── abc002 # ここでもプロジェクトを開くことができる
│ │ │ ├── A.cpp
│ │ │ ├── B.cpp
│ │ │ └── CMakeLists.txt # これを作成(しなくてもよい)
│ │ └── CMakeLists.txt # これを作成
| ├── ARC
.
.
.
CMakeListsはこんな感じ
cmake_minimum_required(VERSION 3.4)
project(ABC CXX)
# サブディレクトリ内のソースをすべてプロジェクトにぶちこむマクロ
MACRO(ADD_ALL_SUBDIR result curdir)
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
SET(dirlist "")
FOREACH(child ${children})
IF(IS_DIRECTORY ${curdir}/${child})
LIST(APPEND dirlist ${child})
# CMakeがディレクトリにあるか(1のパターン)無いか(2のパターン)で分岐
IF(EXISTS ${curdir}/${child}/CMakeLists.txt)
add_subdirectory(${child})
ELSE()
aux_source_directory(${child} SOURCE)
ENDIF()
ENDIF()
ENDFOREACH()
SET(${result} ${dirlist})
ENDMACRO()
ADD_ALL_SUBDIR(SUBDIRS ${ABC_SOURCE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
IF(SOURCE)
add_executable(ABC ${SOURCE})
ENDIF()
上のように書けば, ディレクトリ内にCMakeListsがあるものはサブディレクトリとして個別に実行ができる. CMakeListsが無い場合, 分岐でインクルードはしているが個別に実行はできないので注意.
利点
・まとめて管理できて個別に実行もできる.
欠点
・CMakeListsの数が増える
まとめ
- ビルドと実行をターミナルでやり, 補完だけできればいい → 2のやり方が楽
- デバッグとか実行とかIDEの機能諸々を使いたい → 1or3
- 3のCMakeListsを一回作っておけば, あとからCMakeListsを追加することでどっちの場合にも対応できて便利and楽だと思う.
他になにか良い方法あれば教えてください.