Cmake用法

反光發表於2024-11-25

cmake

1.常見用法

cd some_software-1.4.2
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/opt/the/prefix
cmake --build .
cmake --build . --target install
cmake --build . --target myexe --config Release

2.構建變數

CMAKE_PREFIX_PATH       搜尋路徑 dependent packages
CMAKE_MODULE_PATH       搜尋其他 CMake 模組的路徑
CMAKE_BUILD_TYPE        構建配置,例如 Debug或Release,確定除錯/最佳化標誌。這僅與單配置構建系統(例如Makefile和Ninja)相關。多配置構建系統(例如 Visual Studio 和 Xcode 的構建系統)會忽略此設定。
CMAKE_INSTALL_PREFIX    使用 install構建目標安裝軟體的位置
CMAKE_TOOLCHAIN_FILE    包含交叉編譯資料的檔案,例如 toolchains and sysroots。
BUILD_SHARED_LIBS       是否構建共享庫而不是靜態庫add_library() 不使用型別的命令
CMAKE_EXPORT_COMPILE_COMMANDS   生成一個compile_commands.json 用於基於 clang 的工具的檔案
CMAKE_EXPORT_BUILD_DATABASE     生成一個build_database.json 用於基於 clang 的工具的檔案

3.基本cmake包

//CMakeLists.txt

# 需要的cmake版本宣告
cmake_minimum_required(VERSION 3.15)

# 專案名稱和版本
project(Tutorial VERSION 1.0)

# 生成可執行檔案
add_executable(Tutorial tutorial.cxx)

//tutorial.cxx

#include <iostream>
int main(int argc, char* argv[])
{
  std::cout << "hello" << std::endl;
  return 0;
}

4.程式碼中使用專案版本號

TutorialConfig.h會生成到PROJECT_BINARY_DIR目錄中

//CMakeLists.txt

configure_file(TutorialConfig.h.in TutorialConfig.h)

#將PROJECT_BINARY_DIR新增到包含檔案的搜尋路徑中
#這樣我們就能找到TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

//TutorialConfig.h.in

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

//tutorial.cxx

#include <iostream>
#include <string>
#include "TutorialConfig.h"
int main(int argc, char* argv[])
{
  // report version
  std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
            << Tutorial_VERSION_MINOR << std::endl;
  return 0;
}

5.建立動態庫

// MathFunctions/CMakeLists.txt

# 宣告動態庫
add_library(MathFunctions MathFunctions.cxx)
# 宣告動態庫的介面目錄(動態庫使用者需要使用的目錄(生成時不需要))
target_include_directories(MathFunctions
                           INTERFACE
                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                            $<INSTALL_INTERFACE:include>
                           )

CMakeLists.txt

add_subdirectory(MathFunctions)

6.連結動態庫

CMakeLists.txt

# 連結動態庫名稱
target_link_libraries(Tutorial PUBLIC MathFunctions)

tutorial.cxx

#include "MathFunctions.h"

7.cmake 選項

MathFunctions/CMakeLists.txt

# 宣告選項 預設為ON
option(USE_MYMATH "Use tutorial provided math implementation" ON)
# 選項邏輯
if (USE_MYMATH)
  # 產生編譯宏
  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")

  include(MakeTable.cmake) # generates Table.h

  # library that just does sqrt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
              )

  # state that we depend on our binary dir to find Table.h
  target_include_directories(SqrtLibrary PRIVATE
                             ${CMAKE_CURRENT_BINARY_DIR}
                             )

  # state that SqrtLibrary need PIC when the default is shared libraries
  set_target_properties(SqrtLibrary PROPERTIES
                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                        )

  # link SqrtLibrary to tutorial_compiler_flags
  target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)

  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

MathFunctions/MathFunctions.cxx

//程式碼中的選項宏
#include <cmath>
#ifdef USE_MYMATH
#  include "mysqrt.h"
#endif

#ifdef USE_MYMATH
  return detail::mysqrt(x);
#else
  return std::sqrt(x);
#endif

8.設定 C++ 標準

add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

9.向目標新增編譯選項

CMakeLists.txt

# 設定變數
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")

# 向目標新增編譯選項
# BUILD_INTERFACE限制該設定只有在構建時候生效
target_compile_options(tutorial_compiler_flags INTERFACE
  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)

10.安裝

MathFunctions/CMakeLists.txt

# 安裝動態庫
set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
  list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs}
        EXPORT MathFunctionsTargets
        DESTINATION lib)
# 安裝標頭檔案
install(FILES MathFunctions.h DESTINATION include)

CMakeLists.txt

# add the install targets
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  DESTINATION include
  )

11.測試

CMakeLists.txt

# 生成並安裝測試程式
add_executable(Tutorial tutorial.cxx)
set_target_properties(Tutorial PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})

