cmake入门学习
基础知识
带有头文件cmake编译
Makefile
解决问题
自动编译
自动变量
依赖第三方库
Cmake
项目结构准备
首先创建项目目录结构:
bash
helloworld/
├── CMakeLists.txt # 主 CMake 文件
├── main.c # 主程序
├── include/ # 头文件目录
│ └── hello.h
└── src/ # 源代码目录└── hello.c
文件内容:
- include/hello.h
#ifndef HELLO_H
#define HELLO_Hvoid print_hello();#endif
- src/hello.c
#include <stdio.h>
#include "hello.h"void print_hello() {printf("Hello, World!\n");
}
- main.c
#include "hello.h"int main() {print_hello();return 0;
}
阶段 1: 直接编译 - CMakeLists.txt
cmake_minimum_required(VERSION 3.0) # 最低CMake版本
project(HelloWorld) # 项目名称# 包含头文件目录
include_directories(include)# 添加可执行文件(直接编译所有源文件)
add_executable(hellomain.csrc/hello.c
)# 安装规则(可选)
install(TARGETS hello DESTINATION bin)
编译命令:
mkdir build
cd build
cmake .. # 生成构建系统
make # 编译
./hello # 运行
阶段 2: 静态库链接 - CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(HelloWorldStatic)include_directories(include)# 创建静态库
add_library(hello_lib STATICsrc/hello.c
)# 创建可执行文件
add_executable(hello main.c)# 链接静态库
target_link_libraries(hello hello_lib)# 安装规则(库+可执行文件)
install(TARGETS hello_lib ARCHIVE DESTINATION lib)
install(TARGETS hello DESTINATION bin)
install(FILES include/hello.h DESTINATION include)
编译命令:
rm -rf build ; mkdir build ; cd build
cmake ..
make
./hello
查看静态库内容:
ar t libhello_lib.a # 查看库内文件
nm libhello_lib.a # 查看符号表
阶段 3: 动态库链接 - CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(HelloWorldShared)include_directories(include)# 创建动态库
add_library(hello_lib SHAREDsrc/hello.c
)# 设置版本号
set_target_properties(hello_lib PROPERTIESVERSION 1.0.0SOVERSION 1
)# 创建可执行文件
add_executable(hello main.c)
target_link_libraries(hello hello_lib)# 安装规则
install(TARGETS hello_libLIBRARY DESTINATION libRUNTIME DESTINATION bin # Windows平台需要
)
install(TARGETS hello DESTINATION bin)
install(FILES include/hello.h DESTINATION include)
编译命令:
rm -rf build ; mkdir build ; cd build
cmake ..
make
./hello# 查看动态库依赖
ldd hello | grep hello
Cmake-ARM
一、使用CMake实现(交叉编译)
项目结构
helloworld/
├── CMakeLists.txt # 主CMake文件
├── main.c # 主程序
├── include/ # 头文件目录
│ └── hello.h
└── src/ # 源代码目录└── hello.c
文件内容
- include/hello.h
#ifndef HELLO_H
#define HELLO_Hvoid print_hello();#endif
- src/hello.c
#include <stdio.h>
#include "hello.h"void print_hello() {printf("Hello, ARM World!\n");
}
- main.c
#include "hello.h"int main() {print_hello();return 0;
}
步骤1:直接编译(无库)
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(HelloWorldARM)# 设置交叉编译器
set(CMAKE_C_COMPILER arm-linux-gcc)# 添加可执行文件
add_executable(hellomain.csrc/hello.c
)# 包含头文件目录
target_include_directories(hello PRIVATE include)
步骤2:使用静态库
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(HelloWorldARM)set(CMAKE_C_COMPILER arm-linux-gcc)# 创建静态库
add_library(hello_lib STATICsrc/hello.c
)
target_include_directories(hello_lib PUBLIC include)# 添加可执行文件
add_executable(hello main.c)
target_link_libraries(hello hello_lib)
步骤3:使用动态库
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(HelloWorldARM)set(CMAKE_C_COMPILER arm-linux-gcc)# 创建动态库
add_library(hello_lib SHAREDsrc/hello.c
)
target_include_directories(hello_lib PUBLIC include)# 设置动态库版本
set_target_properties(hello_lib PROPERTIESVERSION 1.0.0SOVERSION 1
)# 添加可执行文件
add_executable(hello main.c)
target_link_libraries(hello hello_lib)
进阶-多架构编译器
多架构编译支持:单一 CMakeLists 适配多种编译器
要实现单一 CMakeLists 文件支持多种编译器架构,同时保持项目结构和代码不变,需要使用 CMake 的工具链文件和条件编译技术。以下是完整的解决方案:
修改后的 CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(HelloWorldMultiArch)# 1. 包含头文件目录
include_directories(include)# 2. 设置默认架构(可通过命令行覆盖)
if(NOT DEFINED TARGET_ARCH)set(TARGET_ARCH "x86_64") # 默认架构
endif()# 3. 根据架构选择工具链文件
if(TARGET_ARCH STREQUAL "arm")set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/toolchains/arm-linux-gcc.cmake)
elseif(TARGET_ARCH STREQUAL "aarch64")set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/toolchains/aarch64-linux-gcc.cmake)
elseif(TARGET_ARCH STREQUAL "riscv")set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/toolchains/riscv-linux-gcc.cmake)
elseif(TARGET_ARCH STREQUAL "x86_64")# 使用本地编译器,无需工具链文件
endif()# 4. 应用工具链文件(如果指定)
if(CMAKE_TOOLCHAIN_FILE)message(STATUS "Using toolchain: ${CMAKE_TOOLCHAIN_FILE}")include(${CMAKE_TOOLCHAIN_FILE})
endif()# 5. 创建动态库
add_library(hello_lib SHAREDsrc/hello.c
)# 6. 设置版本号
set_target_properties(hello_lib PROPERTIESVERSION 1.0.0SOVERSION 1
)# 7. 创建可执行文件
add_executable(hello main.c)
target_link_libraries(hello hello_lib)# 8. 安装规则(通用)
include(GNUInstallDirs) # 包含标准安装路径定义install(TARGETS hello_libLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # Windows平台需要
)install(TARGETS hello DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES include/hello.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
工具链文件目录结构
创建 toolchains/
目录存放各种架构的工具链文件:
project/
├── CMakeLists.txt
├── toolchains/
│ ├── arm-linux-gcc.cmake
│ ├── aarch64-linux-gcc.cmake
│ └── riscv-linux-gcc.cmake
├── main.c
├── include/
│ └── hello.h
└── src/└── hello.c
工具链文件示例
1. ARM 架构工具链 (toolchains/arm-linux-gcc.cmake
)
# ARM 工具链设置
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)# 编译器路径
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)# Sysroot 路径(可选)
set(CMAKE_SYSROOT /opt/arm-sysroot)# 查找规则
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)# CPU 特性优化
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfpu=neon")
2. AArch64 架构工具链 (toolchains/aarch64-linux-gcc.cmake
)
# AArch64 工具链设置
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)# 编译器路径
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)# CPU 特性优化
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=cortex-a72")
3. RISC-V 架构工具链 (toolchains/riscv-linux-gcc.cmake
)
# RISC-V 工具链设置
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR riscv)# 编译器路径
set(CMAKE_C_COMPILER riscv64-unknown-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER riscv64-unknown-linux-gnu-g++)# CPU 特性优化
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=rv64gc -mabi=lp64d")
使用说明
1. 编译不同架构
# ARM 架构
mkdir build-arm && cd build-arm
cmake .. -DTARGET_ARCH=arm
make# AArch64 架构
mkdir build-aarch64 && cd build-aarch64
cmake .. -DTARGET_ARCH=aarch64
make# RISC-V 架构
mkdir build-riscv && cd build-riscv
cmake .. -DTARGET_ARCH=riscv
make# x86_64 架构(默认)
mkdir build-x86 && cd build-x86
cmake .. # 或显式指定 -DTARGET_ARCH=x86_64
make
2. 验证输出文件架构
# 检查生成的可执行文件架构
file hello# ARM 输出示例: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)
# AArch64 输出示例: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV)
# RISC-V 输出示例: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV)
# x86_64 输出示例: ELF 64-bit LSB executable, x86-64, version 1 (SYSV)
3. 安装到不同架构的系统
# ARM 系统安装
cd build-arm
make install DESTDIR=/mnt/arm-rootfs# RISC-V 系统安装
cd build-riscv
make install DESTDIR=/mnt/riscv-rootfs
高级技巧:自动检测架构
如果需要在 CMake 中自动检测当前架构,可以添加以下代码:
# 自动检测主机架构
execute_process(COMMAND uname -m OUTPUT_VARIABLE HOST_ARCH)
string(STRIP ${HOST_ARCH} HOST_ARCH)# 设置默认架构为当前主机架构
if(NOT DEFINED TARGET_ARCH)set(TARGET_ARCH ${HOST_ARCH})
endif()# 显示当前架构信息
message(STATUS "Building for target architecture: ${TARGET_ARCH}")
message(STATUS "Host architecture: ${HOST_ARCH}")