Cmake
簡介
cmake
- 全稱Cross Platform Make,起初爲了跨平臺使用,後被廣泛使用
- 特點:開放源代碼,具有BSD許可;支持Linux、Mac、windows等不同操作系統;編譯語言簡單,易用;編程高效(比autotools快40%),可擴展(ros中catkin,ament,colcon都是基於cmake構建)
gcc
- 由GNU開發的編程語言譯器,C/C++,Java等語言的開發;
- 當項目簡單,可以gcc/g++編譯目標和項目;
- 但比較複雜時,只用gcc組織編譯架構變得極其困難;
Makefile
- Makefile是有條理的gcc編譯命令的文件,利用make工具來執行Makefile文件的編譯指令;
- 當程序簡單時,可以手寫Makefile;
- 當程序複雜時,一般利用CMake和autotools來自動生成Makefile;
CMake語法的主體框架
- 基礎命令
command(arg1 arg2...). #運行命令
set(var_name var_value). #定義變量,或給已存在的變量賦值
commond(arg1 ${var_name}). #使用變量
- 工程配置部分
cmake_minimum_required(VERSION num). #CMake最低版本號要求
project(cur_project_name). #項目信息
set(CMAKE_CXX_FLAGS "XXX") #設定編譯器版本,如-std=c++11
set(CMAKE_BUILD_TYPE "XXX") #設定編譯模式,如Debug/Release
- 依賴執行部分
find_package(std_lib_name VERSION REQUIRED). #引入外部依賴
add_library(<name> [lib_type] source1). #生成庫類型(動態,靜態)
include_directories(${std_lib_name_INCLUDE_DIRS}). #指定include路徑,放在add_executable前面
add_executable(cur_project_name xxx.cpp). #指定生成目標
target_link_libraries(${std_lib_name_LIBRARIES}). #指定libraries路徑,放在add_executable後面
- 其他輔助部分
function(function_name arg) #定義一個函數
add_subdirectory(dir) #定義一個子目錄,用於編譯子目錄下有cmakelist文件,編譯完成後,在子目錄下會生成對應的庫
AUX_SOURCE_DIRECTORY(. SRC_LIST) #查找當前目錄所有文件,並保存到SRC_LIST變量中
FOREACH(one_dir ${SRC_LIST})
MESSAGE(${one_dir}) #使用message進行打印
ENDFOREACH(one_dir)
- 判斷控制部分
if(expression)
COMMAND1(ARGS)
ELSE(expression)
COMMAND2(ARGS)
ENDIF
IF(var)
IF(NOT var)
IF(var1 AND var2)
IF(var1 OR var2)
IF(COMMAND cmd). #當給定的cmd確實是命令並可以調用是爲真
IF(EXISTS dir)
IF(EXISTS file)
IF(IS_DIRECTORY dirname)
IF(file1 IS_NEWER_THAN file2). #當file1比file2新,爲真
IF(variable MATCHES regex). #符合正則
WHILE(condition)
COMMAND1(ARGS)
ENDWHILE(condition)
CMake的常用指令及變量
- ADD_DEFINITIONS
(1)爲源文件的編譯添加由-D引入的宏定義。
(2)命令格式:add_definitions(-DFOO -DBAR …)
例如:add_definitions(-DWIN32) - OPTION
(1)提供用戶可以選擇的選項
(2)命令格式:option( "description [initial value])
例如:
option(USR_MYMATH
"use tutorial......."
ON)
- ADD_CUSTOM_COMMAND/TARGET
(1)COMMAND:爲工程添加一條自定義的構建規則
(2)TARGET:用於給指定名稱的目標執行指定的命令,該目標沒有輸出文件,並始終被構建。
例如:
#僞代碼:爲了說明生成自定義的命令
add_custom_command(TARGET ${CV_ADVANCE_NAME}
PRE_BUILD
COMMAND "僞代碼 find_package std_msgs"
)
#爲了說明導入生成自定義構建的命令
add_custom_target(CV_ADVANCE ALL
DEPENDS ${CV_ADVANCE_NAME}. #依賴add_custom_command輸出的package包
COMMENT "ros package std_msgs"
)
- ADD_DEPENDENCIES
(1)用於解決鏈接時依賴的問題,
(2)當定義的target依賴的另一個target,確保在源碼編譯 本target之前,其他的target已經被構建,使用該語句
#添加一個自定義構建的規則
add_custom_target(CV_ADVANCE DEPENDS ${CV_ADVANCE_NAME})
#添加一個可執行文件
add_executable(${PROJECT_NAME} ${SRC_LIST})
#鏈接一個標準的庫文件
target_link_libraries(${PROJECT_NAME} ${std_lib_name_LIB})
#爲項目鏈接一個依賴文件,項目程序依賴CV_ADVANCE
add_dependencies(${PROJECT_NAME} CV_ADVANCE)
- INSTALL
(1)用於定義安裝規則,安裝的內容可以包括目標二進制、 動態庫、靜態庫以及文件、目錄、腳本等。
(2)常用的如OpenCV一般情況下安裝到系統目錄,即 /usr/lib, /usr/bin和/usr/include
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic)
- TARGET_INCLUDE_DIRECTORIES
(1)設置include文件查找的目錄,具體包含頭文件應用形 式,安裝位置等
target_include_directories(<target>[SYSTEM][BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items])
- SET_TARGET_PROPERTIES
(1)設置目標的一些屬性來改變它們構建的方式。
set_target_properties(target1 target2 ...
PROPERTIES prop1 values
prop2 value2)
- ENABLE_TESTING/ADD_TEST
(1)[enable_testing] :用來控制Makefile是否構建test目標。
(2)[add_test] : 一般需要和enable_testing()配合使用.
(3)ADD_TEST(testname Exename arg1 arg2 …)
(4)生成makefile後可用make test執行測試
ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main)
ENABLE_TESTING()
實例
預備概念
- 靜態庫
(1)定義:鏈接階段,庫中目標文件所含的所有將被程序使用的函數的機器碼,被copy到最終的可執行文件中。因此對應的鏈接方式稱爲靜態鏈接。
(2)特點:靜態庫對函數庫的鏈接是放在編譯時期完成的;程序在運行時與函數庫再無瓜葛,移植方便;運行效率相對快;佔用磁盤和內存空間,因爲所有相關的目標文件與牽涉到的函數庫被鏈接合成一個可執行文件。 - 動態庫
(1)定義:程序編譯時並不會被連接到目標代碼中,而是在程序運行是才被載入。
(2)特點:可執行文件只包含它需要的函數的引用表,而不是所有的函數代碼;只有在程序執行時, 那些需要的函數代碼才被拷貝到內存中;動態庫在程序運行是才被載入,也解決了靜態庫對程序的更新、部署和發佈頁會帶來麻煩;用戶只需要更新動態庫即可,增量更新。 - 靜態庫是犧牲了空間效率,換取了時間效率; 動態庫是犧牲了時間效率,換取了空間效率;
- 使用find_package
(1)cmake不提供搜索庫的方法,需要手動設置環境變量
(2)模塊模式和配置模式,modeule和config
(3)指令按照優先級順序在指定路徑查找Findxxx.cmake和xxxConfig.cmake
(4)cmake能夠找到這兩個文件中的任何一個,我們都能成功使用該庫。
- FIND_PACKAGE-----【MODULE】
(1)FindXXX.cmake文件定義
find_path(hello_cv_install_include_dir
NAMES hell_cv.h
#<必須絕對路徑,推薦安裝在“/usr/share/cmake/Modules/”>
PATHS “.........”)
find_library(hello_cv_install_lib_libarary
NAMES hello_cv_install_lib
PATHS "............")
IF(hello_cv_install_include_dir AND hello_cv_install_lib_libarary)
SET(hello_cv_install_lib_found TRUE)
ENDIF(hello_cv_install_include_dir AND hello_cv_install_lib_libarary)
IF (hello_cv_install_lib_found)
IF (NOT hello_cv_install_lib_FIND_QUIETLY)
MESSAGE(STATUS "")
ENDIF(hell0_cv_install_lib_FIND_QUIETLY)
ELSE (hello_cv_install_lib_found)
IF (NOT hello_cv_install_lib_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "")
ENDIF(hello_cv_install_lib_FIND_REQUIRED)
ENDIF (hello_cv_install_lib_found)
(2)庫文件的生成
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_install_lib)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_install_lib STATIC src/hello_cv_1.cpp)
#設置指定的cmake文件生成目錄
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
#設置install的生成目錄,統一管理三個文件
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
install(FILES cmake/Findhello_cv_install_lib.cmake DESTINATION cmake)
install(FILES include/hello_cv_1.h DESTINATION include)
install(TARGETS hello_cv_install_lib ARCHIVE DESTINATION lib) #將靜態庫安裝到${CMAKE_INSTALL_PREFIX}/lib下
(3)庫文件的導入
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_outer_lib)
add_compile_options(-std=c++11)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../hello_cv_install_lib/install/cmake/)
find_package(hello_cv_install_lib REQUIRED)
if(hello_cv_install_lib_found)
include_directories(${hello_cv_install_include_dir})
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME}
${hello_cv_install_lib_libarary})
else(hello_cv_install_lib_found)
message(FATAL_ERROR "")
endif(hello_cv_install_lib_found)
- FIND_PACKAGE----配置模式
(1)庫文件的生成
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_add_static_lib)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_add_static_lib STATIC src/hello_cv_1.cpp)
#設置install安裝目錄,類似於cmake -DCMAKE_INSTALL_PREFIX指令
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/include)
#設置自定義的install路徑
target_include_directories(hello_cv_add_static_lib PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
#設置目標include的頭文件的屬性
set_target_properties(hello_cv_add_static_lib PROPERTIES
PUBLIC_HEADER "include/hello_cv_1.h")
install(TARGETS hello_cv_add_static_lib
EXPORT hello_cv_add_static_lib-targets #供外部庫使用的申明
PUBLIC_HEADER DESTINATION include
ARCHIVE DESTINATION lib) #將靜態庫安裝到${CMAKE_INSTALL_PREFIX}/lib下
install(EXPORT hello_cv_add_static_lib-targets
NAMESPACE hello_cv_add_static_lib::
#外部要使用find_package一定要使用,命名必須XX-config.cmake
FILE hello_cv_add_static_lib-config.cmake
DESTINATION lib/cmake.hello_cv_add_static_lib)
(2)庫文件的導入
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_add_outer_lib)
add_compile_options(-std=c++11)
set(CMAKE_PREFIX_PATH ${PROJECT_SOURCE_DIR}/../hello_cv_add_static_lib/install)
find_package(hello_cv_add_static_lib REQUIRED)
if(hello_cv_add_static_lib_FOUND)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} hello_cv_add_static_lib::hello_cv_add_static_lib)
else(hello_cv_add_static_lib_FOUND)
message(FATAL_ERROR ".......")
endif(hello_cv_add_static_lib_FOUND)
實例1
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_1)
add_compile_options(-std=c++11)
include_directories(include)
add_executable(hello_cv_1 src/main.cpp src/hello_cv_1.cpp)
實例2
添加一個庫
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_2_add_static)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_2_add_static STATIC/SHARED/MODULE src/hello_cv_1.cpp)
實例3
同時添加動靜態庫
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_3_add_shared_lib)
add_compile_options(-std=c++11)
#設置庫文件的輸出目錄
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
include_directories(include)
#靜態庫xxx.a,動態庫xxx.so
add_library(hello_cv_3_lib SHARED src/hello_cv_1.cpp)
add_library(hello_cv_3_add_static_lib STATIC src/hello_cv_1.cpp)
#設置文件名替換,注意和上行的先後順序
set_target_properties(hello_cv_3_add_static_lib PROPERTIES OUTPUT_NAME "hello_cv_3_lib")
實例4
鏈接一個內部庫的依賴
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_4)
add_compile_options(-std=c++11)
#設置庫文件的輸出目錄,默認輸出到執行目錄空間下
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
set(LIBRARY_OUTPUT_NAME ${PROJECT_NAME})
include_directories(include)
#靜態庫xxx.a,動態庫xxx.so
add_library(hello_cv_4_lib SHARED src/hello_cv_1.cpp)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} hello_cv_4_lib)
實例5
利用INSTALL的方式導入
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_5)
add_compile_options(-std=c++11)
include_directories(include)
add_library(hello_cv_5 STATIC src/hello_cv_1.cpp)
#將頭文件和庫文件安裝到/usr目錄下
set(CMAKE_INSTALL_PREFIX /usr)
#如不指定,默認安裝到/usr/local
message(${CMAKE_INSTALL_PREFIX})
install(FILES include/hello_cv_1.h DESTINATION include)
install(TARGETS hello_cv_5 ARCHIVE DESTINATION lib)
#將靜態庫安裝到${CMAKE_INSTALL_PREFIX}/lib目錄下
#導入庫
cmake_minimum_required(VERSION 2.8.3)
project(hello_cv_5)
add_compile_options(-std=c++11)
include_directories(/usr/include)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} /usr/lib/libhello_cv_5.a)
實例6
cpp文件和h文件分開,編譯成可執行文件
cmake_minimum_required(VERSION 2.8)
add_compile_options(-std=c++11 -fstack-protector-all -Wl,-z,relro,-z,noexecstack -fPIC)
project(hello)
find_package(XXX REQUIRED)
include_directories(${XXX1_INCLUDE_DIRS})
if(NOT DEFINED XXX2_PATH)
message(FATAL_ERROR "please use -DXXX2_PATH=<>")
endif()
set(XXX2_DIR ${XXX2_PATH} CACHE INTERNAL "XXX location")
link_directories(${XXX2_DIR}/lib)
#h文件位置
include_directories(. ./include/)
# main,cpp位置
file(GLOB SRC source/*.cpp)
add_executable(main ${SRC})
target_link_libraries(facedemo_main ${XXX1_LIBRARIES} pthread avcodec avdevice avfilter avformat avutil swscale)