1. 程式人生 > >c++ cmakelist 詳解

c++ cmakelist 詳解

想要 sam 親測 dpa native 過程 gen diff 日誌庫

基本元素

首先cmaklist必須包括以下幾個部分:

#工程名
project(study_case)
#cmake最低版本需求
cmake_minimum_required(VERSION 2.8.3)

#添加添加需要的庫
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/usr/local/share/OpenCV")
find_package(OpenCV 3.2.0 REQUIRED)

#確定需要的頭文件
include_directories(
    include
)

#確定編譯語言
#1
set(CMAKE_CXX_STANDARD 11)
#2. or
add_definitions(-std=c++11)

#設置二進制文件目錄
SET(BIN_DESTINATION ${PROJECT_SOURCE_DIR}/bin)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BIN_DESTINATION})

#如果多個文件相關,則添加源代碼(可選)
set(SRC  
    ${PROJECT_SOURCE_DIR}/src/detector.cpp
    ${PROJECT_SOURCE_DIR}/src/demo.cpp
    ${PROJECT_SOURCE_DIR}/src/test.cpp
)
#如果需要編譯動態庫並鏈接庫文件(可選)
link_directories(${PROJECT_SOURCE_DIR})
add_library(overload SHARED ${SRC})
target_link_libraries(overload -llianghao  -lpthread -lm -lstdc++)

#生成可執行文件
link_directories(${PROJECT_SOURCE_DIR})
#添加可執行文件
#1. 如果文件之間沒有關聯
add_executable(overload src/overload.cpp)
#2. or 如果多個文件相關
add_executable(overload src/overload.cpp ${SRC})

#如果有依賴外部庫則需要添加以下命令(可選)
target_link_libraries(overload -llianghao  -lpthread -lm -lstdc++)

其他功能

1. 使其包含c++11特性 (-std=c++11如何寫進cmakeList.txt)

#1
set(CMAKE_CXX_STANDARD 11)
#2. or
add_definitions(-std=c++11)
#3. or
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
     message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

2. 設定可執行文件的輸出目錄

SET(BIN_DESTINATION ${PROJECT_SOURCE_DIR}/bin)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BIN_DESTINATION})

3. 生成so庫

3.1 想生成多個so庫
# 設置構建本地庫所需的最小版本的cbuild。
cmake_minimum_required(VERSION 3.4.1)
# 創建並命名一個庫,將其設置為靜態
#  或者共享,並提供其源代碼的相對路徑。
# 您可以定義多個庫,而cbuild為您構建它們。
#  Gradle自動將共享庫與你的APK打包。
add_library( hello-lib  #設置庫的名稱。即SO文件的名稱,生產的so文件為“libhello-lib.so”,在加載的時候“System.loadLibrary("hello-lib");”
                SHARED  # 將庫設置為共享庫。
                src/main/jni/hello.cpp    # 提供一個源文件的相對路徑
                src/main/jni/helloJni.cpp    # 提供同一個SO文件中的另一個源文件的相對路徑
              )
#搜索指定的預構建庫,並將該路徑存儲為一個變量。因為cbuild默認包含了搜索路徑中的系統庫,所以您只需要指定您想要添加的公共NDK庫的名稱。cbuild在完成構建之前驗證這個庫是否存在。
find_library(log-lib  # 設置path變量的名稱。
              log   #  指定NDK庫的名稱 你想讓CMake來定位。
               )
#指定庫的庫應該鏈接到你的目標庫。您可以鏈接多個庫,比如在這個構建腳本中定義的庫、預構建的第三方庫或系統庫。
target_link_libraries( hello-lib     #指定目標庫中。與 add_library的庫名稱一定要相同
                       ${log-lib}    # 將目標庫鏈接到日誌庫包含在NDK。
                        )
#如果需要生產多個SO文件的話,寫法如下
add_library( natave-lib  #設置庫的名稱。另一個so文件的名稱
                SHARED  # 將庫設置為共享庫。
                src/main/jni/nataveJni.cpp    # 提供一個源文件的相對路徑
              )
target_link_libraries( natave-lib     #指定目標庫中。與 add_library的庫名稱一定要相同
                       ${log-lib}    # 將目標庫鏈接到日誌庫包含在NDK。
                        )