target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

# add the install targets
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  DESTINATION include
  )

# 啟動測試功能
enable_testing()

# does the application run
add_test(NAME Runs COMMAND Tutorial 25)

# 檢測列印輸出是否包含指定正規表示式
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
  )

# define a function to simplify adding tests
function(do_test target arg result)
  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
  set_tests_properties(Comp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )
endfunction()

# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
# 測試程式的輸出語句
  std::cout << "The square root of " << inputValue << " is " << outputValue
            << std::endl;

12.測試儀表盤

CMakeLists.txt

include(CTest)

CTestConfig.cmake

set(CTEST_PROJECT_NAME "CMakeTutorial")
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")

set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
set(CTEST_DROP_SITE_CDASH TRUE)
ctest [-VV] -D Experimental
ctest [-VV] -C Debug -D Experimental

13.新增系統自檢

在專案中新增一些依賴於目標平臺可能沒有的功能的程式碼

MathFunctions/CMakeLists.txt

#檢查是否可以構建 C++ 原始碼
include(CheckCXXSourceCompiles)

#檢查是否可以構建特定 C++ 原始碼
  check_cxx_source_compiles("
    #include <cmath>
    int main() {
      std::log(1.0);
      return 0;
    }
  " HAVE_LOG)
  check_cxx_source_compiles("
    #include <cmath>
    int main() {
      std::exp(1.0);
      return 0;
    }
  " HAVE_EXP)

  #根據檢查結果修改編譯流程
 if(HAVE_LOG AND HAVE_EXP)
    target_compile_definitions(SqrtLibrary
                               PRIVATE "HAVE_LOG" "HAVE_EXP"
                               )
  endif()

  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

MathFunctions/mysqrt.cxx

#include <cmath>

#if defined(HAVE_LOG) && defined(HAVE_EXP)
  double result = std::exp(std::log(x) * 0.5);
  std::cout << "Computing sqrt of " << x << " to be " << result
            << " using log and exp" << std::endl;
#else
  double result = x;

  // do ten iterations
  for (int i = 0; i < 10; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
  }
#endif

14.新增自定義命令和生成檔案

MathFunctions/MakeTable.cmake

# 我們新增MakeTable的可執行檔案
add_executable(MakeTable MakeTable.cxx)
target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)

# 新增生成原始碼的命令
add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  )

MathFunctions/CMakeLists.txt

# 包含自定義命令檔案
include(MakeTable.cmake)

#目標依賴於生成的檔案
add_library(SqrtLibrary STATIC
              mysqrt.cxx
              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
              )
# 在CMAKE_CURRENT_BINARY_DIR查詢標頭檔案
target_include_directories(SqrtLibrary PRIVATE
                             ${CMAKE_CURRENT_BINARY_DIR}
                             )

15.打包安裝程式

CMakeLists.txt

# setup installer
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
set(CPACK_GENERATOR "TGZ")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)

打包命令

# 打包生成軟體包
cpack
# -G選項選擇生成器 -C多配置構建
cpack -G ZIP -C Debug

16.選擇靜態或共享庫

CMakeLists.txt

option(BUILD_SHARED_LIBS "Build using shared libraries" ON)

MathFunctions/CMakeLists.txt

#設定目標庫的屬性(位置無關程式碼)
set_target_properties(SqrtLibrary PROPERTIES
                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                        )
#設定宏,用於windows程式庫的生成
target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")

MathFunctions/MathFunctions.h
根據宏定義切換連結方式

#if defined(_WIN32)
#  if defined(EXPORTING_MYMATH)
#    define DECLSPEC __declspec(dllexport)
#  else
#    define DECLSPEC __declspec(dllimport)
#  endif
#else // non windows
#  define DECLSPEC
#endif

namespace mathfunctions {
double DECLSPEC sqrt(double x);
}

17.匯出給其他cmake專案使用

17.1匯出安裝 *Targets.cmake檔案

MathFunctions/CMakeLists.txt

# 這裡應保證不匯出當前計算機固有關聯的路徑(使用BUILD_INTERFACE、INSTALL_INTERFACE分別描述)
target_include_directories(MathFunctions
                           INTERFACE
                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                            $<INSTALL_INTERFACE:include>
                           )

# install(TARGETS) 增加EXPORT關鍵字生成CMake檔案MathFunctionsTargets.cmake
install(TARGETS MathFunctions
        EXPORT MathFunctionsTargets
        DESTINATION lib)

# 安裝生成的MathFunctionsTargets.cmake檔案
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

17.2匯出安裝兩個Config cmake檔案

Config.cmake.in

@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )

CMakeLists.txt


