Microsoft visual c++ をコマンドラインベースで使う ( cl 編 )

Visual Studio を使いたくない俺のためのチートシート

もう皆さん知ってるかもしれないんですけど、私、 HISS の主要開発者です。 HISS は Microsoft Visual C++ で作っているんですが、ビルドは全部コマンドラインでやっています。 Visual Studio の IDE を立ち上げたくないし、立ち上げてもよく分からないので。ということで、よく使うコマンドとか、そのオプション引数とか、どういう構成で作ってるかとか、その辺を書いておきます。

本当は “Microsoft Visual C++ をコマンドラインベースで使う” というタイトルだったんですけど、途中で全部書く心が折れて、 cl だけ先に出すことにしました。

コマンドの実行方法

普通のコマンドプロンプトでは、 MSVC の各種コマンドにはパスが通っていません。コマンドだけパスを通したとしても、そのほかに必要な環境変数 ( INCLUDE とか ) が不足しているため、無理矢理普通のコマンドプロンプトに持って来るのはなかなかに面倒くさいです。しかも、x86のバイナリを作るか、x86_64のバイナリを作るかでパスが違うので、これはもう餅は餅屋ということで、おとなしく MSVC についてくる Developer Command Prompt を使うのがいいでしょう。x86の作業をする場合はx86 native tools command prompt for VSなんとかかんとか を使います。x86_64の作業をする場合は x64 native tools command prompt for VSなんとかかんとか を使います。

一番重要なコマンド、 cl

これは Microsoft Visual C / C++ のコンパイラです。まぁ、引数によってはビルドまでやってくれるんですが。

オマジナイとして常に書いた方がいいコマンドは以下です。

cl /nologo /EHsc この後にプションがずらずら

/nologo オプションは、「オレサマがMicrosoft Visual C++ のコンパイラだぜ、どやー」というようなメッセージを表示しないように指示します。んなこたあわかっとんねん、ということです。

/EHsc オプションは、例外の処理方法を制御します。ちょっと詳しく言うと、例外が発生してスコープから脱出する際に、ローカルに確保されているオブジェクトをどうやって破棄するか(デストラクタを呼ぶか呼ばないか)とかが違うようですが、私のように必要に迫られて雰囲気でC++を書いているようなレベルでは気にする必要はないでしょう。むしろ /EHsc のほうがGCCなどの挙動にも近いらしいので、これはとりあえずつけとけ!で問題ないと思います。

次に、よく使いそうなオプションと設定方法を書いておきます。

/c でコンパイルのみ行う

gccの -c と同じです。オブジェクトファイルの出力だけを行います。実際の開発では、 makefile で依存関係を定義して、個々のc / cppふぁいるは /c オプションで分割コンパイルし、あとでリンクするパターンが多いと思います。そうしないと、1行変更しただけで、全部コンパイルしちゃいますからね。

/I で追加のインクルードディレクトリを指定

Visual Studioを使っている人は、「追加のインクルード・ディレクトリ」とかいうGUIのなにかでポチポチやるとできるらしいですが、CUIは素晴らしいので、私は黙って /I を使用します。

/I のあとにスペースを空けて(これから出てくる他のオプションと違ってコロンじゃないので注意)、1つのディレクトリを書きます。複数追加したいときは、 /I オプションを複数使いって、 /I dir1 /I dir2 のように書けばいいらしいです、やったことないですが。

/source-charset でソースファイルのエンコーディングを指定

なにもオプションを指定しないと、MSVCは、ソースコードのエンコーディングを推測しようとします。いわゆるSJIS(CP932)でソースファイルを書くと、BOMがありませんから、asciiとして読まれます。リファレンスを読むと、この場合は現在のコードページ(日本語の場合はコードページ932)と仮定して読まれるらしいのですが、実際に日本語を書くと警告が発生して、UTF-16で保存しろと怒られるので、リファレンスの説明は100%合っているわけではないのかなと思います。

UTF-16でソースコードを記述すると、オプション指定無しでも警告が出ずにコンパイルできるのですが、ソースコードをgit管理していると、UTF-16だとdiffが出せなかったり色々不便なので、UTF-8で書きたいところです。ということで搭乗するのがこのオプションです。

いろんな文字コードを指定できるのですが、男は黙って /source-charset:utf-8 でいいと思います。

/execution-charset で動作文字セットを指定

/source-charset と比べると、こっちを使うシチュエーションはあまりないような気もします。日本語とうまくお付き合いする場合に必要になるかもしれません。

このオプションで、文字列リテラルの中身などを実際にコンパイルする際に、どの文字コードに変換するかを指定します。/source-charsetとどうやって使い分けられているかというと、こんな感じです。

まずソースコードを読みます。このときには/source-charsetを使います。で、とりあえずコンパイラはソースをパースして構文木的な内部表現を作りたいだろうけど、それはコンパイラだけがどういう方式で作るかを知っているので、我々は気にしなくていいです。

で、内部表現に変換できたら、じゃあそれを機械語にしてバイナリに書き込んでいくかってことになるわけですけど、そのときに文字列はテキストセグメントの中に入れられます。このときに/execution-charsetを使います。つまり、ソースコードがどんなエンコーディングで書かれていたとしても、一度内部表現に一本化してから、/execution-charsetで変換し直すことができるわけですね。

HISS ではなんでこれを使っているかというと、 FineSpeech2 の SDK が、CP932のテキストにしか対応していないからです。一部の発音記号などをハードコーディングする場所で文字コードが合わなくなるという現象が起きるので、ここに /execution-charset:.932 を指定しています。

/std でランタイムバージョンを指定

HISS では、一部の機能で C++17 を必要としています。とはいえ、コード整理するために nested namespace を使いたかっただけなんですけど。

なにもオプションを指定しないと、たぶん C++14 ぐらいまでしか有効になっていないので、それよりも新しいC++の構文や標準ライブラリの機能を使おうとすると怒られます。そんなときは、 /std:c++17 という感じで書いて有効にしてあげましょう。

/F え出力ファイル名を指定

実際には、 /F の後にもう人文字付けてから使います。オブジェクトファイルなら /Fo 、実行ファイルなら /Fe です。たとえば、オブジェクトファイルを標準とは違う場所に出力したいなら、 makefile で /Fo:objects/$@ とか書くことができます。

その他のオプション

https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options?view=msvc-170

日本語版もあるのですが、一部機械翻訳でまともによめないので、英語のほうがいいと思います。

いつか link とかも書きたいけど、そんなにオプションをいっぱい使ってるわけじゃないから、詳しくはならなさそう。