参考
配置
cmake是跨平台C/C++构建文件生成工具, 通过CMakeList.txt生成项目构建文件
安装
命令行
sudo apt install -y cmake
卸载
sudo apt --purge remove cmake
源码
- 示例, 安装amd64架构cmake-3.31
sudo apt install libssl-dev
wget https://cmake.org/files/v3.31/cmake-3.31.4.tar.gz
tar -xvzf cmake-3.31.4.tar.gz
cd cmake-3.31.4 && mkdir build && cd build
../bootstrap
# 编译安装
make -j
sudo make install
# 拷贝可执行文件
sudo cp bin/* /usr/bin/
命令
版本
cmake --version
流程
graph LR;
A1[/CMakeLists.txt/]--> B[cmake]-->B1[/构建文件/]-->C[构建工具]--> C1[/可执行程序/库/]
CMakeLists.txt
是cmake配置文件, 用于定义项目构建过程
cmake根据其中指令生成构建系统(如 Makefile 等), 随后通过构建工具进行编译和链接, 生成可执行文件或库
编写
- 示例, 通过cmake编译main.c
// main.c
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
# CMakeLists.txt
# 设置CMake最低版本要求
cmake_minimum_required(VERSION 3.10)
# 设置项目名称
project(main)
# 设置C++标准(C++11)
set(CMAKE_CXX_STANDARD 11)
# 定义可执行文件
add_executable(main main.c)
# 可执行文件安装位置为根目录bin/
install(TARGETS main RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/bin)
graph LR;
X(语法)
X-->A(cmake_minimum_required)-->A1(cmake版本)
X-->B(project)-->B1(项目名)
X-->C(set)-->C1(设置参数)
X-->D(add_executable/add_library)-->D1(定义生成文件)
X-->E(install)-->E1(安装)
生成
cmake读取解析CMakeLists.txt, 检查系统环境、依赖库、编译器等设置, 生成对应平台构建文件, 例如在Unix系统上会生成Makefile
graph LR;
A[/CMakeLists.txt/]-->B[cmake]-->C[/构建文件/]
命令
当前目录生成(不推荐)
.
├── CMakeLists.txt
└── main.c
会产生大量中间文件, 影响整洁
cmake .
在build目录下生成(推荐)
.
├── CMakeLists.txt
├── build
└── main.c
cmake -B build
或者
mkdir build && cd build
cmake ../
使用其他目录CMakeLists.txt
使用source/CMakeList.txt, 在build/下生成构建文件
.
├── source
│ ├── main.c
│ └── CMakeLists.txt
└── build
cmake -S source -B build
构建
构建工具(如make)调用构建文件进行实际编译和链接
graph LR;
A[/构建文件/]-->B[构建工具]-->C[/可执行文件/库/]
命令
cmake调用
- 使用当前目录下构建文件
cmake --build .
- 使用build目录下构建文件
cmake --build build
make调用
进入构建文件目录
make
安装
将构建产物按CMakeLists.txt中设置安装到指定位置
命令
cmake调用
cmake --install 构建目录 (--prefix 安装根路径, 仅在CMakeLists.txt中未指定安装根路径时)
- 示例, 构建目录为build/, 安装构建产物
cmake --install build
- 示例, CMakeLists.txt中未指定根路径情况
若CMakeLists.txt中未指定安装根路径, 可通过–prefix手动指定
install(TARGETS main RUNTIME DESTINATION bin)
设构建目录为build, 安装时使用test/作为根路径
cmake --install build --prefix test
可执行文件会安装到test/bin路径下
make调用
进入构建目录
make install
语法
设置
cmake最低版本
cmake_minimum_required(VERSION major.minor)
graph LR
P(参数)
P-->A(VERSION)-->A1(关键字, 表示所需CMake 版本号)
P-->B(major)-->B1(主版本号)
P-->C(minor)-->C1(次版本号)
- 示例, 设置工程最低cmake版本为3.10
cmake_minimum_required(VERSION 3.10)
...
项目名
project(项目名 (VERSION 版本信息, 可选))
- 示例, 设置项目名为main
cmake_minimum_required(VERSION 3.10)
project(main)
...
变量
set(variable value [PARENT_SCOPE])
graph LR
P(参数)
P-->A(variable)-->A1(变量名, 通常由大写字母组成, 以区别于cmake内置变量和函数)
P-->B(value)-->B1(变量值, 可以是字符串、列表或其他cmake变量)
P-->C(PARENT_SCOPE, 可选)-->C1(如果指定, 变量将在父作用域中设置)
一般变量
- 示例, 设置变量MY_VAR
set(MY_VAR "Hello, World!")
列表变量
- 示例, 设置SRC_LIST存储源文件名
set(SRC_LIST main.cpp test.cpp)
set(MY_LIST "item1" "item2" "item3")
foreach(item IN LISTS MY_LIST)
message(STATUS "List item: ${item}")
endforeach()
父作用域中设置
- 示例, 父作用域设置变量
function(my_func)
set(MY_VAR_INSIDE "inside function" PARENT_SCOPE)
endfunction()
my_func()
message(STATUS "The value of MY_VAR_INSIDE after function call is: ${MY_VAR_INSIDE}")
my_func 函数内指定 PARENT_SCOPE 选项在父作用域中设置 MY_VAR_INSIDE 变量
即使在函数调用之后, MY_VAR_INSIDE 也可以在外部作用域中访问
使用
cmake中通过${变量名}
获取变量值
- 示例, 使用变量ANOTHER_VAR
set(ANOTHER_VAR "Another Value")
set(MY_VAR2 ${ANOTHER_VAR})
常量
运行环境
项目名
对应指令project所声明项目名称
PROJECT_NAME
- 示例, 指定项目名称为main
project(main)
message(${PROJECT_NAME})
目标平台
CMake编译生成目标文件所运行操作系统名称, 交叉编译时可用来指定目标平台类型
CMAKE_SYSTEM_NAME
- 示例, 指定目标平台为linux
cmake -DCMAKE_SYSTEM_NAME=Linux ..
或者
# CMakeLists.txt
...
set(CMAKE_SYSTEM_NAME "Linux")
...
编译器
C/C++编译器
CMAKE_C_COMPILER
CMAKE_CXX_COMPILER
- 示例, 指定编译器为aarch64-linux-gnu-gcc
cmake -DCMAKE_C_COMPILER=/usr/bin/aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=/usr/bin/aarch64-linux-gnu-g++
或者
# CMakeLists.txt
...
set(CMAKE_C_COMPILER "/usr/bin/aarch64-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "/usr/bin/aarch64-linux-gnu-g++")
...
路径
编译目录
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
如果是在源代码目录中编译, 指工程顶层目录
如果是在源代码目录之外的目录中编译, 指工程编译发生的目录
工程根目录
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
当前路径
CMakeLists.txt文件所在完整路径
CMAKE_CURRENT_SOURCE_DIR
创建
可执行文件
将一组源文件编译成可执行文件
add_executable(target item1 ...)
graph LR
P(参数)
P-->A(target)-->A1(目标名称)
P-->D(item1)-->D1(包含源文件)
- 示例, 生成可执行文件main
// main.c
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
add_executable(main main.c)
库文件
创建库文件
库名对应于逻辑目标名称, 在工程全局域内必须唯一
add_library(target [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ...)
graph LR
X(参数)
X-->A(STATIC)-->A1(目标文件归档文件, 在链接其它目标时使用)
X-->B(SHARED)-->B1(会被动态链接, 在运行时被加载)
X-->C(MODULE)-->C1(不会被链接到其它目标中插件, 但可能会在运行时使用dlopen-系列函数动态链)
- 示例, 生成libmain.so文件
// main.h
#include <stdio.h>
void Hello();
// main.c
#include "main.h"
void Hello() {
printf("Hello\n");
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
add_library(main SHARED main.c)
添加
头文件路径
指定编译目标(可执行文件或库)应包含头文件目录
target_include_directories(
target
[SYSTEM]
[AFTER|BEFORE]
<PRIVATE|PUBLIC|INTERFACE>
<directories>
)
graph LR
P(参数)
P-->A(target)-->A1(目标名称, 表示可执行文件或库)
P-->B(SYSTEM, 可选)-->B1(告诉编译器包含目录是系统级目录)
P-->C(AFTER、BEFORE, 可选)-->C1(指定包含目录插入位置)
C1-->C11(AFTER 表示在现有目录之后)
C1-->C12(BEFORE 表示在现有目录之前)
P-->D(INTERFACE PRIVATE PUBLIC)-->D1(指定包含目录作用域)
D1-->D11(PUBLIC)-->D111(对编译目标本身及其依赖均可见)
D1-->D12(PRIVATE)-->D121(仅对编译目标本身可见)
D1-->D13(INTERFACE)-->D131(仅对依赖于该目标其他目标使用)
P-->E(directories)-->E1(要包含头文件目录列表)
- 示例, 为目标文件添加依赖头文件目录
.
├── CMakeLists.txt
├── src
│ ├── main.c
│ └── module
│ └── test_api
│ └── test_api.h
└── third_party
└── hello_lib
└── hello.h
// third_party/hello_lib/hello.h
#include <stdio.h>
extern int libVersion;
int libVersion = 0xFFF;
// src/module/test_api/test_api.h
#include <stdio.h>
extern int apiVersion;
int apiVersion = 0x111;
cmake里会添加依赖头文件目录, 可省略头文件所在目录路径
// src/main.c
#include "hello.h"
#include "test_api.h"
int main() {
printf("%d\n%d\n", libVersion, apiVersion);
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
add_executable(${PROJECT_NAME} src/main.c)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/third_party/hello_lib
${CMAKE_SOURCE_DIR}/src/module/test_api
)
源文件
给目标文件添加依赖源文件路径
target_sources(<target> <INTERFACE|PUBLIC|PRIVATE> [items1...])
graph LR
P(参数)
P-->A(target)-->A1(目标名称)
P-->D(INTERFACE PRIVATE PUBLIC)-->D1(指定包含源文件可见性)
D1-->D11(PUBLIC)-->D111(对编译目标及目标依赖均可见)
D1-->D12(PRIVATE)-->D121(仅对编译目标本身可见)
D1-->D13(INTERFACE)-->D131(仅对依赖于该目标其他目标使用)
P-->E(items1)-->E1(源文件路径)
- 示例, 添加源文件hello.c、main.c编译
.
├── CMakeLists.txt
└── src
├── hello
│ ├── hello.c
│ └── hello.h
└── main.c
// src/hello/hello.h
#include <stdio.h>
void Hello();
// src/hello/hello.c
#include "hello.h"
void Hello() {
printf("Hello\n");
}
// src/main.c
#include "hello/hello.h"
int main() {
Hello();
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
add_executable(${PROJECT_NAME} "")
target_sources(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/src/main.c
${CMAKE_SOURCE_DIR}/src/hello/hello.c
)
链接依赖
指定链接给定目标和/或其依赖项
target_link_libraries(<target> <INTERFACE|PUBLIC|PRIVATE> items...)
graph LR
P(参数)
P-->A(target)-->A1(目标名称, 可以是可执行文件、共享库或静态库)
P-->D1(可见性)
D1-->D11(PUBLIC)-->D111(指定库用于链接目标本身, 并传播给目标依赖项)
D1-->D12(PRIVATE)-->D121(指定库仅用于链接目标本身, 不传播给目标依赖项)
D1-->D13(INTERFACE)-->D131(指定库不会用于链接目标本身, 但会传播给目标依赖项)
P-->E(items)-->E1(库文件)
如果项目需要链接系统库, 可以直接使用库名称(如 pthread、dl、m 等)或系统库变量(如 ${CMAKE_THREAD_LIBS_INIT})
- 示例, 链接系统POSIX线程库
// main.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* ThreadFunction(void* arg) {
int threadNum = *(int*)arg;
printf("Hello from thread %d!\n", threadNum);
sleep(1);
return NULL;
}
int main() {
const int numThreads = 5;
pthread_t threads[numThreads];
int threadArgs[numThreads];
for (int i = 0; i < numThreads; ++i) {
threadArgs[i] = i;
pthread_create(&threads[i], NULL, ThreadFunction, &threadArgs[i]);
}
for (int i = 0; i < numThreads; ++i) {
pthread_join(threads[i], NULL);
}
std::cout << "all threads completed\n";
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
add_executable(${PROJECT_NAME} main.c)
# 链接 POSIX 线程库
target_link_libraries(${PROJECT_NAME} pthread)
生成
子路径生成
为构建添加一个子路径
add_subdirectory([source_dir] [binary_dir] [EXCLUDE_FROM_ALL])
graph LR
P(参数)
P-->A(source_dir)-->A1(指定源 CMakeLists.txt 和代码文件路径)
P-->B(binary_dir)-->B1(指定放置输出文路径)
P-->C(EXCLUDE_FROM_ALL, 可选)-->C1(子目录中目标将不包括在父目录ALL目标中, 用户必须在子目录中显式构建目标)
- 示例, 子路径调用
.
├── CMakeLists.txt
└── src
├── hello
│ ├── CMakeLists.txt
│ └── hello.c
└── main.c
// src/hello/hello.c
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
# src/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(hello)
add_executable(${PROJECT_NAME} hello.c)
// src/main.c
#include "hello/hello.h"
int main() {
Hello();
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
# 执行子目录cmake
add_subdirectory(src/hello)
add_executable(${PROJECT_NAME} src/main.c)
安装
目标文件
可执行文件
install(TARGETS 文件名 RUNTIME DESTINATION 安装路径)
- 示例, 安装可执行文件到根目录bin目录下
// main.c
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(main)
add_executable(${PROJECT_NAME} main.c)
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/bin)
动态库
install(TARGETS LIBRARY DESTINATION )
静态库
install(TARGETS ARCHIVE DESTINATION )
目录
install(DIRECTORY DESTINATION )
文件匹配过滤
install(DIRECTORY DESTINATION FILES_MATCHING PATTERN )
- 示例, 仅安装.h和.hpp
install(DIRECTORY ${CMAKE_SOURCE_DIR}/src DESTINATION shared FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp")
示例
单文件
将单个源文件生成可执行文件
graph LR
X(语法)
X-->A("${PROJECT_NAME}")-->A1(项目名称)
X-->B(add_executable)-->B1(生成可执行文件)
- 示例, 编译main.cpp
// main.cpp
#include <iostream>
int main() {
std::cout << "Hello World" << std::endl;
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(main)
add_executable(${PROJECT_NAME} main.cpp)
多目录
将多个目录下源文件生成可执行文件
graph LR
X(语法)
X-->A(target_include_directories)-->A1(添加头文件目录)
X-->B(target_sources)-->B1(添加源文件)
X-->C("${CMAKE_SOURCE_DIR}")-->C1(表示项目根目录)
- 示例, 将hello_1、hello_2目录下源文件生成可执行文件
.
├── CMakeLists.txt
└── src
├── hello_1
│ ├── hello_1.cpp
│ └── hello_1.hpp
├── hello_2
│ ├── hello_2.cpp
│ └── hello_2.hpp
└── main.cpp
// src/hello_1/hello_1.hpp
#include <iostream>
void Hello_1();
// src/hello_2/hello_2.hpp
#include <iostream>
void Hello_2();
// src/hello_1/hello_1.cpp
#include "hello_1.hpp"
void Hello_1() {
std::cout << "Hello_1" << std::endl;
}
// src/hello_2/hello_2.cpp
#include "hello_2.hpp"
void Hello_2() {
std::cout << "Hello_2" << std::endl;
}
// main.cpp
#include "hello_1.hpp"
#include "hello_2.hpp"
int main() {
Hello_1();
Hello_2();
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(main)
add_executable(${PROJECT_NAME} "")
# 添加可执行文件所依赖头文件目录
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/src/hello_1
${CMAKE_SOURCE_DIR}/src/hello_2
)
# 添加依赖源文件
target_sources(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/src/hello_1/hello_1.cpp
${CMAKE_SOURCE_DIR}/src/hello_2/hello_2.cpp
${CMAKE_SOURCE_DIR}/src/main.cpp
)
生成库
将源文件生成库文件
graph LR
X(语法)
X-->A(target_include_directories)-->A1(添加头文件目录)
X-->B(target_sources)-->B1(添加源文件)
X-->C(add_library)-->C1(生成库)
- 示例, 将源文件生成静态库与动态库
.
├── CMakeLists.txt
└── src
└── test_api
├── test_api.cpp
└── test_api.hpp
// src/test_api/test_api.hpp
#ifndef __INCLUDE_TEST_API_HPP__
#define __INCLUDE_TEST_API_HPP__
#include <iostream>
#ifdef _WIN32
#define __EXPORT __declspec(dllexport)
#else
#define __EXPORT __attribute__((visibility("default")))
#endif
extern "C" {
void Display();
int Add(int x, int y);
}
#endif // __INCLUDE_TEST_API_HPP__
// src/test_api/test_api.cpp
#include "test_api.hpp"
void Display() {
std::cout << "Print test_api success!" << std::endl;
}
int Add(int x, int y) {
return x + y;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(test_api)
# 设置库生成目录为项目根目录下lib
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib)
# 生成库文件
add_library(${PROJECT_NAME}_shared SHARED "")
add_library(${PROJECT_NAME}_static STATIC "")
# 将动态库与静态库名称保存在LIB_NAME变量中
foreach(LIB_NAME ${PROJECT_NAME}_shared ${PROJECT_NAME}_static)
target_include_directories(${LIB_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/test_api)
target_sources(${LIB_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/test_api/test_api.cpp)
# 设置库名
set_target_properties(${LIB_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
endforeach()
链接库
生成可执行文件并链接现有库文件
graph LR
X(语法)
X-->A(target_include_directories)-->A1(添加头文件目录)
X-->B(target_sources)-->B1(添加源文件)
X-->C(target_link_libraries)-->C1(添加链接库文件)
X-->D(find_library)-->D1(按名称查找库路径)
- 示例, 生成可执行文件并链接动态库
.
├── CMakeLists.txt
├── lib
│ ├── libtest_api.a
│ └── libtest_api.so
└── src
├── main.cpp
└── test_api
├── test_api.cpp
└── test_api.hpp
// main.cpp
#include "test_api.hpp"
int main(void) {
Display();
int res = Add(1, 2);
std::cout << "res = " << res << std::endl;
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(main)
# 查找test_api库路径, 存储在FUNC_LIB中
find_library(FUNC_LIB test_api ${CMAKE_SOURCE_DIR}/lib)
add_executable(${PROJECT_NAME} "")
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/test_api)
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/main.cpp)
# 链接库
target_link_libraries(${PROJECT_NAME} PRIVATE ${FUNC_LIB})
子目录编译
可通过划分子目录与主目录CMakeLists.txt, 实现构建文件生成像结构
例如通过多CMakeLists.txt, 实现生成库并链接库生成可执行文件
graph LR
X(语法)
X-->A(add_subdirectory)-->A1(执行子目录CMakeLists.txt)
- 示例, 子目录中生成动态库并链接
.
├── CMakeLists.txt
└── src
├── main.cpp
└── test_api
├── CMakeLists.txt
├── test_api.cpp
└── test_api.hpp
// src/test_api/test_api.hpp
#ifndef __INCLUDE_TEST_API_HPP__
#define __INCLUDE_TEST_API_HPP__
#include <iostream>
#ifdef _WIN32
#define __EXPORT __declspec(dllexport)
#else
#define __EXPORT __attribute__((visibility("default")))
#endif
extern "C" {
int Add(int x, int y);
void Print();
}
#endif // __INCLUDE_TEST_API_HPP__
// src/test_api/test_api.cpp
#include "test_api.hpp"
void Print() {
std::cout << "Print test_api success!" << std::endl;
}
int Add(int x, int y) {
return x + y;
}
// src/main.cpp
#include "test_api.hpp"
int main(void) {
Display();
int res = Add(0xFF, 0xAA);
std::cout << "res = " << res << std::endl;
return 0;
}
# src/test_api/CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(test_api)
add_library(${PROJECT_NAME} SHARED "")
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/test_api/test_api.cpp)
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(main)
# 执行子目录cmake
add_subdirectory(src/test_api)
add_executable(${PROJECT_NAME} "")
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/main.cpp)
target_link_libraries(${PROJECT_NAME} test_api)
三方库
通过cmake使用三方库中内容
find_library
- 示例, 调用三方库opencv(假设其安装在系统目录下)
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat image = cv::imread("./a.png");
cv::imshow("test", image);
return 0;
}
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
project(main)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_SYSTEM_NAME Linux)
find_package(OpenCV REQUIRED)
add_executable(${PROJECT_NAME} "")
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/main.cpp)
target_link_libraries(${PROJECT_NAME} "${OpenCV_LIBS}")
FetchContent
cmake 3.11及以上版本引入FetchContent模块, 可直接下载第三方库编译
- 示例, 下载编译三方库fmt
// main.cpp
#include "fmt/core.h"
int main(){
std::string world = fmt::format("Hello {0}", "World");
fmt::print("{}\n", world);
return 0;
}
方式1 CMakeLists.txt调用
.
├── CMakeLists.txt
├── main.cpp
└── extern
# CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(main)
set(CMAKE_CXX_STANDARD 14)
# 引入FetchContent
include(FetchContent)
FetchContent_Declare(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 9.1.0
SOURCE_DIR ${CMAKE_SOURCE_DIR}/extern/fmt
)
# 构建库
FetchContent_MakeAvailable(fmt)
add_executable(${PROJECT_NAME} "")
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/extern/fmt/include)
target_sources(${PROJECT_NAME} PUBLIC main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt)
方式2 模块调用
可通过.cmake模块实现三方库下载过程, 并在主CMakeLists.txt中调用
.
├── CMakeLists.txt
├── main.cpp
├── cmake
│ └── FMT.cmake
└── extern
新建cmake/FMT.cmake
# cmake/FMT.cmake
include(FetchContent)
set(FMT_LIB fmt)
FetchContent_Declare(${FMT_LIB}
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 9.1.0
SOURCE_DIR ${CMAKE_SOURCE_DIR}/extern/${FMT_LIB}
)
FetchContent_MakeAvailable(${FMT_LIB})
修改根目录CMakeLists.txt, 将fmt库安装逻辑解耦
# CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(main)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
# 导入FMT.cmake模块
include(FMT)
add_executable(${PROJECT_NAME} "")
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/extern/fmt/include)
target_sources(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE ${FMT_LIB}::${FMT_LIB})
cross compiling(交叉编译)
通过交叉编译器可在AMD64平台编译生成ARM架构文件
配置工具链
# 32位
sudo apt install -y arm-linux-gnueabihf-g++
# 64位
sudo apt install -y g++-aarch64-linux-gnu
查看版本
aarch64-linux-gnu-gcc -v
查看工具链路径
- 示例, 交叉编译aarch64架构可执行文件
// main.c
#include <stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(main)
add_executable(${PROJECT_NAME} main.c)
构建
命令调用
通过条件编译宏指定交叉编译器
cmake -DCMAKE_C_COMPILER=gcc路径 -DCMAKE_CXX_COMPILER=g++路径 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=aarch64 -B build
模块调用
创建交叉编译工具链, 设置编译环境, 通过-DCMAKE_TOOLCHAIN_FILE
指定工具链路径
# 指定 C 编译器
set(CMAKE_C_COMPILER 编译器路径)
# 指定 C++ 编译器
set(CMAKE_CXX_COMPILER 编译器路径)
# 指定目标系统(可选)
set(CMAKE_SYSTEM_NAME 平台)
set(CMAKE_SYSTEM_PROCESSOR 架构)
cmake -DCMAKE_TOOLCHAIN_FILE=工具链路径 ...
- 示例, 使用工具链交叉编译
新建cmake/toolchain.cmake
# cmake/toolchain.cmake
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain.cmake -B build