使用Cmake生成跨平臺項目編譯解決方案

項目最近需要在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生成項目

使用Cmake生成跨平臺項目編譯解決方案

如圖所示,在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=)…] -P

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( [WIN32] [MAcosX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 …])

如下所示:

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( [Static | SHAred | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 …])

如下所示:

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( [version] [EXACT] [QUIET] [[REQUIRED|COMPONENTS] [components…]] [NO_POLICY_SCOPE])

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()

for循環

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

? 版權聲明
THE END
喜歡就支持一下吧
點贊14 分享