CMake簡介
原文:http://blog.gclxry.com/use-cmake-on-windows/windows
你或許聽過好幾種 Make 工具,例如 GNU Make ,QT 的 qmake ,微軟的 MS nmake,BSD Make(pmake),Makepp,等等。這些 Make 工具遵循着不一樣的規範和標準,所執行的 Makefile 格式也千差萬別。這樣就帶來了一個嚴峻的問題:若是軟件想跨平臺,必需要保證可以在不一樣平臺編譯。而若是使用上面的 Make 工具,就得爲每一種標準寫一次 Makefile ,這將是一件讓人抓狂的工做。app
CMake就是針對上面問題所設計的工具:它首先容許開發者編寫一種平臺無關的 CMakeList.txt 文件來定製整個編譯流程,而後再根據目標用戶的平臺進一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。從而作到「Write once, run everywhere」。函數
Windows上使用CMake
Windows上使用CMake也很方便,除了傳統的命令行方式使用CMake,還有一個簡單的GUI程序cmake-gui.exe來使用CMake。工具
安裝CMake
Windows上安裝CMake很簡單,去https://cmake.org/ 上面下載最新的CMake安裝包就能夠了。安裝的時候還能夠選擇是否把CMake加到系統的PATH中,以下圖所示:post
爲了方便起見,能夠把CMake加到系統PATH中。visual-studio
CMake的GUI用法
安裝好CMake,會建立一個快捷方式,點擊運行就會運行CMake-gui.exe,這個是CMake的GUI程序,如下圖所示:ui
source code編輯框就是輸入代碼的所在的路徑,這個路徑可以找到一個CMakeLists.txt文件。google
build the binaries編輯框就是編譯輸出的中間文件和最終的二進制文件的目錄。url
由於CMake最終經過CMakeLists.txt文件生成Windows上對應的vs工程文件,不一樣的vs版本也會影響到最終生成vs工程文件,因此configure對話框就是選擇代碼編譯工具的,如圖所示:spa
下面以google test工程的代碼爲例來使用CMake-gui,輸入google test對應的路徑,點擊Generate按鈕就會在E:/googletest/googletest/build目錄生成vs編譯工程文件:
用vs打開gtest.sln文件,就能夠編譯google test代碼了。
CMake的命令行用法
CMake的命令行用法比GUI的用法複雜,可是功能更增強大,值得一學。如下是CMake命令行調用的方法:
1
2
3
4
5
|
cmake [<options>] (<path-to-source> | <path-to-existing-build>)
cmake [(-D<var>=<value>)...] -P <cmake-script-file>
cmake --build <dir> [<options>] [-- <build-tool-options>...]
cmake -E <command> [<options>...]
cmake --find-package <options>...
|
生成編譯工程文件
cmake <CMakeLists.txt_Path>就是生成能夠編譯工程文件。當時運行的目錄在哪裏,生成的可編譯工程文件就在哪一個目錄。好比CMakeLists.txt文件在f:cmake目錄,而當時在f:cmakebuild目錄運行cmake ..,則生成的編譯工程文件在f:cmakebuild目錄。
也能夠再生成編譯工程文件的時候經過-D來添加變量值,好比CMakeLists.txt內容以下:
1
2
3
4
|
cmake_minimum_required (VERSION 2.6)
project (a)
message(${EXECUTABLE_OUTPUT_PATH})
add_executable(b b.cpp)
|
咱們能夠經過-D選擇來設置EXECUTABLE_OUTPUT_PATH的值,也是編譯的文件的輸出目錄:
1
|
cmake -D EXECUTABLE_OUTPUT_PATH="another_output" ..
|
這樣,咱們就給CMakeLists.txt編譯腳本傳遞了新的EXECUTABLE_OUTPUT_PATH值。
編譯工程
CMake除了生成編譯工程文件,它也能夠調用系統的編譯工程來編譯工程,如:
1
|
cmake --build .
|
默認是編譯debug模式,也能夠傳遞在–後面傳遞MSBUILD參數來控制:
1
|
cmake --build . -- /p:Configuration=Release
|
命令行工具模式
CMake有一個-E的命令行工具模式,提供了一些經常使用的功能,比複製文件、建立目錄、讀寫註冊表、讀寫環境變量、計算md5值等等。腳本能夠調用這些功能。
編寫CMakeLists.txt
建立能夠執行程序工程
首先從建立一個最簡單的可執行程序開始,CMakeLists.txt內容以下:
1
2
3
4
5
|
cmake_minimum_required (VERSION 2.6)
project (LearnCMake)
message(${LearnCMake_SOURCE_DIR})
message(${LearnCMake_BINARY_DIR})
add_executable(FirstExecutable hello_world.cpp)
|
第1行是cmake須要的最低版本,目前這個是VERSION 2.6,通常不用修改。
第2~4行表示咱們建立了一個名爲LearnCMake工程,對應生成一個LearnCMake.sln。project函數表示建立一個工程。同時,也生成了4個變量:
- PROJECT_SOURCE_DIR, <PROJECT-NAME>_SOURCE_DIR。工程的源代碼目錄。
- PROJECT_BINARY_DIR, <PROJECT-NAME>_BINARY_DIR。工程的二進制文件目錄。
第5行表示添加一個名爲FirstExecutable的可執行程序項目,它的源代碼爲hello_world.cpp。下面是add_executable的完整用法:
1
2
3
|
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
source1 [source2 ...])
|
默認的是建立控制檯工程,加上WIN32表示建立的是win32工程,以下:
1
|
add_executable(FirstExecutable WIN32 hello_world.cpp)
|
後面是項目的代碼,能夠添加多個代碼文件,用空格分開。
建立庫工程
建立庫工程跟建立可執行程序工程相似,建立庫工程使用add_library函數,以下例子:
1
2
3
4
5
6
|
cmake_minimum_required (VERSION 3.0)
project (LearnCMake)
add_library(FirstLibrary first_library.cpp)
add_library(SecondLibrary second_library.cpp)
add_executable(FirstExecutable hello_world.cpp)
target_link_libraries(FirstExecutable FirstLibrary)
|
add_library的用法以下:
1
2
3
|
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
source1 [source2 ...])
|
默認的是靜態庫,也能夠顯式的設置庫是否爲靜態庫、動態庫或者是模塊。另外BUILD_SHARED_LIBS也可控制編譯成哪一種庫。
target_link_libraries用來連接庫,用法以下:
1
2
|
target_link_libraries(<target> [item1 [item2 [...]]]
[[debug|optimized|general] <item>] ...)
|
set設置變量
add_library、add_executable均可以添加多個源文件,以下:
1
2
3
4
|
cmake_minimum_required (VERSION 3.0)
project (LearnCMake)
add_executable(FirstExecutable main.cpp app_util.h app_util.cpp)
add_library(FirstLibrary app_util.h app_util.cpp)
|
咱們能夠經過定義一個AppUtilSrc變量來代替app_util.h app_util.cpp,以下:
1
2
3
4
5
|
cmake_minimum_required (VERSION 3.0)
project (LearnCMake)
set(AppUtilSrcs app_util.h app_util.cpp)
add_executable(FirstExecutable main.cpp ${AppUtilSrcs})
add_library(FirstLibrary ${AppUtilSrcs})
|
效果是跟上面等價的。還能夠累積值:
1
2
|
set(AppUtilSrcs app_util.h app_util.cpp)
set(AppUtilSrcs ${AppUtilSrcs} b.cpp)
|
這樣AppUtilSrcs就表明着3個文件了。
設置編譯模式
設置mt編譯模式:
1
2
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
|
設置md編譯模式:
1
2
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
|
默認是多字節模式,設置成unicode模式:
1
|
add_definitions(-D_UNICODE)
|
另外add_definitions還能夠設置其餘的選項。
添加其餘CMakeLists.txt
一個CMakeLists.txt裏面的target若是要連接其餘CMakeLists.txt中的target,能夠使用add_subdirectory,咱們以使用googletest庫爲例:
1
2
3
4
5
6
7
8
|
add_subdirectory("../thirdparty/googletest/googletest/" gtest)
file(GLOB_RECURSE gtest_lib_head_files "../thirdparty/googletest/googletest/*.h")
source_group("gtest" FILES ${gtest_lib_head_files})
include_directories("../thirdparty/googletest/googletest/include")
aux_source_directory("./pbase_unittest/src" pbase_unittest_src_files)
file(GLOB_RECURSE pbase_unittest_include_files "./pbase_unittest/include/*.h")
add_executable(pbase_unittest ${pbase_unittest_src_files} ${pbase_unittest_include_files} ${gtest_lib_head_files})
target_link_libraries(pbase_unittest gtest)
|
代碼控制
若是想把./pbase/src目錄下的全部源文件加入到工程,能夠用aux_source_directory把某個目錄下的源文件加入到某個變量中,稍後就能夠使用這個變量表明的代碼了,如:
1
2
|
aux_source_directory("./pbase/src" pbase_lib_src_files)
add_library(pbase ${pbase_lib_src_files})
|
添加頭文件包含目錄是:
1
|
include_directories("../thirdparty/googletest/googletest/include")
|
可是include_directories中的文件不會體現先visual studio工程中,而aux_source_directory只會添加源文件,會忽略頭文件,若是想生存的visual studio工程裏面也包含頭文件,能夠這樣:
1
2
3
|
# add head files
file(GLOB_RECURSE pbase_lib_head_files "./pbase/include/*.h")
add_library(pbase ${pbase_lib_head_files})
|
若是想生存visual studio中的filter,能夠使用source_group:
1
2
|
file(GLOB_RECURSE gtest_lib_head_files "../thirdparty/googletest/googletest/*.h")
source_group("gtest" FILES ${gtest_lib_head_files})
|
最終添加頭文件到工程裏標準模板是:
1
2
3
4
|
file(GLOB_RECURSE gtest_lib_head_files "../thirdparty/googletest/googletest/*.h")
source_group("gtest" FILES ${gtest_lib_head_files})
include_directories("../thirdparty/googletest/googletest/include")
add_executable(pbase_unittest ${gtest_lib_head_files})
|