這裏還有一個坑在裏面,就是如果你的庫的名字起得不是叫xx-lib的話,編譯是通不過的,親測,比如起個so庫名叫test,cpp文件叫做test.cpp,這樣是編譯不過的,不會生成多個so庫。

3.1 生成一個so庫,多個cpp文件
# 查找cpp目錄下的所有源文件
# 並將名稱保存到 DIR_LIB_SRCS 變量
aux_source_directory(src/main/cpp/ DIR_LIB_SRCS)
# 生成鏈接庫
add_library (native-lib SHARED ${DIR_LIB_SRCS})

# 導入cpp目錄下的所有頭文件
include_directories(src/main/cpp/)

替換原有的add_library命令就可以了,其實就是生成一個變量,指定cpp的文件路徑

4. 設置編譯類型

#set release
if (NOT CMAKE_BUILD_TYPE)
    message("not defined Build Type:auto define it to Debug" )
    set (CMAKE_BUILD_TYPE Debug)
endif (NOT CMAKE_BUILD_TYPE)

if (CMAKE_BUILD_TYPE MATCHES "Debug" )
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ")
elseif (CMAKE_BUILD_TYPE MATCHES "Release" )
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 ")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 ")
elseif (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 ")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 ")
else()
    message("unkown defined Build Type:same handle with Debug" )
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ")
endif (CMAKE_BUILD_TYPE MATCHES "Debug")

5. 外部庫FIND_PACKAGE

格式:
FIND_PACKAGE(<name> [version] [EXACT] [QUIET] [NO_MODULE] [ [ REQUIRED | COMPONENTS ] [ componets... ] ] )

5.1 查找*.cmake的順序
1、 find_package(<Name>)命令首先會在模塊路徑中尋找Find<name>.cmake

這是查找庫的一個典型方式,具體查找路徑依次為CMake:
變量${CMAKE_MODULE_PATH}中的所有目錄。

如果沒有, 然後再查看它自己的模塊目錄: /share/cmake-x.y/Modules/

這稱為模塊模式。

2、 如果沒找到這樣的文件:

find_package()會在:

~/.cmake/packages/

/usr/local/share/

中的各個包目錄中查找,尋找:

<庫名字的大寫>Config.cmake
<庫名字的小寫>-config.cmake

(比如庫Opencv,它會查找/usr/local/share/OpenCV中的OpenCVConfig.cmake或opencv-config.cmake)。**這稱為配置模式

不管使用哪一種模式,只要找到.cmake,.cmake裏面都會定義下面這些變量:

例子<NAME>: OpenCVConfig.cmake

<NAME>_FOUND
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES
<NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS
<NAME>_DEFINITIONS
註意大部分包的這些變量中的包名是全大寫的,如 LIBFOO_FOUND ,有些包則使用包的實際大小寫,如 LibFoo_FOUND

  如果找到這個包,則可以通過在工程的頂層目錄中的CMakeLists.txt 文件添加 include_directories(_INCLUDE_DIRS) 來包含庫的頭文件,添加target_link_libraries(源文件 _LIBRARIES)命令將源文件與庫文件鏈接起來。

5.2 使用外部庫的方式

為了能支持各種常見的庫和包,CMake自帶了很多模塊。可以通過命令cmake --help-module-list得到你的CMake支持的模塊的列表,或者直接查看模塊路徑。比如Ubuntu上,模塊的路徑是:
/usr/share/cmake/Modules/

讓我們以bzip2庫為例。CMake中有個FindBZip2.cmake 模塊。只要使用 find_package(BZip2) 調用這個模塊,cmake會自動給一些變量賦值,然後就可以在CMakelists.txt中使用它們了。變量的列表可以查看cmake模塊文件,或者使用命令 cmake –help-module FindBZip2 。

比如一個使用bzip2的簡單程序,編譯器需要知道 bzlib.h 的位置,鏈接器需要找到bzip2庫(動態鏈接的話,Unix上是 libbz2.so 類似的文件,Windows上是 libbz2.dll )。

cmake_minimum_required(VERSION 2.8)
project(helloworld)
add_executable(helloworld hello.c)
find_package(BZip2)
if (BZIP2_FOUND)
  include_directories(${BZIP_INCLUDE_DIRS})
  target_link_libraries (helloworld ${BZIP2_LIBRARIES})
