本文屬於原創,轉載註明出處,歡迎關注微信小程序小白AI博客
微信公衆號小白AI
或者網站 https://xiaobaiai.net 或者個人CSDN http://blog.csdn.net/freeapelinux
[TOC]android
首先說明的是本篇文章不從cmake的整個語法上去講述,而是從一個實際項目的構建上入手,去了解如何優雅的去構建一個軟件項目,搭建一個C/C++
軟件項目基本的依賴組件,最後造成一個構建C/C++
軟件項目的模板,方便後面新項目的重複使用。相信對咱們平常的軟件項目構建都會有很好的收穫。廢話不都說,開始。c++
首先熟悉cmake的一些基操,咱們就能夠信手捏來的、優雅去構建一個項目,避免踩到沒必要要的坑。涉及到的有:git
這裏歸納性說明下經常使用的cmake知識,總的來講cmake的做用就是讓咱們找到依賴的頭文件和庫文件,去編譯源文件、連接目標文件(靜態庫也是目標文件的一個集合),最後生成可執行文件或動/靜態庫:程序員
INCLUDE_DIRECTORIES
將給定的目錄添加到編譯器用於搜索包含文件(如頭文件)的目錄中,相對路徑被解釋爲相對於當前源目錄。注意目錄僅是被添加到當前CMakeLists文件,做用於當前CMakeLists文件相關的庫、可執行文件或者子模塊
編譯,對於兩個不一樣CMakeLists.cmake並列的做用是無效的。區別於TARGET_INCLUDE_DIRECTORIES
,這個命令的做用只是做用於指定的目標,爲指定的目標添加搜索路徑。相似的還有TARGET_LINK_LIBRARIES
命令(添加須要連接的庫文件目錄)。PROJECT_SOURCE_DIR
: 無疑只要是有包含最新PROJECT()命令聲明的CMakeLists.txt,則都是相對當該CMakeLists.txt路徑。CMAKE_SOURCE_DIR
: 構建整個項目時,可能你依賴的第三方項目,這個變量的值就是最頂層CMakeLists.txt的路徑。在 find_path
和 find_library
以及 find_package
時,會搜索一些默認的路徑。當咱們將一些lib安裝在非默認搜索路徑時,cmake就無法搜索到了,可設置:github
find_file
命令實現尋找依賴庫,find_file尋找到的結果存放到CACHE變量,示例:# Once done, this will define # # NANOMSG_INCLUDE_DIR - the NANOMSG include directory # NANOMSG_LIBRARY_DIR - the SPDLOG library directory # NANOMSG_LIBS - link these to use NANOMSG # # SPDLOG_INCLUDE_DIR - the SPDLOG include directory # SPDLOG_LIBRARY_DIR - the SPDLOG library directory # SPDLG_LIBS - link these to use SPDLOG MACRO(LOAD_LIBNANOMSG os arch) SET(3RDPARTY_DIR ${PROJECT_SOURCE_DIR}/3rdparty/target/${${os}}_${${arch}}) MESSAGE(STATUS "3RDPARTY_DIR: ${3RDPARTY_DIR}") FIND_FILE(NANOMSG_INCLUDE_DIR include ${3RDPARTY_DIR} NO_DEFAULT_PATH) FIND_FILE(NANOMSG_LIBRARY_DIR lib ${3RDPARTY_DIR} NO_DEFAULT_PATH) SET(NANOMSG_LIBS nanomsg pthread anl PARENT_SCOPE ) IF(NANOMSG_INCLUDE_DIR) MESSAGE(STATUS "NANOMSG_LIBS : ${NANOMSG_LIBS}") ELSE() MESSAGE(FATAL_ERROR "NANOMSG_LIBS not found!") ENDIF() ENDMACRO()
# set target if (NOT YOUR_TARGET_OS) set(YOUR_TARGET_OS linux) endif() if (NOT YOUR_TARGET_ARCH) set(YOUR_TARGET_ARCH x86_64) endif() if (NOT YOUR_BUILD_TYPE) set (YOUR_BUILD_TYPE Release) endif() ...... if(${YOUR_TARGET_ARCH} MATCHES "(arm*)|(aarch64)") ...... elseif(${YOUR_TARGET_ARCH} MATCHES x86*) ......
CMAKE_TOOLCHAIN_FILE
變量,MESSAGE(STATUS "Configure Cross Compiler") IF(NOT TOOLCHAIN_ROOTDIR) MESSAGE(STATUS "Cross-Compiler defaut root path: $ENV{HOME}/Softwares/arm-himix200-linux") SET(TOOLCHAIN_ROOTDIR "$ENV{HOME}/Softwares/arm-himix200-linux") ENDIF() SET(CMAKE_SYSTEM_NAME Linux) SET(CMAKE_SYSTEM_PROCESSOR arm) SET(CMAKE_C_COMPILER ${TOOLCHAIN_ROOTDIR}/bin/arm-himix200-linux-gcc) SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_ROOTDIR}/bin/arm-himix200-linux-g++) # set searching rules for cross-compiler SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) SET(YOUR_TARGET_OS linux) SET(YOUR_TARGET_ARCH armv7-a) SET(CMAKE_CXX_FLAGS "-std=c++11 -march=armv7-a -mfloat-abi=softfp -mfpu=neon-vfpv4 ${CMAKE_CXX_FLAGS}")
AUX_SOURCE_DIRECTORY
不會遞歸包含子目錄,僅包含指定的dir目錄ADD_SUBDIRECTORY
子模塊的編譯,能夠將子文件夾中或者指定外部文件夾下CMakeLists.txt執行相關編譯工做。ADD_LIBRARY
編譯一個動/靜態庫或者模塊,設定的名字需在整個工程中是獨一無二的,並且在整個同一個工程中,跟父子文件夾路徑無關,咱們即可以經過TARGET_LINK_LIBRARIES
依賴該模塊。ADD_DEFINITIONS(-DTEST -DFOO="foo")
添加FOO
和TEST
宏定義。對於一個較大的軟件項目,咱們會依賴不少第三方的項目,包括源碼依賴或者庫依賴,而後完整的構建本身的軟件項目,則須要去構建依賴項目或者找到咱們所須要庫;另外,軟件項目會考慮到可移植性,即可以在不一樣的平臺上也可以很好友的去構建項目以及將項目轉移到另外一個開發環境時可以快速的開始構建。docker
除了上面所說的,咱們還須要考慮咱們實際軟件項目的架構結構,源碼結構,可讓開發人員更清晰的、更快速的瞭解整個項目。小程序
除此以外,C/C++ 程序員長期以來手動管理依賴,即手動查找、安裝依賴,再配置構建工具(如 cmake)使用依賴。 cmake 還提供了一系列 find_package 方法幫助簡化配置依賴, cmake 還支持多項目/模塊管理,若是依賴源碼同時被 cmake 管理構建,那麼狀況會簡單不少,這種方式稱爲源碼級依賴管理。 隨着代碼管理工具 git 出現並被普遍使用,git submodule 提供了一種不錯的源碼級依賴管理辦法。微信小程序
綜上,優雅的構建軟件項目,咱們實現:bash
另外,咱們還實現一個可複用的C/C++最小開發框架(這個到後續文章中講述):
支持經常使用相關基礎操做組件
一個獨立的應用,應用模塊之間是相互聯繫的,彼此難以分開,這樣簡單的將全部源文件放一塊兒,頭文件放一塊兒,這個對於不是很複雜的應用是很快速的去開始構建和源文件修改操做:
. ├── 3rdparty ├── cmake ├── include ├── src ├── doc ├── tests ├── benchmarks ├── docker ├── CMakeLists.txt
源文件與頭文件分功能模塊存放,這種方式是比較簡單,可是若是成爲其餘項目的3rdparty,則須要在安裝上將頭文件分離出來,不能很方便的被其餘項目直接引用,我的以爲適用於App類項目,而非SDK項目(好比nanomsg這個開源消息中間件庫就是將頭文件和源文件放一塊兒,可是做爲SDK供外部連接就不是很直接、很方便了,須要作install操做以後才能夠或者是將頭文件搜索範圍設置到依賴項目的src級別,且src目錄下模塊分類很明確):
├── 3rdparty ├── submodule # 存放源碼依賴 ├── target # 存放庫依賴 ├── CMakeLists.txt ├── cmake # 存放 find_package cmake文件 ├── cmake ├── platforms │ └── linux │ └── arm.toolchain.cmake ├── src ├── moudle1 ├── source & include file ├── moudle2 ├── source & include file ├── ...... ├── doc ├── tests ├── samples ├── benchmarks ├── docker ├── CMakeLists.txt
該軟件項目能夠分爲不少模塊,各個模塊能夠互相獨立,也能夠組合在一塊兒,典型的如opencv項目,固然這個也適用於應用項目,可是應用項目的話目錄結構太深,開發編輯上稍有不便:
├── 3rdparty ├── cmake ├── platforms │ └── linux │ └── arm.toolchain.cmake ├── include 該目錄只是各功能模塊頭文件的一個彙總包含 ├── modules ├── moudle1 ├── src ├── include ├── moudle2 ├── ...... ├── doc ├── tests ├── samples ├── benchmarks ├── docker ├── CMakeLists.txt
這裏咱們只去實現模板二,其餘模板大同小異。如上面模板章節所述,咱們
. ├── 3rdparty # 第三方庫源碼依賴和庫依賴存放位置 │ ├── CMakeLists.txt # 第三方庫源碼依賴編譯CMakeLists文件 │ ├── spdlog # 源碼依賴示例項目spdlog(github可搜索) │ └── target # 庫依賴存放目錄 │ ├── linux_armv7-a # 以平臺和架構命名區分 │ │ ├── include # 頭文件存放目錄 │ │ └── lib # 庫文件存放目錄 │ └── linux_x86-64 │ ├── include │ └── lib ├── cmake # 存放項目相關的cmakem模塊文件 │ ├── load_3rdparty.cmake │ ├── messagecolor.cmake │ ├── toolchain_options.cmake │ └── utils.cmake ├── CMakeLists.txt # 項目根目錄CMakeLists文件,cmake入口文件 ├── conf # 項目配置文件存放目錄 ├── doc # 項目文檔存放目錄 ├── platforms # 項目平臺性相關內容存放目錄,包括交叉編譯 │ └── linux │ └── arm.himix200.cmake ├── README.md # 項目說明 ├── scripts # 相關腳本存放目錄,包括持續集成和部署相關 ├── src # 項目源碼目錄 │ ├── CMakeLists.txt │ ├── common │ ├── logger │ └── main └── tests # 測試示例源碼存放目錄 ├── CMakeLists.txt └── test_logger.cpp
不論是SDK或者是APP項目,都會有一個版本,用來記錄軟件發佈的每一個節點。軟件版本能夠方便用戶或者本身清楚的知道每一個版本都有哪些內容的更新,能夠對版本作出使用的選擇或者解決版本中遇到的bug。實現版本的管理,須要可以在編譯過程當中清楚的體現當前版本號,在軟件中也可以獲取版本號。這裏版本編號的管理使用常見的major.minor(.patch)
格式,major是最大的版本編號,minor爲其次,patch對應着小版本里的補丁級別。當有極大的更新時,會增長major的版號,而當有大更新,但不至於更新major時,會更新minor的版號,若更新比較小,例如只是bug fixing,則會更新patch的版號。版本號格式示例:v1.0
、v1.2.2
等。
在優雅的構建軟件模板中,咱們將版本信息放置於src/common/version.hpp
文件中:
注:全部的文件路徑都是相對項目根目錄而言。
#pragma once // for cmake // 用於在CMakeLists文件中解析用 // 0.1.0 #define HELLO_APP_VER_MAJOR 0 #define HELLO_APP_VER_MINOR 1 #define HELLO_APP_VER_PATCH 0 #define HELLO_APP_VERSION (HELLO_APP_VER_MAJOR * 10000 + HELLO_APP_VER_MINOR * 100 + HELLO_APP_VER_PATCH) // for source code // 用於在項目源碼中獲取版本號字符串 // v0.1.0 #define _HELLO_APP_STR(s) #s #define HELLO_PROJECT_VERSION(major, minor, patch) "v" _HELLO_APP_STR(major.minor.patch)
在CMakeLists模塊文件中咱們去解析該文件獲取版本號到CMake變量中,在cmake/utils.cmake
添加宏函數:
FUNCTION(hello_app_extract_version) FILE(READ "${CMAKE_CURRENT_LIST_DIR}/src/common/version.hpp" file_contents) STRING(REGEX MATCH "HELLO_APP_VER_MAJOR ([0-9]+)" _ "${file_contents}") IF(NOT CMAKE_MATCH_COUNT EQUAL 1) MESSAGE(FATAL_ERROR "Could not extract major version number from version.hpp") ENDIF() SET(ver_major ${CMAKE_MATCH_1}) STRING(REGEX MATCH "HELLO_APP_VER_MINOR ([0-9]+)" _ "${file_contents}") IF(NOT CMAKE_MATCH_COUNT EQUAL 1) MESSAGE(FATAL_ERROR "Could not extract minor version number from version.hpp") ENDIF() SET(ver_minor ${CMAKE_MATCH_1}) STRING(REGEX MATCH "HELLO_APP_VER_PATCH ([0-9]+)" _ "${file_contents}") IF(NOT CMAKE_MATCH_COUNT EQUAL 1) MESSAGE(FATAL_ERROR "Could not extract patch version number from version.hpp") ENDIF() SET(ver_patch ${CMAKE_MATCH_1}) SET(HELLO_APP_VERSION_MAJOR ${ver_major} PARENT_SCOPE) SET (HELLO_APP_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE) ENDFUNCTION()
在根目錄CMakeLists中調用版本宏:
CMAKE_MINIMUM_REQUIRED(VERSION 3.4) #-------------------------------------------- # Project setting #-------------------------------------------- INCLUDE(cmake/utils.cmake) HELLO_APP_EXTRACT_VERSION() PROJECT(HelloApp VERSION ${HELLO_APP_VERSION} LANGUAGES CXX) MESSAGE(INFO "--------------------------------") MESSAGE(STATUS "Build HelloApp: ${HELLO_APP_VERSION}")
在後面的動靜態庫生成中就能夠設定SOVERSION了,如:
SET_TARGET_PROPERTIES(MyLib PROPERTIES VERSION ${HELLO_APP_VERSION} SOVERSION ${HELLO_APP_VERSION_MAJOR})
這樣就會生成一個liMyLibr.so => liMyLib.so.0 => libMyLib.so.0.1.1
的庫和相關軟連接。不過這個操做謹慎使用,由於在android平臺jni依賴帶版本的庫是沒法找到的。
第三方庫依賴須要咱們本身寫庫和頭文件查找函數,三方庫存放位置以平臺和架構做爲區分,目錄結構隨着工程的建立就基本不會改變了。庫發現宏函數以下示例:
# Once done, this will define # # SPDLOG_INCLUDE_DIR - the SPDLOG include directory # SPDLOG_LIBRARY_DIR - the SPDLOG library directory # SPDLG_LIBS - link these to use SPDLOG # # ...... MACRO(LOAD_LIBSPDLOG os arch) SET(3RDPARTY_DIR ${PROJECT_SOURCE_DIR}/3rdparty/target/${${os}}_${${arch}}) MESSAGE(STATUS "3RDPARTY_DIR: ${3RDPARTY_DIR}") FIND_FILE(SPDLOG_INCLUDE_DIR include ${3RDPARTY_DIR} NO_DEFAULT_PATH) FIND_FILE(SPDLOG_LIBRARY_DIR lib ${3RDPARTY_DIR} NO_DEFAULT_PATH) SET(SPDLOG_LIBS spdlog pthread #PARENT_SCOPE no parent ) IF(SPDLOG_INCLUDE_DIR) SET(SPDLOG_LIBRARY_DIR "${SPDLOG_LIBRARY_DIR}/spdlog") MESSAGE(STATUS "SPDLOG_INCLUDE_DIR : ${SPDLOG_INCLUDE_DIR}") MESSAGE(STATUS "SPDLOG_LIBRARY_DIR : ${SPDLOG_LIBRARY_DIR}") MESSAGE(STATUS "SPDLOG_LIBS : ${SPDLOG_LIBS}") ELSE() MESSAGE(FATAL_ERROR "SPDLOG_LIBS not found!") ENDIF() ENDMACRO()
注意:如SPDLOG_LIBS
變量若是宏函數在根目錄CMakeLists中調用,因此變量做用域能夠做用到全部子目錄,若是不是在根目錄調用,則須要設置PARENT_SCOPE
屬性。
在主CMakeLists中調用宏函數實現三方庫的信息導入:
INCLUDE(cmake/load_3rdparty.cmake) IF(NOT YOUR_TARGET_OS) SET(YOUR_TARGET_OS linux) ENDIF() IF(NOT YOUR_TARGET_ARCH) SET(YOUR_TARGET_ARCH x86-64) ENDIF() MESSAGE(STATUS "Your target os : ${YOUR_TARGET_OS}") MESSAGE(STATUS "Your target arch : ${YOUR_TARGET_ARCH}") LOAD_LIBSPDLOG(YOUR_TARGET_OS YOUR_TARGET_ARCH)
若是你想依賴第三方項目源碼,一塊兒編譯,則咱們能夠經過git submodule
來管理第三方源碼,實現源碼依賴和它的版本管理。固然你能夠不用git submodule,直接將源碼手動放入3rdparty
目錄中。
添加一個git submodule:
# url爲git項目地址 # path爲項目存放目錄,能夠多級目錄,目錄名通常爲項目名稱 # git add <url.git> <path> # 示例,執行後,會直接拉取項目源碼到3rdparty/spdlog目錄下,並建立.gitmodule在倉庫根目錄下 $ git submodule add https://github.com/gabime/spdlog.git 3rdparty/spdlog
還能夠作到帶指定分支進行添加操做:
# 注意:命令須要在項目根目錄下執行,第一次會直接拉取源碼,不用update $ git submodule add -b v1.x https://github.com/gabime/spdlog.git 3rdparty/spdlog $ git submodule update --remote
最後的.gitmodules
文件爲:
[submodule "3rdparty/spdlog"] path = 3rdparty/spdlog url = https://github.com/gabime/spdlog.git branch = v1.x
實現三方項目源碼編譯(首先你依賴的三方項目源碼是支持CMake構建方式的),在3rdparty/CMakeLists.txt
中編寫:
CMAKE_MINIMUM_REQUIRED(VERSION 3.4) PROJECT(HiApp3rdparty) ADD_SUBDIRECTORY(spdlog)
在根目錄CMakeLists.txt中包含3rdparty中CMakeLists.txt,就能夠編譯第三方庫了:
ADD_SUBDIRECTORY(3rdparty)
經過TARGET_LINK_LIBRARIES
就能夠指定第三方項目名稱實現連接。
好比咱們要添加一個日誌模塊,實現對spdlog
項目的一個二次封裝,更好的在本身的項目中使用,那麼咱們創建src/logger
目錄,裏面新建logger.hpp
、logger.cpp
和CMakeLists.txt
三個文件,其中CMakeLists.txt內容是對該日誌模塊實現編譯:
CMAKE_MINIMUM_REQUIRED(VERSION 3.4) AUX_SOURCE_DIRECTORY(. CURRENT_DIR_SRCS) ADD_LIBRARY(module_logger ${CURRENT_DIR_SRCS}) # SPDLOG_LIBS 爲spdlog項目庫名稱 TARGET_LINK_LIBRARIES(module_logger ${SPDLOG_LIBS})
而後在src/CMakeLists.txt
中包含該日誌模塊的編譯:
ADD_SUBDIRECTORY(logger)
在根目錄CMakeLists.txt
中包含子目錄src
,從而實現功能模塊的構建:
ADD_SUBDIRECTORY(src)
注: 爲了演示,庫依賴和源碼依賴都是用的spdlog,這裏實現日誌模塊的話須要選擇其中一種方式。
若是咱們須要實現可執行文件對日誌模塊的調用,咱們能夠添加src/main/main.cpp
文件,在src/CMakeLists.txt
中添加對可執行文件的編譯:
# main app SET(SRC_LIST ./main/main.cpp) ADD_EXECUTABLE(HiApp ${SRC_LIST}) # 配置可執行文件輸出目錄 SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) TARGET_LINK_LIBRARIES(HelloApp module_logger)
固然,若是使用c++11
的特性,咱們能夠專門建立一個cmake文件cmake/toolchain_options.cmake
來配置編譯選項,在其中配置c++11
編譯選項,並在主CMakeLists.txt中包含該cmake文件:
# compiler configuration # 從cmake3.1版本開始才支持CMAKE_CXX_STANDARD配置項 IF(CMAKE_VERSION VERSION_LESS "3.1") IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") ENDIF() ELSE() SET(CMAKE_CXX_STANDARD 11) ENDIF()
測試樣例放於tests
目錄,並在該目錄下創建CMakeLists.txt文件用於構建全部測試demo,並在主CMakeLists.txt下包含tests
目錄:
CMAKE_MINIMUM_REQUIRED(VERSION 3.4) PROJECT(Tests) INCLUDE_DIRECTORIES( ${SPDLOG_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/src ) LINK_DIRECTORIES( ${SPDLOG_LIBRARY_DIR} ) FILE(GLOB APP_SOURCES *.cpp) FOREACH(testsourcefile ${APP_SOURCES}) STRING(REGEX MATCH "[^/]+$" testsourcefilewithoutpath ${testsourcefile}) STRING(REPLACE ".cpp" "" testname ${testsourcefilewithoutpath}) ADD_EXECUTABLE( ${testname} ${testsourcefile}) SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin/tests) TARGET_LINK_LIBRARIES(${testname} ${SPDLOG_LIBS} module_logger ) ENDFOREACH(testsourcefile ${APP_SOURCES})
而後就能夠在tests
目錄下添加測試程序了,好比test_logger.cpp
或者更多的測試demo,tests/CMakeLists.txt
會自動將tests
目錄下全部源文件逐個進行可執行文件生成構建。整個測試樣例的構建就完成了。
CMake給咱們提供了交叉編譯的變量設置,即CMAKE_TOOLCHAIN_FILE
這個變量,只要咱們指定交叉編譯的cmake配置文件,那麼cmake會導入該配置文件的中編譯器配置,編譯選項配置等。咱們設計的交叉編譯工具鏈配置文件存放目錄在platforms/
下,這裏咱們使用華爲海思的一個編譯工具,咱們按類別命名,建立一個工具欄cmake配置文件platforms/linux/arm.himix200.cmake
:
MESSAGE(STATUS "Configure Cross Compiler") SET(CMAKE_SYSTEM_NAME Linux) SET(CMAKE_SYSTEM_PROCESSOR arm) SET(CMAKE_C_COMPILER arm-himix200-linux-gcc) SET(CMAKE_CXX_COMPILER arm-himix200-linux-g++) # set searching rules for cross-compiler SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) SET(YOUR_TARGET_OS linux) SET(YOUR_TARGET_ARCH armv7-a) SET(CMAKE_SKIP_BUILD_RPATH TRUE) SET(CMAKE_SKIP_RPATH TRUE) # set ${CMAKE_C_FLAGS} and ${CMAKE_CXX_FLAGS}flag for cross-compiled process #SET(CROSS_COMPILATION_ARM himix200) #SET(CROSS_COMPILATION_ARCHITECTURE armv7-a) # set g++ param # -fopenmp link libgomp SET(CMAKE_CXX_FLAGS "-std=c++11 -march=armv7-a -mfloat-abi=softfp -mfpu=neon-vfpv4 \ -ffunction-sections \ -fdata-sections -O2 -fstack-protector-strong -lm -ldl -lstdc++\ ${CMAKE_CXX_FLAGS}")
注意:交叉編譯工具鏈是須要在編譯主機上安裝好的。另外第三方庫庫依賴也須要對應編譯出工具鏈版本(源碼依賴除外)。
命令行執行交叉編譯:
$ mkdir build $ cd build $ cmake .. -DCMAKE_TOOLCHAIN_FILE=../platforms/linux/arm.himix200.cmake $ make -j
這樣就實現了交叉編譯,你也能夠配置其餘的交叉編譯工具鏈。
咱們還能夠自定義初始化cmake構建的message
命令打印顏色,能夠方便快速的凸顯出錯誤信息,咱們能夠建立一個文件cmake/messagecolor.cmake
:
IF(NOT WIN32) STRING(ASCII 27 Esc) SET(ColourReset "${Esc}[m") SET(ColourBold "${Esc}[1m") SET(Red "${Esc}[31m") SET(Green "${Esc}[32m") SET(Yellow "${Esc}[33m") SET(Blue "${Esc}[34m") SET(MAGENTA "${Esc}[35m") SET(Cyan "${Esc}[36m") SET(White "${Esc}[37m") SET(BoldRed "${Esc}[1;31m") SET(BoldGreen "${Esc}[1;32m") SET(BoldYellow "${Esc}[1;33m") SET(BOLDBLUE "${Esc}[1;34m") SET(BOLDMAGENTA "${Esc}[1;35m") SET(BoldCyan "${Esc}[1;36m") SET(BOLDWHITE "${Esc}[1;37m") ENDIF() FUNCTION(message) LIST(GET ARGV 0 MessageType) IF(MessageType STREQUAL FATAL_ERROR OR MessageType STREQUAL SEND_ERROR) LIST(REMOVE_AT ARGV 0) _message(${MessageType} "${BoldRed}${ARGV}${ColourReset}") ELSEIF(MessageType STREQUAL WARNING) LIST(REMOVE_AT ARGV 0) _message(${MessageType} "${BoldYellow}${ARGV}${ColourReset}") ELSEIF(MessageType STREQUAL AUTHOR_WARNING) LIST(REMOVE_AT ARGV 0) _message(${MessageType} "${BoldCyan}${ARGV}${ColourReset}") ELSEIF(MessageType STREQUAL STATUS) LIST(REMOVE_AT ARGV 0) _message(${MessageType} "${Green}${ARGV}${ColourReset}") ELSEIF(MessageType STREQUAL INFO) LIST(REMOVE_AT ARGV 0) _message("${Blue}${ARGV}${ColourReset}") ELSE() _message("${ARGV}") ENDIF()
在主CMakeLists.txt中導入該cmake文件,則能夠改變message命令各個級別打印的顏色顯示。
爲了方便debug,咱們在開發過程當中通常是編譯Debug
版本的庫或者應用,能夠利用gdb調試很輕鬆的就能夠發現錯誤具體所在。在主cmake文件中咱們只須要加以下設置便可:
IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose Release or Debug" FORCE) ENDIF() MESSAGE(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
在執行cmake命令的時候能夠設置CMAKE_BUILD_TYPE
變量值切換Debug
或者Release
版本編譯:
$ cmake .. -DCMAKE_BUILD_TYPE=Release
對於SDK項目,咱們須要對外提供頭文件和編譯完成後的庫文件,就須要用到cmake提供的install
命令了。
咱們安裝需求是:
src
目錄下的每一個模塊頭文件都可以安裝,並按原目錄存放安裝lib
目錄下bin
目錄首先模塊頭文件的安裝實現均在src/{module}/CMakeLists.txt
中實現,這裏是安裝目錄,並過濾掉.cpp
或者.c
文件以及CMakeLists.txt
文件,以logger
模塊爲例:
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${CMAKE_INSTALL_PREFIX}/include FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "CMakeLists.txt" EXCLUDE )
注意:在UNIX系統上,CMAKE_INSTALL_PREFIX
變量默認指向/usr/local
,在Windows系統上,默認指向c:/Program Files/${PROJECT_NAME}
。
而後是庫文件的安裝,也相關ADD_LIBRARY
命令調用後中實現:
INSTALL(TARGETS module_logger ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)
最後是可執行文件的安裝,跟安裝庫是同樣的,添加到ADD_EXECUTABLE
命令調用的後面,只是由於是可執行文件,屬於RUNTIME
類型,cmake會自動安裝到咱們設置的bin目錄,這裏以HelloApp
爲例:
INSTALL(TARGETS HelloApp ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)
執行安裝命令:
$ make install DESTDIR=$PWD/install
則會在相對當前目錄install/usr/local
目錄下生成:
. ├── bin │ ├── HelloApp │ └── test_logger ├── include │ ├── common │ │ ├── common.hpp │ │ └── version.hpp │ └── logger │ └── logger.hpp └── lib └── libmodule_logger.a
至此,安裝完成。
「工欲善其事,必先利其器」,把基礎築好,在軟件開發過程當中也是很重要的,就如項目中需求明確同樣,本篇文章我把C/C++
項目開發的總體框架造成一個模板,不斷總結改進,方便後續相似項目的快速開發。本篇文章也主要實現項目構建方面的內容,下一篇準備實現一個基本C/C++
框架所必須的基礎模塊,包括日誌模塊、線程池、經常使用基礎功能函數模塊、配置導入模塊、單元測試、內存泄露檢查等。若有問題或者改進,一塊兒來交流學習,最後歡迎你們關注個人公衆號小白AI
,不打廣告,不爲了寫而寫,只爲了分享本身的學習過程^_^。
整個構建模板源碼能夠在個人github上找到,歡迎star: https://github.com/yicm/CMakeCppProjectTemplate