チュートリアル:CMake を使用したコンパイラ警告の管理

警告は、特に C++ では重要です。

C++ コンパイラは、return のない関数など、多くのばかげたコードを受け入れることを余儀なくされています。 、初期化されていない警告の使用など。ただし、そのようなことを行うと、少なくとも警告を発行できます。

しかし、CMake で非常にコンパイラ固有のフラグをどのように管理しますか?

ヘッダー ファイルが他のプロジェクトに警告を漏らさないようにするにはどうすればよいですか?

私の以前のアプローチ

以前は、 CMAKE_CXX_FLAGS を単純に変更しました コマンド ラインで変数を使用して、適切な警告フラグを設定します。したがって、たとえば CI では、次のように CMake を呼び出しました。

cmake -DCMAKE_CXX_FLAGS="-Werror -Wall -Wextra …"

そうすれば、コンパイラは常に警告フラグを有効にします。

このアプローチは確実に機能しますが、いくつかの問題があります:

<オール> <リ>

CMAKE_CXX_FLAGS を手動で更新することを忘れないでください CI とすべてのロケール開発マシンで.私はときどきそれを忘れて、機能を実装し、それを CI にプッシュしました.警告のためにコンパイルが失敗し、面倒でした.

<リ>

警告はすべてをコンパイルするために使用されます 警告が有効になっています。これは add_subdirectory() を使用する場合に問題になります 警告なしでコンパイルされないいくつかの外部依存関係をコンパイルするには、 -Werror を削除する必要があります または、何らかの方法で外部ターゲットの警告を手動で無効にします。

<リ>

バージョン管理システムとビルド ファイルから警告オプションを分離します。コードは特定の警告レベルを念頭に置いて設計されているため、これは問題があると思います。これはビルド ファイルにも反映されるはずです。

<リ>

特に清潔感はありません。

そこで、最新のプロジェクト foonathan/lex で、より良い解決策を探しました。

ターゲット プロパティを変更して警告を有効にする

-DCMAKE_CXX_FLAGS="…" の場合 CMakeLists.txt に移動してみませんか? ?

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} …")

これをしないでください!

CMAKE_CXX_FLAGS はグローバル変数であり、すべて のコンパイラ フラグを変更します

代わりにこれを行います:

add_library(my_library …)
target_include_directories(my_library PUBLIC include/)
target_link_libraries(my_library PUBLIC other_library)
target_compile_options(my_library PRIVATE -Werror -Wall -Wextra)

ライブラリを作成するときは、インクルード ディレクトリを指定し、他のライブラリへのリンクを指定します。target_compile_options() を使用 ターゲットのコンパイラ フラグを指定することもできます。それを使用して、警告を指定することもできます。また、警告は PRIVATE として指定されます。 、それらはライブラリをコンパイルするときにのみ使用されます。それにリンクしているターゲットは警告を有効にしません。対照的に、それにリンクしているターゲットは、PUBLIC であるため、インクルード ディレクトリと他のライブラリを取得します。 .

ヒント: target_compile_options(my_target PRIVATE …) を使用 ターゲットで警告を有効にします。

これは素晴らしい解決策です。唯一の問題は、コンパイラ フラグがコンパイラに依存することです。上記の警告は GCC と clang では機能しますが、MSVC では機能しません。

if() を始める前に 、ジェネレータ式を見てください:

target_compile_options(my_library PRIVATE
     $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
          -Wall>
     $<$<CXX_COMPILER_ID:MSVC>:
          /W4>)

このコードは -Wall を有効にします GCC と clang と /W4 の場合

ヒント: ジェネレータ式を使用して、さまざまなコンパイラに対してさまざまな警告を条件付きで有効にします。

ヘッダー ファイルでの警告の防止

これで、ライブラリをコンパイルするときに警告が自動的に有効になり、すべての警告が修正されることが期待されます。しかし、さらに警告がある別のプロジェクトで使用されている場合はどうなるでしょうか?

たとえば、 -Wconversion でコンパイルします しかし、私の依存関係はそうではありません。そのため、ヘッダー ファイルには警告が発行されるインスタンスがいくつかあり、これは厄介です。

これらの警告を修正するためのプル リクエストやローカルでの無効化以外に私にできることはあまりありませんが、ライブラリ ライターとして、あなたが依存関係にあるプロジェクトの問題を防ぐことができます。

トリックは target_include_directories(my_library SYSTEM PUBLIC include/) を使用することです .SYSTEM インクルード ディレクトリをシステム インクルード ディレクトリに変換します。コンパイラは、そこから生成されたヘッダー ファイルから警告を発行しません。

my_library をリンクする外部プロジェクト ライブラリのヘッダー ファイルからは警告が表示されませんが、ライブラリのソース ファイルからも警告が表示されません!

ソース ファイルにヘッダー ファイルをインクルードするときは警告が必要ですが、他のソース ファイルからヘッダー ファイルをインクルードするときは警告が表示されたくないので、次のような方法を試してみてください:

add_library(my_library …)
target_include_directories(my_library PRIVATE include/)
target_include_directories(my_library SYSTEM PUBLIC include/)

include/ を非公開で追加します SYSTEM なし 、しかし公的には.悲しいことに、これは機能しません.

でも、あと少しです:

add_library(my_library …)
target_include_directories(my_library PRIVATE include/)
target_include_directories(my_library SYSTEM INTERFACE include/)

INTERFACE を使用する必要があります PUBLIC の代わりに .インターフェイス プロパティは、ターゲットにリンクしている外部ターゲットにのみ与えられ、ターゲット自体をコンパイルするときに使用されることはありません.これは PRIVATE の反対です. これはターゲットにのみ使用され、外部には使用されません。

PUBLIC で動かなかった理由 これは、パブリック プロパティが両方とも PRIVATE であるためです。 と INTERFACE .

ガイドライン: ライブラリのインクルード ディレクトリを 2 回指定します。PRIVATE で 1 回指定します。 そして一度 SYSTEM INTERFACE で .そうすれば、外部コードはヘッダー ファイルから警告を受け取りませんが、あなたのコードはそうします.

ヘッダーのみのライブラリの処理

上記の方法はほとんどのライブラリでうまく機能しますが、ヘッダーのみのライブラリでは機能しません。

あなたが善良な市民であれば、インターフェイス ライブラリ ターゲットを作成しました:

add_library(my_library INTERFACE)
target_sources(my_library INTERFACE …)
target_include_directories(my_library SYSTEM INTERFACE include/)

そうすれば、ライブラリのユーザーは target_link_libraries() を使用できます 適切なインクルード パスを自動的に取得します。

ただし、ヘッダーのみのライブラリはコンパイルされていないため、 target_compile_options(my_library PRIVATE …) は使用できません .インターフェイス ライブラリは INTERFACE のみを持つことができます

代わりにできることは、コンパイルする必要がある非インターフェース ターゲットを作成することです。これは、警告をチェックするためだけです。とにかく、そのようなターゲットが 1 つあることを願っています。テストです!

add_executable(my_library_test …)
target_link_libraries(my_library_test PUBLIC my_library)
target_compile_options(my_library_test PRIVATE …)

ヒント: ヘッダーのみのライブラリの場合、ライブラリのテスト ターゲットで警告を有効にします。

ただし、問題が 1 つあります。テスト ターゲットがヘッダーのみのターゲットにリンクしているため、SYSTEM が返されます。 含めて、実際に警告を受けないようにしてください!

include ディレクトリを再度追加しますが、SYSTEM は使用しません my_library の構成を複製する以外に解決策がわかりません。 my_library_test のターゲット それにリンクする代わりに。

何かご存知でしたら教えてください。

どの警告を有効にする必要がありますか?

有効にする必要がある警告のリストについて説明して、この投稿を閉じましょう。

GCC/clang の場合、通常、次の一連の警告があります:

    <リ>

    -Werror :警告をエラーとして扱います。警告を修正せざるを得ないので、私はこれが好きです。また、警告を見逃すことを不可能にします。そのフラグがないと、コンパイル時に警告が生成されますが、見逃す可能性があります。後でコンパイルは触れません。そのファイルが再び表示されるため、警告は再度発行されません。

    <リ>

    -pedantic-errors :これにより、基本的に厳密な標準準拠が可能になります。これは -Werror -pedantic と同等ではないことに注意してください。 、なぜですか?

    <リ>

    -Wall :より適切な名前は -Wcommon です .初期化されていない変数の使用などの一般的な警告を有効にします。

    <リ>

    -Wextra :-Wall によって有効にされないいくつかのより一般的な警告 .

    <リ>

    -Wconversion :float のような値を変更する可能性のある変換に関する警告を有効にします int へ .

    <リ>

    -Wsign-conversion :signed 間の変換に関する警告を有効にします と unsigned .やや面倒ですが、それでも便利です。-Wconversion でカバーされていないことに注意してください。 C++ モードで (何らかの理由で)。

もちろん、これらの警告によって有効にされない警告は他にもあります。警告のリスト (GCC/clang) を参照して、自分で確認することをお勧めします。

私のセットアップで気に入らない唯一のことは、未使用の関数/変数/などに関する警告です。プロトタイプを作成するとき、関数が使用されていないため、コンパイルできない不完全なコードがよくあります。バグがいくつかあるので、有効のままにします。

MSVC の場合、/WX /W4 を使用します .これにより、警告レベル 4 が有効になりますが、これは多くても多すぎず、エラーとして扱われます。

結論

target_compile_options() を使用 ライブラリターゲットの警告を有効にするジェネレーター式ですが、 PRIVATE を使用してください target.Combine INTERFACE にリンクしているプロジェクトで警告を有効にしないようにします。 SYSTEM のディレクトリを含める そこに警告が表示されないようにして、 PRIVATE を使用します SYSTEM のないディレクトリを含める 独自のプロジェクトをコンパイルするため。

そうすれば、プロジェクトをコンパイルするときに自動的に警告が表示されますが、他のユーザーには表示されません。