endif (BZIP2_FOUND
5.3 find_package中的參數定義

FIND_PACKAGE(<name> [version] [EXACT] [QUIET] [[REQUIRED|COMPONENTS] [ componets... ] ] )

1 version參數
需要一個版本號,它是正在查找的包應該兼容的版本號。
eg:
find_package(Boost ${boost_version})

2 EXACT選項
要求版本號必須精確匹配。如果在find-module內部對該命令的遞歸調用沒有給定[version]參數,那麽[version]和EXACT選項會自動地從外部調用前向繼承。對版本的支持目前只存在於包和包之間。
eg:
find_package(Boost ${boost_version} EXACT REQUIRED)

3 QUIET 參數:
禁掉沒有被發現時的警告信息。對應於Find.cmake模塊中的 NAME_FIND_QUIETLY。
eg:
find_package(Boost ${boost_version} EXACT QUIET)

4 REQUIRED 參數
其含義是指是工程必須的,表示如果報沒有找到的話,cmake的過程會終止,並輸出警告信息。對應於Find.cmake模塊中的 NAME_FIND_REQUIRED 變量。
eg:
find_package(Boost REQUIRED COMPONENTS system)

COMPONENTS參數
REQUIRED選項之後,或者如果沒有指定REQUIRED選項但是指定了COMPONENTS選項在它們的後面可以列出一些與包相關(依賴)的部件清單(components list)
eg:
find_package(Boost REQUIRED COMPONENTS system)

6. 其他

cmake中一些預定義變量
  • PROJECT_SOURCE_DIR 工程的根目錄
  • PROJECT_BINARY_DIR 運行cmake命令的目錄,通常是${PROJECT_SOURCE_DIR}/build
  • CMAKE_INCLUDE_PATH 環境變量,非cmake變量
  • CMAKE_LIBRARY_PATH 環境變量
  • CMAKE_CURRENT_SOURCE_DIR 當前處理的CMakeLists.txt所在的路徑
  • CMAKE_CURRENT_BINARY_DIR target編譯目錄
    使用ADD_SURDIRECTORY(src bin)可以更改此變量的值
    SET(EXECUTABLE_OUTPUT_PATH <新路徑>)並不會對此變量有影響,只是改變了最終目標文件的存儲路徑
  • CMAKE_CURRENT_LIST_FILE 輸出調用這個變量的CMakeLists.txt的完整路徑
  • CMAKE_CURRENT_LIST_LINE 輸出這個變量所在的行
  • CMAKE_MODULE_PATH 定義自己的cmake模塊所在的路徑
    SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然後可以用INCLUDE命令來調用自己的模塊
  • EXECUTABLE_OUTPUT_PATH 重新定義目標二進制可執行文件的存放位置
  • LIBRARY_OUTPUT_PATH 重新定義目標鏈接庫文件的存放位置
  • PROJECT_NAME 返回通過PROJECT指令定義的項目名稱
  • CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 用來控制IF ELSE語句的書寫方式
系統信息
  • CMAKE_MAJOR_VERSION cmake主版本號,如2.8.6中的2
  • CMAKE_MINOR_VERSION cmake次版本號,如2.8.6中的8
  • CMAKE_PATCH_VERSION cmake補丁等級,如2.8.6中的6
  • CMAKE_SYSTEM 系統名稱,例如Linux-2.6.22
  • CAMKE_SYSTEM_NAME 不包含版本的系統名,如Linux
  • CMAKE_SYSTEM_VERSION 系統版本,如2.6.22
  • CMAKE_SYSTEM_PROCESSOR 處理器名稱,如i686
  • UNIX 在所有的類UNIX平臺為TRUE,包括OS X和cygwin
  • WIN32 在所有的win32平臺為TRUE,包括cygwin
開關選項
  • BUILD_SHARED_LIBS 控制默認的庫編譯方式。如果未進行設置,使用ADD_LIBRARY時又沒有指定庫類型,默認編譯生成的庫都是靜態庫 (可在t3中稍加修改進行驗證)
  • CMAKE_C_FLAGS 設置C編譯選項
  • CMAKE_CXX_FLAGS 設置C++編譯選項

運行

cmake .
make

c++ cmakelist 詳解