モダンCMake:ビルドスクリプトを改善するための10のヒント

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()



およびその他の同様の命令を使用しないでください。









 #  - 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     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用に生成されたプロジェクトに表示されません。









 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:ライブラリユーザーの柔軟性を高める



公に利用可能なライブラリを作成している場合は、アドバイスが適用されます。 この場合、次のシナリオを簡素化する必要があります。









ライブラリを追加するときに、一意の同義語を作成します。







 #  - add_library(foo ${FOO_SRC}) #  ,      add_library(MyOrg::foo ALIAS foo)
      
      





ライブラリユーザーに、 BUILD_SHARED_LIBS



オプションを使用して、ライブラリの静的バージョンと動的バージョンのどちらをBUILD_SHARED_LIBS



するかを選択するBUILD_SHARED_LIBS



ます。







レイアウト設定を設定し、ヘッダーとライブラリのコンパイルフラグを検索する場合、キーワード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)







記事を作成する前に、いくつかの英語の資料を読み、テストし、再考しました。









これらのソースからのいくつかのヒントは、記事には反映されていません。 したがって、それらを読んだ後、あなたは間違いなくCMakeに習熟するでしょう。








All Articles