項(xiàng)目最近需要在windows平臺(tái)上運(yùn)行,我花了幾周時(shí)間將linux服務(wù)器移植到Windows平臺(tái),目前已能正常運(yùn)行。然而,新的需求出現(xiàn)了,考慮到代碼結(jié)構(gòu)和組織在兩個(gè)平臺(tái)上是分開的,為了能夠同步維護(hù)兩邊的代碼,我們需要一個(gè)跨平臺(tái)的項(xiàng)目編譯解決方案。經(jīng)過(guò)調(diào)研,我們選擇了cmake工具。本文將詳細(xì)介紹使用cmake進(jìn)行生產(chǎn)項(xiàng)目的一些基礎(chǔ)知識(shí)。
一、CMake簡(jiǎn)介 您可能聽說(shuō)過(guò)多種Make工具,例如gnu Make、qt的qmake、微軟的MS nmake、BSD Make(pmake)、Makepp等。這些工具遵循不同的規(guī)范和標(biāo)準(zhǔn),所執(zhí)行的Makefile格式也各不相同。這給跨平臺(tái)軟件的編譯帶來(lái)了挑戰(zhàn):為了在不同平臺(tái)上編譯,必須為每種標(biāo)準(zhǔn)編寫Makefile,這無(wú)疑是一項(xiàng)繁瑣的工作。
CMake正是在此背景下應(yīng)運(yùn)而生的工具:它允許開發(fā)者編寫一種平臺(tái)無(wú)關(guān)的CMakeLists.txt文件來(lái)定制整個(gè)編譯流程,然后根據(jù)目標(biāo)用戶的平臺(tái)生成所需的本地化Makefile和工程文件,如Linux的Makefile或Windows的visual studio工程。從而實(shí)現(xiàn)“一次編寫,到處運(yùn)行”的目標(biāo)。
1、Linux安裝CMake 在Linux上,可以直接使用yum -y install cmake進(jìn)行安裝,默認(rèn)安裝的版本是CMake 2.8.12。
如果需要使用CMake的最新版本,可以訪問(wèn)CMake官方網(wǎng)站(https://www.php.cn/link/6943317304e0f076bc8f12dc02c48e9b。
建議直接使用yum安裝,或者從官方網(wǎng)站下載cmake-3.12.0-rc2-Linux-x86_64.tar.gz。
2、Windows安裝CMake 在Windows上安裝CMake,可以直接從CMake官方網(wǎng)站的下載頁(yè)面獲取,官方提供了msi安裝版本和源代碼,還提供了一些預(yù)編譯的版本。
建議在Windows上使用安裝版本,也可以下載預(yù)編譯的版本cmake-3.12.0-rc2-win64-x64.zip,但需要手動(dòng)設(shè)置環(huán)境變量。
Windows上既有命令行版本也有GUI版本,具體使用哪種版本取決于您的習(xí)慣。
設(shè)置環(huán)境變量的方法為:我的電腦->屬性->高級(jí)系統(tǒng)設(shè)置->環(huán)境變量,然后將CMake的路徑添加到path環(huán)境變量中。
二、CMake初體驗(yàn) 在本節(jié)中,我們假設(shè)已經(jīng)有一個(gè)項(xiàng)目,并編寫好了CMake的配置文件CMakeLists.txt。
1、Windows使用cmake-gui生成項(xiàng)目
如圖所示,在source code處選擇CMakeLists.txt文件所在的路徑,然后在binaries中選擇項(xiàng)目生成的地址,點(diǎn)擊configure,選擇已安裝的編譯器(如vs2015)。
然后點(diǎn)擊生成,在binaries目錄中就會(huì)生成Visual Studio的工程文件,打開工程文件即可開始編譯。
2、Windows使用cmake生成項(xiàng)目 首先配置好環(huán)境變量,然后打開Windows命令行工具,進(jìn)入項(xiàng)目目錄(CMakeLists.txt所在目錄),新建一個(gè)build文件夾(因?yàn)镃Make會(huì)生成許多中間文件,因此新建一個(gè)文件夾,以便需要清理時(shí)直接刪除build文件夾)。
然后執(zhí)行cmake ../
在build目錄中就會(huì)生成Visual Studio的項(xiàng)目文件,Windows上默認(rèn)生成的是Visual Studio項(xiàng)目。如果需要生成其他編譯器的Makefile,則需要使用-G指定編譯器,如下:
cmake -G “MinGW Makefiles” ../
可以使用cmake –help查看可用的編譯器名稱。
生成項(xiàng)目工程文件或Makefile后,就可以使用相應(yīng)的編譯器來(lái)編譯項(xiàng)目了。
3、Linux使用cmake生成項(xiàng)目 在Linux上使用CMake生成項(xiàng)目與上述第2小節(jié)類似。
在CMakeLists.txt所在目錄新建build目錄,在build目錄中執(zhí)行:
cmake ../
就會(huì)在build目錄中生成Makefile文件,然后可以繼續(xù)執(zhí)行make編譯項(xiàng)目。
4、CMake常用指令 cmake [] (
cmake [(-D=
cmake –build
cmake -E
cmake –find-package …
三、CMake配置文件語(yǔ)法 1、指定CMake最低版本 cmake_minimum_required (VERSION 2.6)
2、設(shè)置項(xiàng)目名稱 project (LearnCMake)
3、創(chuàng)建可執(zhí)行程序工程(exe) add_executable函數(shù)用于創(chuàng)建一個(gè)可執(zhí)行程序工程。
add_executable(
如下所示:
add_executable(FirstExecutable hello_world.cpp)
也可以添加多個(gè)源文件到工程中,如下:
add_executable(FirstExecutable main.cpp app_util.h app_util.cpp)
4、創(chuàng)建庫(kù)文件工程(a/so/lib/dll) add_library函數(shù)用于創(chuàng)建一個(gè)庫(kù)文件工程。
add_library(
如下所示:
add_library(SecondLibrary second_library.cpp)
與add_executable類似,也可以添加多個(gè)源文件。
add_library(SecondLibrary test.cpp app_util.h app_util.cpp)
默認(rèn)生成的是靜態(tài)庫(kù),也可以顯式設(shè)置庫(kù)的類型為靜態(tài)庫(kù)、動(dòng)態(tài)庫(kù)或模塊。BUILD_SHARED_LIBS也可以控制編譯生成的庫(kù)類型。
add_library(SecondLibrary SHARED second_library.cpp)
5、set設(shè)置變量 前兩節(jié)的add_library和add_executable可以添加多個(gè)源文件,但文件多了之后可能會(huì)占用很長(zhǎng)的行,因此我們可以使用set函數(shù)進(jìn)行變量賦值,然后在調(diào)用add_library和add_executable生成項(xiàng)目。
如下所示,效果與前面的示例相同。
set(AppUtilSrcs app_util.h app_util.cpp)
add_executable(FirstExecutable main.cpp ${AppUtilSrcs})
add_library(FirstLibrary test.cpp ${AppUtilSrcs})
使用set函數(shù),還可以對(duì)變量值進(jìn)行累加,如下AppUtilSrcs就代表3個(gè)文件了:
set(AppUtilSrcs app_util.h app_util.cpp)
set(AppUtilSrcs ${AppUtilSrcs} b.cpp)
除了文件名定義,set還用于變量定義
set(CMAKE_CXX_FLAGS_RELEASE “${CMAKE_CXX_FLAGS_RELEASE} /MT”)
6、代碼控制 如果一個(gè)項(xiàng)目很大,文件數(shù)以千計(jì),那么一個(gè)一個(gè)文件添加就太麻煩了,因此CMake使用aux_source_directory函數(shù)來(lái)添加目錄到工程中。
如下所示,將目錄下所有文件賦值給第一個(gè)變量,然后將這個(gè)變量加到工程中。
aux_source_directory(“./pbase/src” pbase_lib_src_files)
add_library(pbase ${pbase_lib_src_files})
除了添加文件目錄外,我們經(jīng)常還需要包含第三方庫(kù)(頭文件、庫(kù)文件)等需求,添加頭文件目錄功能如下:
include_directories函數(shù)用于添加頭文件包含目錄。
include_directories(“../thirdparty/googletest/googletest/include”)
link_directories函數(shù)用于添加需要鏈接文件的庫(kù)目錄。
link_directories(“../thirdparty/googletest/googletest/lib”)
link_libraries函數(shù)用于添加需要連接的庫(kù)文件。
link_libraries(“protobuf.so”)
鏈接目標(biāo)文件和庫(kù)文件,使用target_link_libraries函數(shù),這里的目標(biāo)文件是指通過(guò)add_executable()和add_library()指令生成的已創(chuàng)建目標(biāo)文件。
target_link_libraries(test lua mysql)
從編譯文件列表中排除文件,可以使用CMake提供的list的REMOVE_ITEM功能來(lái)實(shí)現(xiàn)。
aux_source_directory(src lua_src)
list(REMOVE_ITEM lua_src “src/lua.c” “src/luac.c”)
7、添加編譯選項(xiàng) CMake使用add_compile_options函數(shù)來(lái)添加編譯選項(xiàng),示例如下:
add_compile_options(-std=gnu99)
CMake使用add_definitions函數(shù)為源文件的編譯添加由-D定義的標(biāo)志,示例如下:
add_definitions(-O3 -g -W -Wall -Wno-deprecated -Woverloaded-virtual -Wwrite-strings -D__WUR= -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DTIXML_USE_STL)
注意,這兩個(gè)選項(xiàng)都是針對(duì)所有平臺(tái)、編譯器,因此需要謹(jǐn)慎使用,最好使用if來(lái)進(jìn)行流程處理。
8、添加其他的CMakeLists.txt 一個(gè)CMakeLists.txt里面的target如果要鏈接其他CMakeLists.txt中的target,可以使用add_subdirectory函數(shù),如下所示:
add_subdirectory(“../thirdparty/googletest/googletest/” gtest)
target_link_libraries(gtest)
9、find_package find_package為外部工程加載設(shè)置。
find_package(
QUIET選項(xiàng)將會(huì)禁掉包沒(méi)有被發(fā)現(xiàn)時(shí)的警告信息。REQUIRED選項(xiàng)表示如果沒(méi)有找到包,CMake的過(guò)程會(huì)終止,并輸出警告信息。
find_package可以根據(jù)CMake內(nèi)置的.cmake腳本查找相應(yīng)的庫(kù)模塊,調(diào)用find_package成功后,會(huì)有相應(yīng)的變量“生成”有效。
例如,調(diào)用find_package(Qt5Widgets)后,返回的變量Qt5Widgets_FOUND和Qt5Widgets_INCLUDE_DIRS就會(huì)生效。然后就可以在CMakeLists.txt中使用上述變量了。
10、條件、循環(huán)控制 If else結(jié)構(gòu)
if(condition) elseif(condition) else() endif()
foreach(loop_var arg1 arg2 ...) COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... endforeach(loop_var)
while循環(huán)
while(condition) COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... endwhile(condition)
11、Install指令 Install指令用于定義安裝規(guī)則,安裝的內(nèi)容可以包括目標(biāo)二進(jìn)制、動(dòng)態(tài)庫(kù)、靜態(tài)庫(kù)以及文件、目錄、腳本等。
參數(shù)中的TARGETS后面跟的就是我們通過(guò)ADD_EXECUTABLE或ADD_LIBRARY定義的目標(biāo)文件,可能是可執(zhí)行二進(jìn)制、動(dòng)態(tài)庫(kù)、靜態(tài)庫(kù)。
目標(biāo)類型也就相對(duì)應(yīng)的有三種,ARCHIVE特指靜態(tài)庫(kù),LIBRARY特指動(dòng)態(tài)庫(kù),RUNTIME特指可執(zhí)行目標(biāo)二進(jìn)制。
INSTALL(TARGETS targets... [[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION <dir>] [PERMISSIONS permissions...] [CONFIGURATIONS [Debug|Release|...]] [COMPONENT <component>] [OPTIONAL] ] [...])
示例如下:
INSTALL(TARGETS myrun mylib mystaticlib RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION libstatic )
上面的例子會(huì)將:
可執(zhí)行二進(jìn)制myrun安裝到${CMAKE_INSTALL_PREFIX}/bin目錄
動(dòng)態(tài)庫(kù)libmylib安裝到${CMAKE_INSTALL_PREFIX}/lib目錄
靜態(tài)庫(kù)libmystaticlib安裝到${CMAKE_INSTALL_PREFIX}/libstatic目錄
特別注意的是,您不需要關(guān)心TARGETS具體生成的路徑,只需要寫上TARGETS名稱就可以了。
我的博客即將搬運(yùn)同步至php中文網(wǎng)+社區(qū),邀請(qǐng)大家一同入駐:https://www.php.cn/link/9fe06b33ea459f011178ef3156ff09c7