1. 程式人生 > 其它 >CMAKE 基礎學習篇1

CMAKE 基礎學習篇1

目錄

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_FLAGSCMAKE_CXX_FLAGS

為每一個target都設定編譯引數

target_compile_definitions(cmake_examples_compile_flags
    PRIVATE EX3
)

該命令會使得編譯target時,帶上編譯選項-DEX3。並且此處如果使用PUBLIC限定,使得連結了該target的編譯也會帶上該編譯引數。

設定預設C++編譯引數

一般預設情況下,編譯引數變數CMAKE_CXX_FLAGSCMAKE_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)