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