C言語の開発環境構築はVisual Studioを入れるのが一番手っ取り早そうなのだが、macOSの場合はVisual Studioが現在対応していない。
そのため、VSCodeで色々頑張る方法を模索した。
コンパイラ
clangはmacOSの場合、デフォルトで入っている。
gccを使いたい場合はインストールする。
$ brew install gcc
コンパイラ警告
C言語の場合、コンパイラの警告を見るのがプラクティスとして重要になってくる。
デフォルトでは大した警告を出してくれないので、フラグを与えることで警告の種類を増やす必要がある。
ひとまず重要なフラグとして-Wall
と-Wextra
がある(gccもclangも共通)。
-Wall
によって全ての警告がオンになる……わけではなく、これは"基本的な警告が"全てオンになる、的な感じでしかないらしい。
なので、ここから漏れた警告として-Wextra
を足す必要がある。
これで今度こそ全ての警告がオンになる……というわけでは全く無く、まだまだ足した方が良い大事な警告がこの世にはたくさんあるらしい。
世界は広い。
clangの場合、-Weverything
という最強のフラグがある。
今度こそ全ての警告がオンになった……と思うのだが、いかんせん今度はあまりにも警告が細かくてうるさい。
幸い、警告を指定してオフにするフラグを後ろに追加していくこともできるので、結果として「全マシのニンニク抜きアブラ抜き」みたいな感じになってくる。
どっちにしろ、ベストのフラグ構成を目指そうとするとかなりの沼になる。
とりあえず-Wall -Wextra
で妥協して先に進むことにする。
Makefile
スタバの呪文みたいな大量のフラグをコンパイルの度に渡さないといけない運用もアレなので、Makefileを導入する。
Makefileはプロジェクト毎に作成する。
とりあえずは必要最低限の、コンパイラフラグをキープするためだけのMakefileを作成した。
CC = gcc
CFLAGS = -Wall -Wextra
main:
$(CC) $(CFLAGS) -o main main.c
# `./main`が生成される。
$ make
VSCodeの拡張
Microsoftの「C/C++」拡張がすごいスタンダードな空気を醸し出しているが、どうやらLLVMの「clangd」拡張の方が高性能らしい。
私は両方試そうとしたが、未使用の変数に警告を出す(後述するが、つまりコンパイラ警告をリンターで表示する)のが前者ではどうしても上手くいかず、後者にしたら一発で上手くいったので、後者をメインで使っていくことにした。
ただし、デバッグ関係の機能とかは「C/C++」拡張にしか無いので、両方インストールしておくのが良いらしい。
「C/C++」拡張をインストールした状態で「clangd」拡張をインストールすると、ポップアップで「Microsoftの方のやつも入ってんねんけど。特にインテリセンス機能がウチと競合してるさかい、こいつウチが黙らせてええか?」的なことを聞かれるので、「Yes」を押すと、VSCodeのsettings.json
に以下の項目が追加される。
{
"C_Cpp.intelliSenseEngine": "disabled"
}
これで無事両立ができた。
必要なバイナリ
イカれたメンバーを紹介するぜ!(訳:前提となる用語の解説)
- LLVM: 任意のプログラミング言語に対応可能なコンパイラ基盤。
- clang: LLVMを使った、CとかC++とか向けのコンパイラ。
- clangd: 言語サーバー。エディタ向けの機能とか提供するやつ。
- clang-tidy: リンター。clangdにも組み込まれている。
- clang-format: フォーマッター。clangdにも組み込まれている。
さて、clang
とclangd
はmacOSの場合デフォルトで入っている。(Xcode Command Line Toolsのインストールが必要かも。)
VSCodeの「clangd」拡張はclangd
があれば動くので、特に追加でインストールするものはない。
clang-tidy
とclang-format
について。これらのバイナリはmacOSにはインストールされていない。
VSCodeの「clangd」拡張を動かす分には、clangd
経由でこれらの機能が呼ばれるため、clang-tidy
やclang-format
といった個別のバイナリは必要ない。
だが、なんとなくコマンドラインからもこれらの機能を呼びたいと思ったなら、インストールする必要がある。
clang-tidy
とclang-format
は個別にインストールすることもできるが、以下のコマンドでまとめてインストールすることができる。
$ brew install llvm
バイナリは/usr/local/opt/llvm/bin
にインストールされる。
デフォルトではここにパスが通っておらず、自分で通す必要がある。
(この辺の話はインストール後にHomebrewがCaveatsとして表示してくれる。)
ここで注意。上では「まとめてインストール」と書いたが、ここにはclang
とかその他色んなバイナリも含まれる。
特にclang
はmacOSの組み込みのもの(/usr/bin/clangd
)と2つ存在することになる。
上記のようにパスを通し、インストールしたclang
が組み込みのものより優先して呼ばれるようになることで、どこかで何かトラブルを起こす可能性がある。
私の環境では今のところ問題は起きていないが、この辺は自己責任でお願いしたい。
フォーマッター
VSCode自体の設定になるが、editor.formatOnSave
はお好みでtrueにしておくと良い。
言語毎に設定を変えることもできる。
さて、C言語の書式のスタイル標準は乱立している。
ということでclang-formatでは、書式をいちいち細かく指定することができる。
コマンドならば引数で直接設定したりもできるが、主な方法としては.clang-format
というYAMLファイルを設置することになる。
.clang-format
はプロジェクトフォルダに置いてもいいし、プロジェクト毎に設定する気がないなら親フォルダに置いてもいい。
(clang-formatは対象ファイルのあるフォルダから出発して、順に親フォルダへと遡りながら.clang-format
を探し、最初に見つけたものを採用する。)
上では「細かく指定」と書いたが、基本的にはどれかのスタイル標準を選ぶだけで事が足りるだろう。
.clang-format
で指定できるオプションの中にはBasedOnStyle
という項目があり、以下のいずれかの標準を指定できる。1
LLVM
, Google
, Chromium
, Mozilla
, WebKit
, Microsoft
, GNU
ということで、とりあえずこんな感じの.clang-format
を用意すれば良い。
BasedOnStyle: Microsoft
リンター
clang-tidyの診断項目は1つ1つに名前がついており、個別にオン・オフを設定する事ができる。
これもまたコンパイラ警告と同様、初期状態では全てオンにはなっていない。そんでもって全てオンにするとかなり鬱陶しい。
従ってこれもベストの設定を探る余地がある。
設定は.clang-format
の時と同じように.clang-tidy
を設置することで行える。
個人的に触れておきたいのはreadability-identifier-naming
という診断項目で、各識別子のCasing(CamelCaseとかUPPER_CASEとか)を細かく設定してチェックする事ができる。
フォーマッターでは強制できなかった命名規則もこれでルール化できそうだ。
そして、重要な診断項目としてclang-diagnostic-*
シリーズがある。
これはclangコンパイラによる診断という事らしく、そのほとんどがコンパイラ警告に対応している。
例えばclang-diagnostic-division-by-zero
は、clangに-Wdivision-by-zero
を渡すことで有効になる警告と同じ内容の診断項目だ。(他の診断項目も全てこのような命名対応規則に従っている。)
clang-tidyの診断結果はVSCodeの「clangd」拡張によってリアルタイムに波線で表示されるので、いちいちコンパイルしなくてもコンパイラ警告の内容を確認できるということになり、ありがたい。
しかし、clang-diagnostic-*
シリーズの診断を行うには注意点がある。
clang-tidyのベースとして動いているclangに適切なコマンド引数(特に警告レベルのフラグ)を与える必要があるのだ。
例えば未使用の変数を検出するためには、.clang-tidy
でclang-diagnostic-unused-variable
をオンにするだけではダメで、clangに-Wunused-variable
かあるいは-Wall
等を渡さないといけないことになる。
では具体的にどうすればベースのclangにコマンド引数を渡せるのかというと、コンパイルデータベースと呼ばれるファイルをプロジェクト内に設置するのである。
コンパイルデータベースの作成
compile_commands.json
とcompile_flags.txt
の2通りがあり、後者の方が簡易版となる。
先に簡易版のcompile_flags.txt
について説明すると、これは非常に単純で、単に渡したい引数を以下のように改行して羅列するだけだ。
-Wall
-Wextra
しかし、Makefileと多重管理になる点が面倒になってくる。
次にcompile_commands.json
で、これは書き方がもっと具体的かつ本格的な感じになってくるわけだが、これに関しては「Bear」というツールで自動生成する事ができる。
$ brew install bear
$ bear -- make
こうすれば、make
によって走るビルドプロセスをbearが監視し、良い感じのcompile_commands.json
を生成してくれる。
これで無事Makefileとの多重管理を避けられた。
ちなみに、Makefileの中でgccを使ったビルドを指定している場合、compile_commands.json
の中身もgccによるコンパイルの設定が記述されるが、clang-tidy側のclangはその辺も読み取った上で上手く合わせた実行をしてくれるっぽい。
デバッガー
使ってないから分かんない。(「C/C++」拡張を無理にインストールした意味がここで無くなる。)
完成
もう何も恐くない。
-
InheritParentConfig
という、親フォルダの.clang-format
の設定を引き継ぐことを指定する値もある。 ↩