cmake使用

 

cmake使用详细教程(日常使用这一篇就足够了)

配置

cmake是跨平台C/C++构建文件生成工具, 通过CMakeList.txt生成项目构建文件

安装

linux

命令行
sudo apt install -y cmake
源码安装
  • 示例, 安装cmake-3.25.0
wget https://cmake.org/files/v3.25/cmake-3.25.0-linux-aarch64.tar.gz

tar -xvzf cmake-3.25.0-linux-aarch64.tar.gz                                    

cd cmake-3.25.0-linux-aarch64 && mkdir build && cd build

../bootstrap

# 编译安装
make && make install      

命令

查看版本

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-系列函数动态链)
  • 示例, 生成main.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(要包含头文件目录列表)
  • 示例, 为main目标文件添加头文件目录
.
├── CMakeLists.txt
├── src
│   ├── main.c
│   └── module
│       └── test_api
│           └── include
│               └── test_api.h
└── third_party
    └── hello_lib
        └── hello.h
// third_party/hello_lib/hello.h
#include <stdio.h>

void Hello() {
    printf("Hello OK\n");
}
// src/module/test_api/include/test_api.h
#include <stdio.h>

void Test() {
    printf("Test OK\n");
}
// src/main.c
#include "hello.h"
#include "test_api.h"

int main() {
    Hello();
    Test();
    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/include
)

源文件路径

给目标文件添加依赖源文件路径

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(main main.c)

# 链接 POSIX 线程库
target_link_libraries(main 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目标中, 用户必须在子目录中显式构建目标)

安装

目标文件

可执行文件
install(TARGETS 文件名 RUNTIME DESTINATION 安装路径)
  • 示例, 安装可执行文件main到根目录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")

示例

单文件

将单个.cpp生成可执行文件

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)

多目录

将多个目录下.cpp文件生成可执行文件

graph LR
    X(语法)
    X-->A(target_include_directories)-->A1(添加头文件目录)
    X-->B(target_sources)-->B1(添加源文件)
    X-->C("${CMAKE_SOURCE_DIR}")-->C1(表示项目根目录)
  • 示例, 将source_1、source_2目录下文件生成可执行文件
.
├── CMakeLists.txt
├── main.cpp
├── include_1
│   └── hello_1.hpp
├── include_2
│   └── hello_2.hpp
├── source_1
│   └── hello_1.cpp
└── source_2
    └── hello_2.cpp
// include_1/hello_1.hpp
#include <iostream>
void Hello_1();
// include_2/hello_2.hpp
#include <iostream>
void Hello_2();
// source_1/hello_1.cpp
#include "hello_1.hpp"
void Hello_1() {
    std::cout << "Hello_1" << std::endl;
}
// source_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}/include_1
    ${CMAKE_SOURCE_DIR}/include_2
)

# 依赖源文件
target_sources(${PROJECT_NAME} PRIVATE
    ${CMAKE_SOURCE_DIR}/source_1/hello_1.cpp
    ${CMAKE_SOURCE_DIR}/source_2/hello_2.cpp
    ${CMAKE_SOURCE_DIR}/main.cpp
)

生成库

将源文件生成库文件

graph LR
    X(语法)
    X-->A(target_include_directories)-->A1(添加头文件目录)
    X-->B(target_sources)-->B1(添加源文件)
    X-->C(add_library)-->C1(生成库)
  • 示例, 将test_api.cpp生成静态库与动态库
.
├── CMakeLists.txt
├── main.cpp
├── include
│   └── test_api.hpp
└── source
    └── test_api.cpp
// include/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__
// source/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}/include)
    target_sources(${LIB_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/source/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(按名称查找库路径)
  • 示例, 生成可执行文件, 链接lib/下libtest_api库
// main.cpp
#include "test_api.hpp"

int main(void) {
    int res = Add(1, 2);
    std::cout << "res = " << res << std::endl;
    Display();
    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}/include)
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/main.cpp )
# 链接库
target_link_libraries(${PROJECT_NAME} PRIVATE ${FUNC_LIB})

子目录编译

可通过划分子目录与主目录CMakeLists.txt, 实现构建文件生成像结构

例如通过多CMakeLists.txt, 实现生成库并链接库生成可执行文件

graph LR
    X(语法)
    X-->A(add_subdirectory)-->A1(执行子目录CMakeLists.txt)
  • 示例, 在test_api/生成libtest_api库, 并和main.cpp链接生成可执行文件
.
├── CMakeLists.txt
├── test_api
│   ├── CMakeLists.txt
│   ├── include
│   │   └── test_api.hpp
│   └── source
│       └── test_api.cpp
└── main.cpp

test_api目录

// test_api/include/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__
// test_api/include/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;
}
# test_api/CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(test_api)

set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib)

add_library(${PROJECT_NAME} SHARED "")
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/source/test_api.cpp)

主目录

# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(main)

# 设置预链接库名称
set(EXTRA_LIBS ${EXTRA_LIBS} test_api)

# 添加子目录cmake
add_subdirectory(test_api)

add_executable(${PROJECT_NAME} "")
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/test_api/include)
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/main.cpp)
target_link_libraries(${PROJECT_NAME} ${EXTRA_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);
}
主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)

cmake模块调用

可通过.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

查看工具链路径

  • 示例, 交叉编译arm64架构可执行文件
// 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.cpp)

命令调用

命令行中通过条件编译宏指定交叉编译器

cmake -DCMAKE_C_COMPILER=gcc路径 -DCMAKE_CXX_COMPILER=g++路径 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=armv8

.cmake调用

通过-DCMAKE_TOOLCHAIN_FILE指定工具链

# 指定 C 编译器
set(CMAKE_C_COMPILER /path/to/cross/gcc)

# 指定 C++ 编译器
set(CMAKE_CXX_COMPILER /path/to/cross/g++)

# 指定目标系统(可选)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake路径 ......

新建cmake/toolchain.cmake

# cmake/toolchain.cmake
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)