CMakeはC / C ++用のビルドシステムであり、毎年ますます普及しています。 実際には、新しいプロジェクトのデフォルトのソリューションになりました。 ただし、CMakeでタスクを実行する多くの例には、古風で信頼性の低い肥大化したアクションが含まれています。 CMakeでアセンブリスクリプトを記述する方法をより簡潔に説明します。
実際のヒントを試してみたい場合は、githubで例を取り上げ、記事を読んでそれを探索してください: https : //github.com/sergey-shambir/modern-cmake-sample
ヒント#1:CMakeの高い最小バージョンを指定する
古い開発環境との互換性が重要であるため、このアドバイスは公開ライブラリを作成する人には適用されません。 また、クローズドソースプロジェクトまたは高度に専門化されたオープンソースソフトウェアを作成している場合は、すべての開発者にCMakeの最新バージョンのインストールを要求できます。 これがないと、多くの記事のヒントが機能しません! 執筆時点で、CMake 3.8があります。
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
ヒント#2:makeやmake installを呼び出さないでください
最新のCMakeは、ビルドシステム自体を呼び出すことができます。 CMakeのドキュメントでは、このモードはビルドツールモードと呼ばれます。
# myproj myproj-build mkdir ../myproj-build && cd ../myproj-build # ../myproj cmake -DCMAKE_BUILD_TYPE=Release ../myproj # cmake --build . # , '-j4' . cmake --build . -- -j4
Visual Studioプロジェクトを生成している場合は、特定の構成で特定のプロジェクトをビルドできるなど、コマンドラインからビルドすることもできます。
cmake --build . \ --target myapp \ --config Release \ --clean-first
Linuxでは、make installを使用しないでください。使用するとシステムが詰まります。 これについては別の記事があります。 撮って撮影したい、またはmake installを使用しない理由に関する教育プログラム
ヒント#3:複数のCMakeLists.txtを使用する
CMakeLists.txtのネストは問題ありません。 プロジェクトが3つのライブラリ、3つのテストスイート、2つのアプリケーションに分かれている場合、それぞれにCMakeLists.txt
を追加してCMakeLists.txt
ませんか? 次に、別の中央CMakeLists.txt
を作成し、その中のCMakeLists.txt
を実行する必要があります。 これは、中央のCMakeListsのように見える場合があります。
cmake_minimum_required(VERSION 3.8 FATAL_ERROR) project(opengl-samples) # : CMakeLists # , add_subdirectory include(scripts/functions.cmake) add_subdirectory(libs/libmath) add_subdirectory(libs/libplatform) add_subdirectory(libs/libshade) # enable_testing BUILD_TESTING, # BUILD_TESTING=ON. # `cmake -DBUILD_TESTING=OFF projectdir` , # . enable_testing() if(BUILD_TESTING) add_subdirectory(tests) endif() # .. ..
ヒント4:グローバルスコープを詰まらせない
絶対に必要でない限り、グローバル変数を開始しないでください。 link_directories()
、 include_directories()
、 add_definitions()
、 add_compile_options()
およびその他の同様の命令を使用しないでください。
- target_link_librariesを使用して、ターゲットが依存する静的および動的、内部および外部ライブラリを追加します
- include_directoriesの代わりにtarget_include_directoriesを使用して、ターゲットが依存するヘッダーの検索パスを追加します
- add_definitionsの代わりにtarget_compile_definitionsを使用して、ターゲットがしようとしているマクロを追加します
- target_compile_optionsを使用して、ターゲットが使用する特定のコンパイラフラグを追加します
# - add_library(mylibrary \ ColorDialog.h ColorDialog.cpp \ ColorPanel.h ColorPanel.cpp) # ! - ! # /usr/include/wx-3.0 # find_package . target_include_directories(mylibrary /usr/include/wx-3.0)
ライブラリがプロジェクト内にあり、ヘッダー検索パスがtarget_include_directories(libfoo PUBLIC ...)
コンストラクトを通じてライブラリにアタッチされている場合、target_link_libraries
がライブラリヘッダー検索パスを追加できることに注意してください。
モダンCMakeのプレゼンテーション/ Tobias Beckerによる紹介から抜粋した依存関係図の例があります。
ヒント#5:最終的にC ++ 17またはC ++ 14を有効にする!
近年、C ++標準は頻繁に更新されています。C++ 11、C ++ 14、C ++ 17には驚くべき変更が加えられています。 可能な限り、古いコンパイラを放棄するようにしてください。 たとえば、Linuxの場合、Clangとlibc ++の最新バージョンをインストールし、静的C ++ランタイムレイアウトですべてのプロジェクトのビルドを開始することを妨げるものはありません。
コンパイルフラグを使用せずにC ++ 17を有効にする最良の方法は、CMakeに必要であることを明示的に伝えることです。
# : cxx_std_17 target_compile_features(${TARGET} PUBLIC cxx_std_17) # : set_target_properties(${TARGET} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO )
target_compile_features
を使用すると、C ++ 17またはC ++ 14ではなく、コンパイラ側の特定の機能を要求できます。 よく知られているCMake機能コンパイラの完全なリストは、ドキュメントにあります 。
ヒント#6:関数を使用する
CMakeでは、関数マクロと関数を宣言できます。 2つの違いは1つだけです。関数内に設定された変数はローカルです。
アセンブリのカスタマイズの現在の問題を解決したり、多くのアセンブリの目標を簡単に追加したりするための関数を作成すると便利です。 以下の例は、C ++ 17をより正確に有効にするために書かれました。
- CMake 3.8は、
/std:c++latest
を渡す方法をまだ知らない/std:c++latest
C ++ 17を有効にするためにVisual Studioに/std:c++latest
フラグ - Clang / libc ++で
std::experimental::filesystem
Experimentalstd::experimental::filesystem
を使用する場合、libc++experimental.a
にはファイルシステムモジュールがないため、プロジェクトをlibc++experimental.a
とリンクする必要があることをリンカーに伝える必要があります。 スレッド/ミューテックスの実装などのため、pthreadとリンクする必要もあります。 pthreadに依存
# CMake C++17 . # . function(custom_enable_cxx17 TARGET) # C++17 , CMake . target_compile_features(${TARGET} PUBLIC cxx_std_17) # C++latest Visual Studio if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS "/std:c++latest") # libc++, libc++experimental pthread Clang elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS "-stdlib=libc++ -pthread") target_link_libraries(${TARGET} c++experimental pthread) endif() endfunction(custom_enable_cxx17)
各関数は、本質的にCMake言語またはその動作をオーバーライドするように設計されたハックです。 他の開発者にとって、このハッキングの意味は明確ではありません。 したがって、その目的と意味を説明する関数内の各命令にコメントを追加してみてください。
KDEなどの大規模なオープンソースプロジェクトでは、それらの関数を使用するのは不適切な形式です。 他のオプションを検討することもできます:「明示的であることが暗黙的である」という原則に基づいてビルドスクリプトを明示的に記述するか、CMakeプロジェクトの上流に独自の関数を追加することを提案することもできます。
ヒント#7(物議を醸す):ソースファイルを一度に1つずつリストしない
私の同僚は、OpenGL、GLES、DirectX、Vulkanを介してモデルとアニメーションを含むシーンをレンダリングするための小さな3Dエンジンを開発しています。 このプロジェクトについて彼と話し合ったところ、彼はVisual Studioを使用してすべてのプラットフォーム(Windows、Linux、Android)を構築していることがわかりました! 彼は、MicrosoftがAndroid NDKをめったに更新しないことに不満を抱いているが、1つの簡単な理由でMSBuildによるビルドを拒否したくない。
彼は、2つのビルドシステムのアセンブリのファイルのリストに同行したくありません。
iOSからAndroidにゲームを移植し、Xcodeプロジェクトを読み取り、 Android.mk
のファイルのリストを自動的に展開するスクリプトを使用して2つのビルドシステムをサポートしAndroid.mk
。 CMakeを使用する場合、スクリプトを作成する必要さえありません。
CMakeにはaux_source_directory
関数がありますが、欠点があります:ヘッダーがリストに追加されず、IDE用に生成されたプロジェクトに表示されません。
-
file(GLOB ...)
が助けになり、マスクでファイルをスキャンします - 新しい変数でグローバルスコープを
custom_add_executable_from_dir(name)
ように、関数custom_add_executable_from_dir(name)
作成します - 別のファイルに配置された関数が現在のCMakeLists.txtへのパスを参照ポイントとして使用するように、変数
CMAKE_CURRENT_SOURCE_DIR
を使用します
function(custom_add_executable_from_dir TARGET) # file(GLOB TARGET_SRC "CMAKE_CURRENT_SOURCE_DIR/*.cpp" # add_executable(${TARGET} ${TARGET_SRC}) endfunction()
ライブラリターゲットのcustom_add_library_from_dir
関数を同じ方法で追加できます。
あなたが手作りのファンであるか、公共図書館を作成する場合は、おそらく一度に1つずつファイルを追加する方がよいでしょう。 この場合、 target_sources
を使用してプラットフォーム固有のファイルを追加します。
add_library(libfoo Foo.h Foo_common.cpp) if(WIN32) target_sources(libfoo Foo_win32.cpp) endif(WIN32)
ヒント#8:bashユーティリティを実行せず、cmake -Eを実行する
自動化のためにcmakeからBashコマンドを呼び出して、ディレクトリを作成したり、アーカイブを解凍したり、md5の量を計算したりすることは間違いありません。 ただし、コマンドラインユーティリティを呼び出すと、クロスプラットフォームのプロジェクトを奪われる可能性があります。 より移植性の高い方法は、 コマンドラインツールモードを使用してcmake -E
を呼び出すことcmake -E
。
ヒント#9:ライブラリユーザーの柔軟性を高める
公に利用可能なライブラリを作成している場合は、アドバイスが適用されます。 この場合、次のシナリオを簡素化する必要があります。
- プログラマはライブラリをサブモジュールとして追加し、
CMakeLists.txt
を介してCMakeLists.txt
を有効にしadd_subdirectory
- プログラマーは、ライブラリーのアセンブリーでカスタマイズを行いたい。 最も一般的なのは、完全に静的または動的なレイアウト、テストとサンプルを含むアセンブリ、またはライブラリのみです
ライブラリを追加するときに、一意の同義語を作成します。
# - add_library(foo ${FOO_SRC}) # , add_library(MyOrg::foo ALIAS foo)
ライブラリユーザーに、 BUILD_SHARED_LIBS
オプションを使用して、ライブラリの静的バージョンと動的バージョンのどちらをBUILD_SHARED_LIBS
するかを選択するBUILD_SHARED_LIBS
ます。
レイアウト設定を設定し、ヘッダーとライブラリのコンパイルフラグを検索する場合、キーワードPUBLIC、PRIVATE、INTERFACEを使用して、ライブラリに依存するターゲットが必要な設定を継承できるようにします。
- PUBLICは、ライブラリ依存プロジェクトにもこれらのオプションが必要であることを意味します
- PRIVATEは、ライブラリをビルドするためにのみオプションが必要であることを意味します
- INTERFACEは、アセンブリにはオプションは必要ないが、ライブラリを使用するにはオプションが必要であることを意味します
target_link_libraries(foobarapp PUBLIC MyOrg::libfoo PRIVATE MyOrg::libbar )
ヒント#10:CTestで自動テストを登録する
CTestサブシステムは、通常のBoost.Test、Catch、またはGoogle Testsの代わりに、テストのために特別なライブラリを使用することを強制しません。 CMakeがすべてのテストまたは選択したテストを1つのctest
コマンドで実行できるように、 ctest
テストのみを登録します。
プロジェクト全体でCTestサポートを有効にするために、 enable_testing
# enable_testing BUILD_TESTING, # BUILD_TESTING=ON. # `cmake -DBUILD_TESTING=OFF projectdir` , # . enable_testing() if(BUILD_TESTING) add_subdirectory(tests/libhellotest) add_subdirectory(tests/libgoodbyetest) endif()
CTestのテストに実行可能ファイルを登録するには、 add_test
を呼び出す必要があります。
# - add_executable(${TARGET} ${TARGET_SRC}) # CMake . # , . add_test(${TARGET} ${TARGET})
他に読むものは何ですか?
OpenSourceプロジェクト用にCMakeとCTestを構成した場合、継続的統合を有効にできます。CMakeアセンブリを使用したGitHub C / C ++プロジェクトの継続的統合(CI)
記事を作成する前に、いくつかの英語の資料を読み、テストし、再考しました。
- Modern CMakeのプレゼンテーション/ C ++およびOpenSourceに捧げられた会議の1 つからの紹介
- CMakeのプレゼンテーション-ミュンヘンで開催されたC ++会議の1 つからの紹介とベストプラクティス
- target_link_librariesを使用してモダンCMakeをポストする
- post CMakeでC ++ 11以降を有効にする
これらのソースからのいくつかのヒントは、記事には反映されていません。 したがって、それらを読んだ後、あなたは間違いなくCMakeに習熟するでしょう。