include(CMakePackageConfigHelpers)
# 生成MathFunctionsConfig.cmake
# 根據Config.cmake.in生成包含匯出的配置檔案MathFunctionsConfig.cmake
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
  INSTALL_DESTINATION "lib/cmake/MathFunctions"
  NO_SET_AND_CHECK_MACRO
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
  )

# 生成MathFunctionsConfigVersion.cmake
write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
  COMPATIBILITY AnyNewerVersion
)
# 安裝兩個config檔案
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake
  DESTINATION lib/cmake/MathFunctions
  )

17.3 匯出MathFunctionsTargets.cmake

通常,專案在被外部專案使用之前會先進行構建和安裝。但是,在某些情況下,最好直接從構建樹中匯出目標。然後,引用構建樹的外部專案可以使用這些目標,而無需進行安裝。

CMakeLists.txt

export(EXPORT MathFunctionsTargets
  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
)

17.4 匯出Targets檔案NAMESPACE用法

此命令生成MathFunctionsTargets.cmake檔案並安排將其安裝到${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions。該檔案包含適合下游使用的程式碼,用於從安裝樹匯入安裝命令中列出的所有目標。

install(EXPORT MathFunctionsTargets
        FILE MathFunctionsTargets.cmake
        NAMESPACE MathFunctions::
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)

17.5 匯入target檔案

include(${INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions/MathFunctionsTargets.cmake)
add_executable(myexe src1.c src2.c )
target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)

18.打包debug和release

18.1區分除錯庫的字尾

CMakeLists.txt

set(CMAKE_DEBUG_POSTFIX d)

add_library(tutorial_compiler_flags INTERFACE)

add_executable(Tutorial tutorial.cxx)
set_target_properties(Tutorial PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})

target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)

18.2 設定庫版本

MathFunctions/CMakeLists.txt

set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0")
set_property(TARGET MathFunctions PROPERTY SOVERSION "1")

18.3 構建debug和release庫

cd debug
cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake --build .
cd ../release
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .

18.4 debug+release打包

打包指令碼
MultiCPackConfig.cmake

MultiCPackConfig.cmake 
include("release/CPackConfig.cmake")

set(CPACK_INSTALL_CMAKE_PROJECTS
    "debug;Tutorial;ALL;/"
    "release;Tutorial;ALL;/"
    )

打包命令

cpack --config MultiCPackConfig.cmake

19.匯入cmake包

19.1一般匯入

find package的用法

find_package(Catch2)
find_package(GTest REQUIRED)
find_package(Boost 1.79 COMPONENTS date_time)

find package完整使用方式

cmake_minimum_required(VERSION 3.10)
project(MyExeProject VERSION 1.0.0)

# Make project-provided Find modules available
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

find_package(SomePackage REQUIRED)
add_executable(MyExe main.cpp)
target_link_libraries(MyExe PRIVATE SomePrefix::LibName)

19.2匯入並使用git包

include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
  FIND_PACKAGE_ARGS NAMES GTest
)
FetchContent_MakeAvailable(googletest)

add_executable(ThingUnitTest thing_ut.cpp)
target_link_libraries(ThingUnitTest GTest::gtest_main)
include(FetchContent)
FetchContent_Declare(
  Catch2
  URL https://intranet.mycomp.com/vendored/Catch2_2.13.4_patched.tgz
  URL_HASH MD5=abc123...
  OVERRIDE_FIND_PACKAGE
)

# The following is automatically redirected to FetchContent_MakeAvailable(Catch2)
find_package(Catch2)

19.3匯入多種配置的庫

find_library(math_REL NAMES m)
find_library(math_DBG NAMES md)
add_library(math STATIC IMPORTED GLOBAL)
set_target_properties(math PROPERTIES
  IMPORTED_LOCATION "${math_REL}"
  IMPORTED_LOCATION_DEBUG "${math_DBG}"
  IMPORTED_CONFIGURATIONS "RELEASE;DEBUG"
)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE math)

20.匯入其他

匯入可執行檔案

匯入一個可生成原始碼的可執行檔案,然後編譯生成的原始碼

add_executable(myexe IMPORTED)

set_property(TARGET myexe PROPERTY
             IMPORTED_LOCATION "../InstallMyExe/bin/myexe")

add_custom_command(OUTPUT main.cc COMMAND myexe)
add_executable(mynewexe main.cc)

匯入動態庫

add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY
             IMPORTED_LOCATION "/path/to/libfoo.a")

add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE foo)

windows 匯入

add_library(bar SHARED IMPORTED)
set_property(TARGET bar PROPERTY
             IMPORTED_LOCATION "c:/path/to/bar.dll")
set_property(TARGET bar PROPERTY
             IMPORTED_IMPLIB "c:/path/to/bar.lib")
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE bar)