CMAKE 基礎學習篇1
- 01 基礎
01 基礎
A 認識CMAKE
本篇檔案結構:
A-hello-cmake$ tree
.
├── CMakeLists.txt
├── main.cpp
- CMakeLists.txt : 包含想要執行的CMake命令
- main.cpp : 原始檔
入門概念
- CMakeLists.txt : 執行cmake命令時,會在當前資料夾下搜尋該檔案並執行其中的命令,如果不存在該檔案則cmake命令會報錯。
- 最低CMake版本要求:
cmake_minimum_required(VERSION 3.5)
- Project : 專案名稱可以方便多專案結構的變數使用
project (hello_cmake)
- 建立可執行檔案:該命令需要指明(生成的可執行檔名稱,原始檔名稱序列)
add_executable(hello_cmake main.cpp)
- 某些命令會建立一些全域性環境變數:
cmake_minimum_required(VERSION 2.6)
project (hello_cmake)
add_executable(${PROJECT_NAME} main.cpp)
該例子中project()
會建立一個變數${PROJECT_NAME}
,方便後續使用。
二進位制檔案目錄
CMAKE_BINARY_DIR : 使用者執行cmake命令生成二進位制檔案的根目錄。CMake支援兩種方式來生成二進位制檔案目錄:in-place原地生成 和 out-of-souce 隔絕原始碼的另一目錄下生成。
- in-place build :原始檔目錄下執行cmake命令,和原始檔混雜在一起
A-hello-cmake$ cmake .
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/matrim/workspace/cmake-examples/01-basic/A-hello-cmake
A-hello-cmake$ tree
.
├── CMakeCache.txt
├── CMakeFiles
│ ├── 2.8.12.2
│ │ ├── CMakeCCompiler.cmake
│ │ ├── CMakeCXXCompiler.cmake
│ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ ├── CMakeSystem.cmake
│ │ ├── CompilerIdC
│ │ │ ├── a.out
│ │ │ └── CMakeCCompilerId.c
│ │ └── CompilerIdCXX
│ │ ├── a.out
│ │ └── CMakeCXXCompilerId.cpp
│ ├── cmake.check_cache
│ ├── CMakeDirectoryInformation.cmake
│ ├── CMakeOutput.log
│ ├── CMakeTmp
│ ├── hello_cmake.dir
│ │ ├── build.make
│ │ ├── cmake_clean.cmake
│ │ ├── DependInfo.cmake
│ │ ├── depend.make
│ │ ├── flags.make
│ │ ├── link.txt
│ │ └── progress.make
│ ├── Makefile2
│ ├── Makefile.cmake
│ ├── progress.marks
│ └── TargetDirectories.txt
├── cmake_install.cmake
├── CMakeLists.txt
├── main.cpp
├── Makefile
- out-of-space build :額外建立了build目錄,
cmake ..
進行生成。
A-hello-cmake$ mkdir build
A-hello-cmake$ cd build/
matrim@freyr:~/workspace/cmake-examples/01-basic/A-hello-cmake/build$ cmake ..
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/matrim/workspace/cmake-examples/01-basic/A-hello-cmake/build
A-hello-cmake/build$ cd ..
A-hello-cmake$ tree
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ │ ├── 2.8.12.2
│ │ │ ├── CMakeCCompiler.cmake
│ │ │ ├── CMakeCXXCompiler.cmake
│ │ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ │ ├── CMakeSystem.cmake
│ │ │ ├── CompilerIdC
│ │ │ │ ├── a.out
│ │ │ │ └── CMakeCCompilerId.c
│ │ │ └── CompilerIdCXX
│ │ │ ├── a.out
│ │ │ └── CMakeCXXCompilerId.cpp
│ │ ├── cmake.check_cache
│ │ ├── CMakeDirectoryInformation.cmake
│ │ ├── CMakeOutput.log
│ │ ├── CMakeTmp
│ │ ├── hello_cmake.dir
│ │ │ ├── build.make
│ │ │ ├── cmake_clean.cmake
│ │ │ ├── DependInfo.cmake
│ │ │ ├── depend.make
│ │ │ ├── flags.make
│ │ │ ├── link.txt
│ │ │ └── progress.make
│ │ ├── Makefile2
│ │ ├── Makefile.cmake
│ │ ├── progress.marks
│ │ └── TargetDirectories.txt
│ ├── cmake_install.cmake
│ └── Makefile
├── CMakeLists.txt
├── main.cpp
構建可執行檔案
其實CMake在build目錄下生成了Makefile檔案,在該目錄下執行make真正構建專案的可執行檔案。
$ mkdir build
$ cd build
$ cmake ..
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /workspace/cmake-examples/01-basic/hello_cmake/build
$ make
Scanning dependencies of target hello_cmake
[100%] Building CXX object CMakeFiles/hello_cmake.dir/hello_cmake.cpp.o
Linking CXX executable hello_cmake
[100%] Built target hello_cmake
$ ./hello_cmake
Hello CMake!
B 標頭檔案 hello-headers
本篇檔案結構:
B-hello-headers$ tree
.
├── CMakeLists.txt
├── include
│ └── Hello.h
└── src
├── Hello.cpp
└── main.cpp
目錄相關的路徑變數
CMake聲明瞭一些變數:
Variable | Info |
---|---|
CMAKE_SOURCE_DIR | The root source directory (環境變數) |
CMAKE_CURRENT_SOURCE_DIR | The current source directory if using sub-projects and directories. 當前CMakeList.txt所在目錄 |
CMAKE_BINARY_DIR | The root binary / build directory. This is the directory where you ran the cmake command. (環境變數) |
CMAKE_CURRENT_BINARY_DIR | The build directory you are currently in. 當前編譯target所在目錄 |
PROJECT_BINARY_DIR | The build directory for the current project. 執行cmake命令的目錄 |
PROJECT_SOURCE_DIR | The source directory of the current cmake project. 工程根目錄 |
https://www.jianshu.com/p/9d246e4071d4
建立變數例子
- 建立一個原始檔變數:
# Create a sources variable with a link to all cpp files to compile
set(SOURCES
src/Hello.cpp
src/main.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
- GLOB命令查詢匹配檔案:
file(GLOB SOURCES "src/*.cpp")
現代CMake不提倡這種方式來關聯原始檔名稱,而是建議通過一些類似add_XXX的方法來宣告變數。
標頭檔案路徑設定
target_include_directories(target
PRIVATE
${PROJECT_SOURCE_DIR}/include
)
- 該方法相當於在編譯時使用
-I
指定了標頭檔案搜尋路徑。 - PRIVATE識別符號的意義建議檢視CMake官方文件瞭解。
使用Verbose
之前的例子中,使用make命令,只會簡單列印build了哪些檔案。
使用make VERBOSE=1
可以把一些make的細緻過程打印出來:
$ make clean
$ make VERBOSE=1
/usr/bin/cmake -H/home/matrim/workspace/cmake-examples/01-basic/hello_headers -B/home/matrim/workspace/cmake-examples/01-basic/hello_headers/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build/CMakeFiles /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory `/home/matrim/workspace/cmake-examples/01-basic/hello_headers/build'
make -f CMakeFiles/hello_headers.dir/build.make CMakeFiles/hello_headers.dir/depend
make[2]: Entering directory `/home/matrim/workspace/cmake-examples/01-basic/hello_headers/build'
cd /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/matrim/workspace/cmake-examples/01-basic/hello_headers /home/matrim/workspace/cmake-examples/01-basic/hello_headers /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build/CMakeFiles/hello_headers.dir/DependInfo.cmake --color=
make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/01-basic/hello_headers/build'
make -f CMakeFiles/hello_headers.dir/build.make CMakeFiles/hello_headers.dir/build
make[2]: Entering directory `/home/matrim/workspace/cmake-examples/01-basic/hello_headers/build'
/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build/CMakeFiles 1
[ 50%] Building CXX object CMakeFiles/hello_headers.dir/src/Hello.cpp.o
/usr/bin/c++ -I/home/matrim/workspace/cmake-examples/01-basic/hello_headers/include -o CMakeFiles/hello_headers.dir/src/Hello.cpp.o -c /home/matrim/workspace/cmake-examples/01-basic/hello_headers/src/Hello.cpp
/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build/CMakeFiles 2
[100%] Building CXX object CMakeFiles/hello_headers.dir/src/main.cpp.o
/usr/bin/c++ -I/home/matrim/workspace/cmake-examples/01-basic/hello_headers/include -o CMakeFiles/hello_headers.dir/src/main.cpp.o -c /home/matrim/workspace/cmake-examples/01-basic/hello_headers/src/main.cpp
Linking CXX executable hello_headers
/usr/bin/cmake -E cmake_link_script CMakeFiles/hello_headers.dir/link.txt --verbose=1
/usr/bin/c++ CMakeFiles/hello_headers.dir/src/Hello.cpp.o CMakeFiles/hello_headers.dir/src/main.cpp.o -o hello_headers -rdynamic
make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/01-basic/hello_headers/build'
/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build/CMakeFiles 1 2
[100%] Built target hello_headers
make[1]: Leaving directory `/home/matrim/workspace/cmake-examples/01-basic/hello_headers/build'
/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/01-basic/hello_headers/build/CMakeFiles 0
C 連結靜態庫 static-library
本篇學習建立並連結一個靜態庫的簡單例子。
該篇檔案結構:
$ tree
.
├── CMakeLists.txt
├── include
│ └── static
│ └── Hello.h
└── src
├── Hello.cpp
└── main.cpp
建立靜態庫
add_library(hello_library STATIC
src/Hello.cpp
)
該方法將會使用Hello.cpp建立一個靜態庫libhello_library.a
靜態庫的標頭檔案關聯
target_include_directories(hello_library
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
在以下兩種情況時,編譯命令會包含該指定的標頭檔案路徑:
- 編譯庫
- 任意連結該靜態庫的可執行檔案
範圍限定宣告符號:
- PRIVATE:該目錄新增到target的include directories
- INTERFACE:該目錄新增到連結該庫的target的include directories
- PUBLIC:同時包含上述兩種。
上述方式的標頭檔案路徑新增相當於:
#include "static/Hello.h"
連結靜態庫
add_executable(hello_binary
src/main.cpp
)
target_link_libraries( hello_binary
PRIVATE
hello_library
)
類似於:
/usr/bin/c++ CMakeFiles/hello_binary.dir/src/main.cpp.o -o hello_binary -rdynamic libhello_library.a
D 連結動態庫 shared-library
建立動態庫
add_library(hello_library SHARED
src/Hello.cpp
)
為庫新增別名
add_library(hello::library ALIAS hello_library)
通過為庫新增別名,後續連結相關庫時可以直接使用其別名。
連結共享庫
add_executable(hello_binary
src/main.cpp
)
target_link_libraries(hello_binary
PRIVATE
hello::library
)
上述cmake命令等價於:
/usr/bin/c++ CMakeFiles/hello_binary.dir/src/main.cpp.o -o hello_binary -rdynamic libhello_library.so -Wl,-rpath,/home/matrim/workspace/cmake-examples/01-basic/D-shared-library/build
E 安裝 installing
本篇展示如何生成make install
命令,幫助將專案的一些標頭檔案和可執行檔案安裝到系統上。
本篇檔案結構:
$ tree
.
├── cmake-examples.conf
├── CMakeLists.txt
├── include
│ └── installing
│ └── Hello.h
├── README.adoc
└── src
├── Hello.cpp
└── main.cpp
- cmake-examples.conf : 示例配置檔案
安裝Installing
CMake提供make install
在目標系統上安裝可執行檔案、庫等,這些安裝位置通過變數CMAKE_INSTALL_PREFIX
來控制。通過cmake命令可以修改該變數:
cmake .. -DCMAKE_INSTALL_PREFIX=/install/location
通過install方法,決定哪些檔案會被安裝:
install (TARGETS cmake_examples_inst_bin
DESTINATION bin)
該例子為安裝該可執行檔案到destionation ${CMAKE_INSTALL_PREFIX}/bin
# 安裝庫
install (TARGETS cmake_examples_inst
LIBRARY DESTINATION lib)
# 安裝標頭檔案目錄
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/
DESTINATION include)
# 安裝配置檔案
install (FILES cmake-examples.conf
DESTINATION etc)
- 當執行
make install
後,CMake會生成install_manifest.txt
檔案,記錄所有被安裝的檔名稱。 - 如果在root下執行
make install
命令後,上述這個記錄檔案則會被root持有。
過載預設安裝位置
上述的例子中的預設安裝位置來源環境變數CMAKE_INSTALL_PREFIX,即/usr/local/
if( CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT )
message(STATUS "Setting default CMAKE_INSTALL_PREFIX path to ${CMAKE_BINARY_DIR}/install")
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE STRING "The path to use for make install" FORCE)
endif()
這個cmake命令例子會把預設安裝位置改為build目錄下。
臨時暫存安裝目錄
如果需要暫存安裝,來確認所有必要檔案已經被安裝。可以使用make install
命令並帶上 DESTDIR 路徑引數。
make install DESTDIR=/tmp/stage
該命令會建立一個安裝路徑:${DESTDIR}/${CMAKE_INSTALL_PREFIX}
,上述例子安裝後路徑如下:
$ tree /tmp/stage
/tmp/stage
└── usr
└── local
├── bin
│ └── cmake_examples_inst_bin
├── etc
│ └── cmake-examples.conf
└── lib
└── libcmake_examples_inst.so
解除安裝
CMake並沒有新增make uninstall
來支援解除安裝功能,有需要可以具體再查閱資料,一個例子如下:
sudo xargs rm < install_manifest.txt
F 構建型別 build-type
CMake有許多內建的構建編譯選項,不同級別的編譯選擇可以指定不同的優化等級和除錯資訊與否。預設一般有以下幾種編譯型別(及其預設優化級別)
- Release - Adds the
-O3 -DNDEBUG
flags to the compiler - Debug - Adds the
-g
flag - MinSizeRel - Adds
-Os -DNDEBUG
- RelWithDebInfo - Adds
-O2 -g -DNDEBUG
flags
設定編譯型別
cmake .. -DCMAKE_BUILD_TYPE=Release
修改預設編譯型別和自定義細節
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_C_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -rdynamic -g -ggdb")
set(CMAKE_C_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE Release)
endif()
G 編譯引數 compile-flags
CMake主要提供了兩種方式來設定編譯引數選項:
- 方法
target_compile_definitions()
- 變數
CMAKE_C_FLAGS
和CMAKE_CXX_FLAGS
為每一個target都設定編譯引數
target_compile_definitions(cmake_examples_compile_flags
PRIVATE EX3
)
該命令會使得編譯target時,帶上編譯選項-DEX3
。並且此處如果使用PUBLIC限定,使得連結了該target的編譯也會帶上該編譯引數。
設定預設C++編譯引數
一般預設情況下,編譯引數變數CMAKE_CXX_FLAGS
和CMAKE_C_FLAGS
為空或者為適當選項。
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE) # 設定CPP編譯選項
同理可得:
CMAKE_C_FLAGS
用於設定C編譯引數CMAKE_LINKER_FLAGS
用於設定連結引數
還有一種傳遞引數的方式
cmake .. -DCMAKE_CXX_FLAGS="-DEX3"
H 三方庫使用 third-party-library
幾乎所有比較重要的專案都需要諸如第三方庫、外部標頭檔案等依賴項。CMake支援使用find_package()
函式來支援查詢這些依賴。FindXXX.cake
檔案中的依賴會去CMAKE_MODULE_PATH
路徑中查詢。Linux上預設搜尋依賴路徑為/usr/share/cmake/Modules
本篇示例將需要boost庫安裝到本機中。
查詢包 Finding a Package
https://zhuanlan.zhihu.com/p/97369704?utm_source=wechat_session
find_package()
函式將從CMAKE_MODULE_PATH
中的資料夾列表中搜索“FindXXX.cmake”中的 CMake 模組。
find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)